Android用BaseRecyclerViewAdapterHelper和SmartRefreshLayout实现最简单的下拉刷新和上拉加载更多

1、前言:平时在android端用jsoup爬取网页数据时经常用到下拉刷新和上拉加载更多,逛了不少的博客、简书、也看了不少人家的优秀代码,发现了一个非常简单的方式,不敢独享所以发出来。

2、用到的框架主要是这两个

BaseRecyclerViewAdapterHelper和SmartRefreshLayout(以防失效自己复制粘贴)

BRVH的GitHub地址

https://github.com/CymChad/BaseRecyclerViewAdapterHelper(以防失效自己复制粘贴)

SmartRefreshLayout的GitHub地址

https://github.com/scwang90/SmartRefreshLayout
首先感谢一下这位作者我学习Jsoup和BaseRecyclerViewAdapterHelper就是看了他的源码,他的简书介绍和源码地址如下
简书传送们

https://www.jianshu.com/p/e8137cfb6812(以防失效自己复制粘贴)

GitHub传送门

https://github.com/xiaweizi/jsoupJianshuDemo(以防失效自己复制粘贴)

3、进入主题了,我第一次写废话多了点

我取得的数据来源主要是http://www.mzitu.com/的妹子自拍标签下的数据,注意这个标签下的图片没有防盗链,其他的有防盗链的,抓到无法显示的,还有这只是用于学习,不做商业用途,

首先进到http://www.mzitu.com/的自拍标签下把www换成m后刷新进手机版网页,手机版网页的图片对手机进行过适配可以很好兼容

原来的网页如下图


进入手机版后如下图


要抓数据就抓最合适的,要达到最佳效果手机和电脑两个网页要反复切换的。还有取数据用到Jsoup和OkHttp3如果不会请自行百度学习,很简单的

正事前啰嗦一下Jsoup本身也可以把一个网页请求下来再抓取数据,但是我不推荐,Jsoup抓取静态页面很牛逼,用专业的网络框架请求网页效果会更嗨,说一下抓数据的思路,先用OkHttp3请求网页数据把整张网页请求下来再交给Jsoup抓取网页中的资源,再把数据放到集合里再展示。

  1. 网络请求:用的是异步加载的android联网不支持在主线程

 private void getDataFromNet(String url) {
        mHttpClient = new OkHttpClient();
        Request request = new Request
                .Builder()
                .url(url)
                .build();
        mHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json = response.body().string();
//                paseHtml(json);
                processData(json);
//                handler.sendEmptyMessage(10);
            }
        });


    }

取数据和搞适配器

private void processData(String json) {
        meiZiBeans=spiderData(json) ;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {

                    mAdapter.addData(meiZiBeans);
                }


        });

    }

    private ArrayList spiderData(String html) {
        ArrayList meiZiBeans = new ArrayList<>();
        Document doc = Jsoup.parse(html);
        Elements lis = doc.select("div.comment-body");
        for (Element e : lis) {
            MeiZiBean meiZiBean = new MeiZiBean();
            String img = e.select("img").attr("src");
            Log.e("图片地址", img);
            meiZiBean.setMeiziImg(img);
            meiZiBeans.add(meiZiBean);
        }
        Log.e("集合的大小", String.valueOf(meiZiBeans.size()));
        return meiZiBeans;
    }

拿到数据后搞适配器没做判空,要就自己加,这里展示数据也不能在主线程可以用Handler或者runOnUiThread

适配器代码也很简单

public class MZiAdapter extends BaseQuickAdapter {
    private List mList;
    private Context context;
    public MZiAdapter(Context context) {
        super(R.layout.item_card_view);
        this.context=context;
    }


    @Override
    protected void convert(BaseViewHolder helper, MeiZiBean item) {
        Glide.with(context).load(item.getMeiziImg()).dontAnimate().into((ImageView) helper.getView(R.id.image));
    }

}

再说一下BaseRecyclerViewAdapterHelper自带一个上下文mContext,这种Adapter在用的时候传一个上下文,最重要的放数据进去是addData和setNewData();这两个有区别的。这个适配器就是简书的那位同志的代码风格,照套的自己写传个集合的Adapter也可以的和setNewDate的效果一样的。

