手把手教学,实现RecyclerView 自动加载更多

说一下写这个文章的原因

1.熟悉RecyclerView
2.自动加载更多,作为每个应用开发过程当中几乎必不可少的功能,了解如何实现,然后做一些封装,很有必要

分析

我们先来分析一下,什么时候需要用到加载更多的功能?自然是用户向上翻动列表,快要到所有列表项的底部,或则已经到底部的时候需要加载更多的数据,用于展示给用户(为了更好的用户体验,肯定是用户没有到最低端的时候就自动加载更多啦)。那么问题来了,怎么样能知道快要到底部,或者已经到底部了?翻查Android 文档

  void addOnScrollListener (RecyclerView.OnScrollListener  listener) 

这个方法可以监听RecyclerView 的滚动。哈哈距离解决问题更近一步了。我们看看OnScrollListener 里面抽象方法的参数。

void

onScrollStateChanged(RecyclerView recyclerView, int newState)
Callback method to be invoked when RecyclerView's scroll state changes.

void

onScrolled(RecyclerView recyclerView, int dx, int dy)
Callback method to be invoked when the RecyclerView has been scrolled.

这没有办法获得RecyclerView 中最下面的元素是哪一个啊。要是每次滚动的时候,我们就能拿到RecyclerView
所显示的Item中最下面的那个的位置就好了。不急我们看看LinearLayoutManager的文档

int findLastVisibleItemPosition ()
Returns the adapter position of the last visible view. 
This position does not include adapter changes that were dispatched after the last layout pass.
Note that, this value is not affected by layout orientation or item order traversal. (setReverseLayout(boolean) 
Views are sorted by their positions in the adapter, not in the layout.
If RecyclerView has item decorators, they will be considered in calculations as well.
LayoutManager may pre-cache some views that are not necessarily visible. Those views are ignored in this method.

这个方法就是找到最后一个可见Item的位置。太好了,只要每次滚动的时候监听一下最后一个可见的Item 的位置,我们就可以决定是否架子啊更多了。
感觉我们已经准备的差不多了。
那我们开始吧

代码设计

根据上面的思路,我们设计一下代码。
1.当需要加载更多的时候,肯定需要回调啊。那好我们先定义一个回调的接口:

  interface OnLoadingMore {    void onLoadMore();}

2.如果正在加载的时候,用户又上下的滑动,再触发了加载更多,而上次的触发的加载还没有结束,怎么办?定义一个变量 ,如果正在加载了,还没有完成,不能再次加载。

private boolean isLoading;

3.为了提高用户的体验,我们应该设置一个用户滑到提前多少个Item的时候触发加载更多的变量。

private int visibleThreshold = 5;

4.RecyclerView 应该有两种类型的(暂且这么分吧,实际开发中可能会超过)。

@Override public int getItemViewType(int position) {
       if (position == getItemCount() - 1) {
             return TYPE_LOAD_MORE; 
          } else {
               return TYPE_NORMAL; 
        }
 }

感觉已经准备的差不多了,可以开始了

我们选择在BaseAdapter 中实现自动加载更多的功能,可以方便子类使用。
贴代码啦:


public class BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
    List<T> dataSet = new ArrayList<>();
    private final int TYPE_LOAD_MORE = 100;
    private final int TYPE_NORMAL = 101;

    private boolean isLoading;
    private int visibleThreshold = 5;
    OnLoadingMore loadingMore;
private boolean canLoadMore = true;

    public BaseAdapter(RecyclerView recyclerView) {
          //传入一个RecyclerView 下面就是监听滚动啦
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                int itemCount = layoutManager.getItemCount();
                int lastPosition = layoutManager.findLastVisibleItemPosition();
                Log.i("lastPosition --> ", lastPosition + "");
                Log.i("itemCount  --> ", itemCount + " ");
                //如果当前不是正在加载更多,并且到了该加载更多的位置,加载更多。
                if (!isLoading && (lastPosition >= (itemCount - visibleThreshold))) {
                    if (canLoadMore&&loadingMore != null) {
                        isLoading = true;
                        loadingMore.onLoadMore();
                    }
                }
            }
        });
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;

        if (viewType == TYPE_LOAD_MORE) {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_load_more, parent, false);
            ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.pb_loading);
            progressBar.setInterpolator(new AccelerateInterpolator(2));
            progressBar.setIndeterminate(true);
        } else {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_normal, parent, false);
        }
        return new BaseViewHolder<>(view);
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        if (getItemViewType(position) == TYPE_LOAD_MORE) {
                View itemView=holder.itemView;
                  //判定是不是可以加载更多或则正在加载  
                   if (canLoadMore && isLoading) {
                if (itemView.getVisibility() != View.VISIBLE) {
                    itemView.setVisibility(View.VISIBLE);
                }
            } else if (itemView.getVisibility() == View.VISIBLE) {
                itemView.setVisibility(View.GONE);
            }
        } else {
            TextView textView = (TextView) holder.itemView;
            textView.setText(String.valueOf(position));
        }
    }


    @Override
    public int getItemCount() {
        return dataSet.size() + 1;
    }
    //分成两种类型 1.普通的item 2.底部的Progress Bar
    @Override
    public int getItemViewType(int position) {
        if (position == getItemCount() - 1) {
            return TYPE_LOAD_MORE;
        } else {
            return TYPE_NORMAL;
        }
    }

    public void setLoadingMore(OnLoadingMore loadingMore) {
        this.loadingMore = loadingMore;
    }

    public void setLoading(boolean loading) {
        isLoading = loading;
    }

    public void addData(T t) {
        dataSet.add(t);
        notifyDataSetChanged();
    }
public void setCanLoadMore(boolean canLoadMore) {    this.canLoadMore = canLoadMore;}
  //回调接口
    interface OnLoadingMore {
        void onLoadMore();
    }

}

对应的布局文件:
1.item_load_more: 很简单的一个ProgressBar 加上 一个TextView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ProgressBar
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:id="@+id/pb_loading"
        android:layout_toLeftOf="@+id/tv_msg" />

    <TextView
        android:id="@+id/tv_msg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="正在加载" />
</RelativeLayout>

2.item_normal:只是一个TextView

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    android:textColor="#000">
</TextView>

效果截图:


截图
截图

哈哈还是挺简单的吧。有问题的可以回复我。

完整的代码请移步github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,448评论 25 707
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,727评论 1 92
  • 在IntelliJ IDEA 15中使用Maven时,IDEA将默认的编译版本、源码版本设置为jdk5。编译项目的...
    天外之石阅读 882评论 0 0
  • 我知道,就算我真得被这个男性主宰的世界抛弃,你依旧会在,依旧会捧着我的脸,含着泪说爱我…… 就像,我爱你一样。 所...
    天官阅读 201评论 0 0