Android App网络通信中利用okhttp实现下拉刷新和上拉加载实战(抓取文章信息 超详细 附源码)

简介: Android App网络通信中利用okhttp实现下拉刷新和上拉加载实战(抓取文章信息 超详细 附源码)

需要源码和工具类请点赞关注收藏后评论区留言私信~~~

一、实现下拉刷新和上拉加载功能

网络上的信息很多,往往无法依次拉下来,故而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>

创作不易 觉得有帮助请点赞关注收藏~~~

相关文章
|
3月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
334 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
3月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
103 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
3月前
|
开发工具 Android开发 git
全志H713 Android 11 :给AOSP源码,新增一个Product
本文介绍了在全志H713 Android 11平台上新增名为myboard的产品的步骤,包括创建新的device目录、编辑配置文件、新增内核配置、记录差异列表以及编译kernel和Android系统的详细过程。
85 0
|
3月前
|
Ubuntu 开发工具 Android开发
Repo下载、编译AOSP源码:基于Ubuntu 21.04,android-12.1.0_r27
文章记录了作者在Ubuntu 21.04服务器上配置环境、下载并编译基于Android 12.1.0_r27版本的AOSP源码的过程,包括解决编译过程中遇到的问题和错误处理方法。
135 0
|
14天前
|
JSON 小程序 JavaScript
uni-app开发微信小程序的报错[渲染层错误]排查及解决
uni-app开发微信小程序的报错[渲染层错误]排查及解决
208 7
|
14天前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
279 1
|
14天前
|
JavaScript 前端开发 UED
Vue与uni-app开发中通过@font-face巧妙引入自定义字体
Vue与uni-app开发中通过@font-face巧妙引入自定义字体
36 9
|
18天前
|
缓存 小程序 索引
uni-app开发微信小程序时vant组件van-tabs的使用陷阱及解决方案
uni-app开发微信小程序时vant组件van-tabs的使用陷阱及解决方案
105 1
|
18天前
|
小程序 JavaScript API
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
这篇文章介绍了如何在uni-app和微信小程序中实现将图片保存到用户手机相册的功能。
275 0
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机