接下来就是下拉刷新和上拉加载更多了

 private void refreshData() {
        refreshLayout.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(RefreshLayout refreshlayout) {
                getDataFromNet(A_baseUrl + Page + B_baseUrl);
                refreshlayout.finishRefresh(2000);
            }
        });
        refreshLayout.setOnLoadmoreListener(new OnLoadmoreListener() {
            @Override
            public void onLoadmore(RefreshLayout refreshlayout) {
                LoadMore=true;
                ++Page;
                getDataFromNet(A_baseUrl + Page + B_baseUrl);
                Log.e("item数", String.valueOf(mAdapter.getItemCount()));
                Log.e("集合数", String.valueOf(meiZiBeans.size()));
                refreshlayout.finishLoadmore(2000);
            }
        });
    }

注意这里的下拉刷新没有清空原来的集合再填重新添加数据,只是重新传一下第一页的地址给看一下效果而已,重要的是的是上拉加载更多页数加一后重新调用getDataFromNet()方法再搞适配器Adapter,addData();就是这样就实现了加载更多。

下面贴一下全部的主代码

public class MainActivity extends AppCompatActivity {
    private RefreshLayout refreshLayout;
    private RecyclerView recyclerView;
    private MZiAdapter mAdapter;
    private ArrayList meiZiBeans;
    private OkHttpClient mHttpClient;
    int Page = 1;
    String A_baseUrl = "http://m.mzitu.com/zipai/comment-page-";
    String B_baseUrl = "/#comments";
//    private Handler handler = new Handler() {
//        @Override
//        public void handleMessage(Message msg) {
//            super.handleMessage(msg);
//            mAdapter.setNewData(meiZiBeans);
//
//        }
//    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = findViewById(R.id.rv_mian);
        mAdapter = new MZiAdapter(this);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(mAdapter);
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
        mAdapter.openLoadAnimation(BaseQuickAdapter.SLIDEIN_BOTTOM);
        //上拉刷新下拉加载更多
        refreshLayout = findViewById(R.id.srl_m_refresh);
//        meiZiBeans = new ArrayList<>();
        getDataFromNet(A_baseUrl + Page + B_baseUrl);

        mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                Toast.makeText(MainActivity.this, "点击了第" + position + "个item", Toast.LENGTH_LONG).show();
            }
        });
        refreshData();
    }

    private void refreshData() {
        refreshLayout.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(RefreshLayout refreshlayout) {
                getDataFromNet(A_baseUrl + Page + B_baseUrl);
                refreshlayout.finishRefresh(2000);
            }
        });
        refreshLayout.setOnLoadmoreListener(new OnLoadmoreListener() {
            @Override
            public void onLoadmore(RefreshLayout refreshlayout) {
                ++Page;
                getDataFromNet(A_baseUrl + Page + B_baseUrl);
                Log.e("item数", String.valueOf(mAdapter.getItemCount()));
                Log.e("集合数", String.valueOf(meiZiBeans.size()));
                refreshlayout.finishLoadmore(4000);
            }
        });
    }



    private void getDataFromNet(String url) {
        mHttpClient = new OkHttpClient();
        Request request = new Request
                .Builder()
                .url(url)
                .build();
        mHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json = response.body().string();
                paseHtml(json);
//                processData(json);
//                handler.sendEmptyMessage(10);
            }
        });


    }

    private void paseHtml(String json) {
        meiZiBeans = new ArrayList<>();
        Document doc = Jsoup.parse(json);
        Elements lis = doc.select("div.comment-body");
        for (Element e : lis) {
            MeiZiBean meiZiBean = new MeiZiBean();
            String img = e.select("img").attr("src");
            Log.e("图片地址", img);
            meiZiBean.setMeiziImg(img);
            meiZiBeans.add(meiZiBean);
        }
        Log.e("集合的大小", String.valueOf(meiZiBeans.size()));
         runOnUiThread(new Runnable() {
             @Override
            public void run() {
             mAdapter.addData(meiZiBeans);
//             mAdapter.setNewData(meiZiBeans);
              }


         });
    }

