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>

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

相关文章
|
4月前
|
JSON API 开发工具
【Azure 应用服务】调用Azure REST API来获取 App Service的访问限制信息(Access Restrictions)以及修改
【Azure 应用服务】调用Azure REST API来获取 App Service的访问限制信息(Access Restrictions)以及修改
|
26天前
|
JSON JavaScript 前端开发
harmony-chatroom 自研纯血鸿蒙OS Next 5.0聊天APP实战案例
HarmonyOS-Chat是一个基于纯血鸿蒙OS Next5.0 API12实战开发的聊天应用程序。这个项目使用了ArkUI和ArkTS技术栈,实现了类似微信的消息UI布局、输入框光标处插入文字、emoji表情图片/GIF动图、图片预览、红包、语音/位置UI、长按语音面板等功能。
55 2
|
2月前
|
网络协议 Shell 网络安全
解决两个 Android 模拟器之间无法网络通信的问题
让同一个 PC 上运行的两个 Android 模拟器之间能相互通信,出(qiong)差(ren)的智慧。
31 3
|
2月前
|
JavaScript 小程序 开发者
uni-app开发实战:利用Vue混入(mixin)实现微信小程序全局分享功能,一键发送给朋友、分享到朋友圈、复制链接
uni-app开发实战:利用Vue混入(mixin)实现微信小程序全局分享功能,一键发送给朋友、分享到朋友圈、复制链接
440 0
|
4月前
|
消息中间件 Java
【实战揭秘】如何运用Java发布-订阅模式,打造高效响应式天气预报App?
【8月更文挑战第30天】发布-订阅模式是一种消息通信模型,发送者将消息发布到公共队列,接收者自行订阅并处理。此模式降低了对象间的耦合度,使系统更灵活、可扩展。例如,在天气预报应用中,`WeatherEventPublisher` 类作为发布者收集天气数据并通知订阅者(如 `TemperatureDisplay` 和 `HumidityDisplay`),实现组件间的解耦和动态更新。这种方式适用于事件驱动的应用,提高了系统的扩展性和可维护性。
80 2
|
4月前
|
Java 应用服务中间件 开发工具
[App Service for Windows]通过 KUDU 查看 Tomcat 配置信息
[App Service for Windows]通过 KUDU 查看 Tomcat 配置信息
|
4月前
|
Go 开发者
【应用服务 App Service】App Service发生错误请求时,如何查看IIS Freb日志,从中得知错误所发生的模块,请求中所携带的Header信息
【应用服务 App Service】App Service发生错误请求时,如何查看IIS Freb日志,从中得知错误所发生的模块,请求中所携带的Header信息
|
4月前
|
网络协议 Go Windows
【应用服务 App Service】App Service中抓取网络日志
【应用服务 App Service】App Service中抓取网络日志
|
4月前
|
安全 网络安全 Android开发
探索安卓开发之旅:从新手到专家网络安全与信息安全:防范网络威胁,保护数据安全
【8月更文挑战第29天】在这篇技术性文章中,我们将踏上一段激动人心的旅程,探索安卓开发的世界。无论你是刚开始接触编程的新手,还是希望提升技能的资深开发者,这篇文章都将为你提供宝贵的知识和指导。我们将从基础概念入手,逐步深入到安卓开发的高级主题,包括UI设计、数据存储、网络通信等方面。通过阅读本文,你将获得一个全面的安卓开发知识体系,并学会如何将这些知识应用到实际项目中。让我们一起开启这段探索之旅吧!
|
4月前
|
网络协议 编译器 Go
揭秘!TCP、RPC、gRPC、HTTP大PK,谁才是网络通信界的超级巨星?一篇文章带你秒懂!
【8月更文挑战第25天】本文以教程形式深入对比了TCP、RPC、gRPC与HTTP这四种关键通信协议,并通过Go语言中的示例代码展示了各自的实现方法。TCP作为一种可靠的传输层协议,确保了数据的完整性和顺序性;RPC与gRPC作为远程过程调用框架,特别适合于分布式系统的函数调用与数据交换,其中gRPC在性能和跨语言支持方面表现出色;HTTP则是广泛应用于Web浏览器与服务器通信的应用层协议。选择合适的协议需根据具体需求综合考量。
305 0