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>

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

相关文章
|
1月前
|
安全 Windows
【Azure Cloud Service】在Windows系统中抓取网络包 ( 不需要另外安全抓包工具)
通常,在生产环境中,为了保证系统环境的安全和纯粹,是不建议安装其它软件或排查工具(如果可以安装,也是需要走审批流程)。 本文将介绍一种,不用安装Wireshark / tcpdump 等工具,使用Windows系统自带的 netsh trace 命令来获取网络包的步骤
72 32
|
2月前
|
网络协议 Shell 网络安全
解决两个 Android 模拟器之间无法网络通信的问题
让同一个 PC 上运行的两个 Android 模拟器之间能相互通信,出(qiong)差(ren)的智慧。
34 3
|
3月前
|
数据采集 存储 监控
网络爬虫的最佳实践:结合 set_time_limit() 与 setTrafficLimit() 抓取云盘数据
本文探讨了如何利用 PHP 的 `set_time_limit()` 与爬虫工具的 `setTrafficLimit()` 方法,结合多线程和代理 IP 技术,高效稳定地抓取百度云盘的公开资源。通过设置脚本执行时间和流量限制,使用多线程提高抓取效率,并通过代理 IP 防止 IP 封禁,确保长时间稳定运行。文章还提供了示例代码,展示了如何具体实现这一过程,并加入了数据分类统计功能以监控抓取效果。
75 16
网络爬虫的最佳实践:结合 set_time_limit() 与 setTrafficLimit() 抓取云盘数据
|
3月前
|
数据采集 存储 JavaScript
构建您的第一个Python网络爬虫:抓取、解析与存储数据
【9月更文挑战第24天】在数字时代,数据是新的金矿。本文将引导您使用Python编写一个简单的网络爬虫,从互联网上自动抓取信息。我们将介绍如何使用requests库获取网页内容,BeautifulSoup进行HTML解析,以及如何将数据存储到文件或数据库中。无论您是数据分析师、研究人员还是对编程感兴趣的新手,这篇文章都将为您提供一个实用的入门指南。拿起键盘,让我们开始挖掘互联网的宝藏吧!
|
4月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
616 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
4月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
214 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
4月前
|
数据采集 存储 JavaScript
构建你的首个Python网络爬虫:抓取、解析与存储数据
【8月更文挑战第31天】在数字时代的浪潮中,数据成为了新的石油。了解如何从互联网的海洋中提取有价值的信息,是每个技术爱好者的必备技能。本文将引导你通过Python编程语言,利用其强大的库支持,一步步构建出你自己的网络爬虫。我们将探索网页请求、内容解析和数据存储等关键环节,并附上代码示例,让你轻松入门网络数据采集的世界。
|
4月前
|
开发工具 Android开发 git
全志H713 Android 11 :给AOSP源码,新增一个Product
本文介绍了在全志H713 Android 11平台上新增名为myboard的产品的步骤,包括创建新的device目录、编辑配置文件、新增内核配置、记录差异列表以及编译kernel和Android系统的详细过程。
230 0
|
4月前
|
Ubuntu 开发工具 Android开发
Repo下载、编译AOSP源码:基于Ubuntu 21.04,android-12.1.0_r27
文章记录了作者在Ubuntu 21.04服务器上配置环境、下载并编译基于Android 12.1.0_r27版本的AOSP源码的过程,包括解决编译过程中遇到的问题和错误处理方法。
257 0
|
数据采集 搜索推荐
爬虫---如何抓取app的思路和方案
爬虫---如何抓取app的思路和方案背景2015年,谷歌开始对外部App的内部链接和内容进行抓取,目前已经累计抓取了300多亿个。 搜索引擎是内容门户之后的互联网第二次重大技术革命。然而伴随着智能手机的普及,应用软件(APP)取代网页,成为主流的技术。
2045 0