//    private void processData(String json) {
//        meiZiBeans=spiderData(json) ;
//            runOnUiThread(new Runnable() {
//                @Override
//                public void run() {
//                    mAdapter.addData(meiZiBeans);
//                }
//
//
//        });
//
//    }
//    private ArrayList spiderData(String html) {
//        ArrayList meiZiBeans = new ArrayList<>();
//        Document doc = Jsoup.parse(html);
//        Elements lis = doc.select("div.comment-body");
//        for (Element e : lis) {
//            MeiZiBean meiZiBean = new MeiZiBean();
//            String img = e.select("img").attr("src");
//            Log.e("图片地址", img);
//            meiZiBean.setMeiziImg(img);
//            meiZiBeans.add(meiZiBean);
//        }
//        Log.e("集合的大小", String.valueOf(meiZiBeans.size()));
//        return meiZiBeans;
//    }
}


那个SmartRefreshLayout设置样式我选择在application中设置全局的复制官方的改一下颜色就得了,有兴趣的自己改样式

public class MyApp  extends Application{
    static {
        //设置全局的Header构建器
        SmartRefreshLayout.setDefaultRefreshHeaderCreater(new DefaultRefreshHeaderCreater() {
            @Override
            public RefreshHeader createRefreshHeader(Context context, RefreshLayout layout) {
                layout.setPrimaryColorsId(R.color.colorPrimary, android.R.color.white);//全局设置主题颜色
                return new BezierCircleHeader(context);//.setTimeFormat(new DynamicTimeFormat("更新于 %s"));//指定为经典Header,默认是 贝塞尔雷达Header
            }
        });
        //设置全局的Footer构建器
        SmartRefreshLayout.setDefaultRefreshFooterCreater(new DefaultRefreshFooterCreater() {
            @Override
            public RefreshFooter createRefreshFooter(Context context, RefreshLayout layout) {
                //指定为经典Footer,默认是 BallPulseFooter
                return new ClassicsFooter(context).setDrawableSize(20);
            }
        });
    }
}

别忘了在功能清单文件里配置

 android:name=".myapplication.MyApp"

日志集合数据的变化

Android用BaseRecyclerViewAdapterHelper和SmartRefreshLayout实现最简单的下拉刷新和上拉加载更多_第1张图片

这种每次集合都是18

gif效果图


注意这里没有做下拉刷新逻辑的要的就自己做,这里下拉刷新就是重新传链接在加载,自己操作试试,慢慢体会addData的效果


这就是一种上拉加载更多的做法,我看过很多上拉加载的例子,发现他们很多都是两种做法,一是每次上拉加载的都是新的,集合重新new数据再装,在把数据加到RecyclerView的尾部,像以上的代码做法一样,不过这个有坑的,就是点击事件的时候就很蛋疼,按照传统的做法根据item的position去取集合的数据时就会发生下标越界,我也已经解决了,你们要自己找办法,二是每次下拉加载更多时,把数据都加到一个集合里,就是集合里的数据是连续的,这样就与RecyclerView 的item一一对应,这两种做法都可以的,注意用BaseRecyclerViewAdapterHelper的setNewData()就是做法二,集合是连续的,再啰嗦一下用addData()可以用BaseRecyclerViewAdapterHelper里的所有动画,可以完美使用,用setNewData()的话必须把所有的动画都禁用,包括布局的包裹属性也要改成固定大小,不然会很难看的,用setNewData()可以基本实现需求这样,但是我觉得他不是严格意义上的上拉加载

以下是第二种做法的上拉加载更多:

