需要源码和工具类请点赞关注收藏后评论区留言私信~~~
一、实现下拉刷新和上拉加载功能
网络上的信息很多,往往无法依次拉下来,故而App引入了分页加载功能,最开始先展示第一页内容,等到用户拉到该页底部后再去加载下一页内容,如此往复,按需加载,既提高了系统效率,也加快了显示速度
然而Android只提供了下拉刷新布局SwipeRefreshLayout,用于在页面顶部下拉时的刷新操作,并未提供在页面底部上拉加载的控件,不过借助循环视图的滚动监听器,开发者依然能够侦听到列表底部的上拉操作,此时用到了循环视图的addOnScrollListener方法。可以将上拉操作的侦听过程分解为以下两个步骤
1:重写onScrolled方法,其内部调用布局管理器的findLastVisibleItemPosition方法,寻找最后一个可见项的序号,并记录该项的序号
2:重写onScrollStateChanged方法,其内部判断当前的滚动状态
以文章的上拉加载操作为例,这里使用了wanandroid网站的公开接口,按照分页序号抓取每页的文章标题,抓取数据采用了okhttp访问网站接口,再从返回的JSON报文中解析该页的标题列表,并追加显示在界面下方
接着把文章列表拉到底部,继续往上拉动触发上拉加载事件,可观察到上拉加载效果如下图,显示提示文字和进度圈,
演示视频如下
下拉刷新与上拉加载
如下图所示 可一直下拉显示新的文章标题
代码如下
Java类
package com.example.network; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.View; import android.widget.LinearLayout; import com.example.network.adapter.ArticleAdapter; import com.example.network.bean.ArticleInfo; import org.json.JSONArray; import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; import java.util.List; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class PullRefreshActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener { private final static String TAG = "PullRefreshActivity"; private SwipeRefreshLayout srl_dynamic; // 声明一个下拉刷新布局对象 private RecyclerView rv_dynamic; // 声明一个循环视图对象 private LinearLayout ll_bottom; // 声明一个线性视图对象 private ArticleAdapter mAdapter; // 声明一个线性适配器对象 private List<ArticleInfo> mArticleList = new ArrayList<>(); // 文章列表 private int mPageNo = 0; // 已加载的分页序号 private int mLastVisibleItem = 0; // 最后一个可见项的序号 private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pull_refresh); srl_dynamic = findViewById(R.id.srl_dynamic); // 设置下拉刷新布局的进度圆圈颜色 srl_dynamic.setColorSchemeResources( R.color.red, R.color.orange, R.color.green, R.color.blue); srl_dynamic.setOnRefreshListener(this); // 设置下拉布局的下拉刷新监听器 initRecyclerDynamic(); // 初始化动态线性布局的循环视图 ll_bottom = findViewById(R.id.ll_bottom); mHandler.post(() -> onRefresh()); // 刚打开页面时候的初始加载 } // 初始化动态线性布局的循环视图 private void initRecyclerDynamic() { rv_dynamic = findViewById(R.id.rv_dynamic); // 创建一个垂直方向的线性布局管理器 LinearLayoutManager manager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false); rv_dynamic.setLayoutManager(manager); // 设置循环视图的布局管理器 // 构建一个文章列表的线性适配器 mAdapter = new ArticleAdapter(this, mArticleList); rv_dynamic.setAdapter(mAdapter); // 设置循环视图的线性适配器 rv_dynamic.setItemAnimator(new DefaultItemAnimator()); // 设置循环视图的动画效果 // 给循环视图添加滚动监听器 rv_dynamic.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) { // 滚动停止 // 滚到了最后一项 if (mLastVisibleItem+1 == mAdapter.getItemCount()) { // 显示底部的加载更多文字 ll_bottom.setVisibility(View.VISIBLE); // 滚到最后一项 rv_dynamic.scrollToPosition(mArticleList.size()-1); // 延迟500毫秒后加载更多文章 mHandler.postDelayed(() -> loadArticle(), 500); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // 寻找最后一个可见项的序号 mLastVisibleItem = manager.findLastVisibleItemPosition(); } }); } // 一旦在下拉刷新布局内部往下拉动页面,就触发下拉监听器的onRefresh方法 @Override public void onRefresh() { mArticleList.clear(); mPageNo = 0; loadArticle(); // 加载网络文章 } // 加载网络文章 private void loadArticle() { String url = String.format("https://www.wanandroid.com/article/list/%d/json", mPageNo); OkHttpClient client = new OkHttpClient(); // 创建一个okhttp客户端对象 // 创建一个GET方式的请求结构 Request request = new Request.Builder().url(url).build(); Call call = client.newCall(request); // 根据请求结构创建调用对象 // 加入HTTP请求队列。异步调用,并设置接口应答的回调方法 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { // 请求失败 runOnUiThread(() -> srl_dynamic.setRefreshing(false)); } @Override public void onResponse(Call call, final Response response) throws IOException { // 请求成功 String resp = response.body().string(); mPageNo++; runOnUiThread(() -> showArticle(resp)); // 显示返回的文章 } }); } // 显示返回的文章 private void showArticle(String resp) { srl_dynamic.setRefreshing(false); int lastSize = mArticleList.size(); List<ArticleInfo> addList = new ArrayList<>(); try { JSONObject jsonObject = new JSONObject(resp); JSONObject data = jsonObject.getJSONObject("data"); JSONArray datas = data.getJSONArray("datas"); for (int i=0; i<datas.length(); i++) { JSONObject item = datas.getJSONObject(i); ArticleInfo article = new ArticleInfo(); article.setTitle(item.getString("title")); addList.add(article); } mArticleList.addAll(addList); if (lastSize == 0) { // 下拉刷新开头文章 mAdapter.notifyDataSetChanged(); // 刷新所有列表项数据 } else { // 上拉加载更多文章 // 只刷新指定范围的列表项数据 mAdapter.notifyItemRangeInserted(lastSize, addList.size()); } ll_bottom.setVisibility(View.GONE); // 隐藏底部的加载更多文字 } catch (Exception e) { e.printStackTrace(); } } }
XML文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="5dp"> <!-- 注意SwipeRefreshLayout要使用全路径 --> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/srl_dynamic" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <!-- 注意RecyclerView要使用全路径 --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_dynamic" android:layout_width="match_parent" android:layout_height="wrap_content" /> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> <LinearLayout android:id="@+id/ll_bottom" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:visibility="gone"> <TextView android:id="@+id/tv_seq" android:layout_width="match_parent" android:layout_height="30dp" android:gravity="center" android:textColor="@color/black" android:textSize="15sp" android:text="正在加载更多文章" /> <ProgressBar android:id="@+id/pb_loading" style="@style/Base.Widget.AppCompat.ProgressBar" android:layout_width="20dp" android:layout_height="20dp" /> </LinearLayout> </LinearLayout>
创作不易 觉得有帮助请点赞关注收藏~~~