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>

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

相关文章
|
9月前
|
Android开发 数据安全/隐私保护 开发者
Android自定义view之模仿登录界面文本输入框(华为云APP)
本文介绍了一款自定义输入框的实现,包含静态效果、hint值浮动动画及功能扩展。通过组合多个控件完成界面布局,使用TranslateAnimation与AlphaAnimation实现hint文字上下浮动效果,支持密码加密解密显示、去除键盘回车空格输入、光标定位等功能。代码基于Android平台,提供完整源码与attrs配置,方便复用与定制。希望对开发者有所帮助。
179 0
|
JavaScript 前端开发 Android开发
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
443 13
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
9月前
|
XML 搜索推荐 Android开发
Android改变进度条控件progressbar的样式(根据源码修改)
本文介绍了如何基于Android源码自定义ProgressBar样式。首先分析了系统源码中ProgressBar样式的定义,发现其依赖一张旋转图片实现动画效果。接着分两步指导开发者实现自定义:1) 模仿源码创建一个旋转动画XML文件(放置在drawable文件夹),修改图片为自定义样式;2) 在UI控件中通过`indeterminateDrawable`属性应用该动画。最终实现简单且个性化的ProgressBar效果,附带效果图展示。
600 2
|
10月前
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码android版环境配置流程及功能明细
部署需基于 CentOS 7.9 系统,硬盘不低于 40G,使用宝塔面板安装环境,包括 PHP 7.3(含 Redis、Fileinfo 扩展)、Nginx、MySQL 5.6、Redis 和最新 Composer。Swoole 扩展需按步骤配置。2021.08.05 后部署需将站点目录设为 public 并用 ThinkPHP 伪静态。开发环境建议 Windows 操作系统与最新 Android Studio,基础配置涉及 APP 名称修改、接口域名更换、包名调整及第三方登录分享(如 QQ、微信)的配置,同时需完成阿里云与腾讯云相关设置。
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
474 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
11月前
|
XML JavaScript Android开发
【Android】网络技术知识总结之WebView,HttpURLConnection,OKHttp,XML的pull解析方式
本文总结了Android中几种常用的网络技术,包括WebView、HttpURLConnection、OKHttp和XML的Pull解析方式。每种技术都有其独特的特点和适用场景。理解并熟练运用这些技术,可以帮助开发者构建高效、可靠的网络应用程序。通过示例代码和详细解释,本文为开发者提供了实用的参考和指导。
440 15
|
数据采集 JavaScript Android开发
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
554 7
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
1522 11
|
网络协议 Shell 网络安全
解决两个 Android 模拟器之间无法网络通信的问题
让同一个 PC 上运行的两个 Android 模拟器之间能相互通信,出(qiong)差(ren)的智慧。
332 3
|
Java Unix Linux
Android Studio中Terminal运行./gradlew clean build提示错误信息
遇到 `./gradlew clean build`命令执行出错时,首先应检查错误信息的具体内容,这通常会指向问题的根源。从权限、环境配置、依赖下载、版本兼容性到项目配置本身,逐一排查并应用相应的解决措施。记住,保持耐心,逐步解决问题,往往复杂问题都是由简单原因引起的。
1337 2

热门文章

最新文章