public class MainActivity extends AppCompatActivity {
    private RefreshLayout refreshLayout;
    private RecyclerView recyclerView;
    private MZiAdapter mAdapter;
    private ArrayList meiZiBeans;
    private OkHttpClient mHttpClient;
    int Page = 1;
    String A_baseUrl = "http://m.mzitu.com/zipai/comment-page-";
    String B_baseUrl = "/#comments";
//    private Handler handler = new Handler() {
//        @Override
//        public void handleMessage(Message msg) {
//            super.handleMessage(msg);
//            mAdapter.setNewData(meiZiBeans);
//
//        }
//    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = findViewById(R.id.rv_mian);
        mAdapter = new MZiAdapter(this);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(mAdapter);
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
        mAdapter.openLoadAnimation(BaseQuickAdapter.SLIDEIN_BOTTOM);
        //上拉刷新下拉加载更多
        meiZiBeans = new ArrayList<>();
        refreshLayout = findViewById(R.id.srl_m_refresh);
        getDataFromNet(A_baseUrl + Page + B_baseUrl);
        mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                Toast.makeText(MainActivity.this, "点击了第" + position + "个item", Toast.LENGTH_LONG).show();
            }
        });
        refreshData();
    }

    private void refreshData() {
        refreshLayout.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(RefreshLayout refreshlayout) {
                getDataFromNet(A_baseUrl + Page + B_baseUrl);
                refreshlayout.finishRefresh(2000);
            }
        });
        refreshLayout.setOnLoadmoreListener(new OnLoadmoreListener() {
            @Override
            public void onLoadmore(RefreshLayout refreshlayout) {
                ++Page;
                getDataFromNet(A_baseUrl + Page + B_baseUrl);
                Log.e("item数", String.valueOf(mAdapter.getItemCount()));
                Log.e("集合数", String.valueOf(meiZiBeans.size()));
                refreshlayout.finishLoadmore(4000);//加载效果时间为4s
            }
        });
    }



    private void getDataFromNet(String url) {
        mHttpClient = new OkHttpClient();
        Request request = new Request
                .Builder()
                .url(url)
                .build();
        mHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json = response.body().string();
                paseHtml(json);
//                processData(json);
//                handler.sendEmptyMessage(10);
            }
        });


    }

    private void paseHtml(String json) {
//        meiZiBeans = new ArrayList<>();
        Document doc = Jsoup.parse(json);
        Elements lis = doc.select("div.comment-body");
        for (Element e : lis) {
            MeiZiBean meiZiBean = new MeiZiBean();
            String img = e.select("img").attr("src");
            Log.e("图片地址", img);
            meiZiBean.setMeiziImg(img);
            meiZiBeans.add(meiZiBean);
        }
        Log.e("集合的大小", String.valueOf(meiZiBeans.size()));
         runOnUiThread(new Runnable() {
             @Override
            public void run() {
//             mAdapter.addData(meiZiBeans);
             mAdapter.setNewData(meiZiBeans);
              }


         });
    }

//    private void processData(String json) {
//        meiZiBeans=spiderData(json) ;
//            runOnUiThread(new Runnable() {
//                @Override
//                public void run() {
//                    mAdapter.addData(meiZiBeans);
//                }
//
//
//        });
//
//    }
//    private ArrayList spiderData(String html) {
//        ArrayList meiZiBeans = new ArrayList<>();
//        Document doc = Jsoup.parse(html);
//        Elements lis = doc.select("div.comment-body");
//        for (Element e : lis) {
//            MeiZiBean meiZiBean = new MeiZiBean();
//            String img = e.select("img").attr("src");
//            Log.e("图片地址", img);
//            meiZiBean.setMeiziImg(img);
//            meiZiBeans.add(meiZiBean);
//        }
//        Log.e("集合的大小", String.valueOf(meiZiBeans.size()));
//        return meiZiBeans;
//    }
}

注意一下

meiZiBeans = new ArrayList<>();

的位置在这里集合已经是全局变量的,看日志数据的变化

Android用BaseRecyclerViewAdapterHelper和SmartRefreshLayout实现最简单的下拉刷新和上拉加载更多_第2张图片

上拉一次集合变36,再上拉一次就加18,就变54新的数据就是加载更多的数据,这种做法思路在xRecyclerView的案例里也是差不多,把数据加进一个集合里再notifyDataSetChanged()刷新一下Adapter。

上个gi效果图有点模糊还有删除多余的帧数。


源码没有上传,要的留言一下,我再上传。

你可能感兴趣的:(Android用BaseRecyclerViewAdapterHelper和SmartRefreshLayout实现最简单的下拉刷新和上拉加载更多)