轻松实现RecycleView的下拉刷新、加载更多

来源:互联网 发布:中国网络教育大学 编辑:程序博客网 时间:2024/06/06 07:08

PullRefresh.gif

那如同这个题目,这里面涉及的东西其实还是比较多的,RecycleView SwipeRefreshLayout,下拉刷新(这个就是SwipeRefreshLayout的),加载更多。

SwipeRefreshLayout

这个是Google自己封装的一个下拉刷新的控件,里面使用了5.0开始的嵌套滑动机制,有兴趣的朋友可以去看看源码!使用起来其实就涉及到以下方法:

setOnRefreshListener() 下拉刷新的相关回调。

setRefresh() 通知是否开始刷新或者刷新完成。(坑1)

setColorSchemeColors() loading的时候progressbar的颜色,支持多个。

SwipeRefreshLayout的坑

进入页面调用setRefresh(true),更不不显示刷新的小圆圈?!
简单的说,这个就是在oncreate()方法执行的时候,view还没有绘制出来,这个时候你设置刷新不刷新其实都一样的,解决方法,post一下!

mRecyclerView.post(new Runnable() {        @Override        public void run() {            mRefreshLayout.setRefreshing(refresh);        }    });

RecycleView

RecycleView其实出现都有一定的年头了,前几天公司来面试的居然说他还没有用过。。这个也是醉醉的!

RecycleViewListView的强力升级!加入了holder便于管理和复用相同的类型。

就我目前掌握的情况,RecycleView对于ListView有了以下的不同:

1、加入了LayoutManager用用管理各种类型的布局,而且通过不同的布局可以实现横向、竖向、瀑布式的等各种复杂的布局。

2、加入Holder来管理相关布局和复用,对于每一种Type的View你都要创建一个对应的Holder来管理它!

3、取消了header和bottom布局。

4、没有现成的itemClick回调。

5、引入了丰富的动画效果。(坑4)

6、添加了丰富的数据刷新的方法,可以局部刷新了!(坑3)

7、可自定义相关分割线。

8、支持swipe删除和drag排序。(ItemTouchHelper 帮助类)

9、默认是不显示scrollBar的(坑2)

10、可以设置不同类型holder占据不同的空间(ItemColumnSpan)

上面这些不是所有的都讲,其实本文主要涉及的就是相关adapter,里面对应不同的holder,及相关的封装。然后说说踩了哪些坑。

基本思路

  • 1、明确什么时候开始加载更多?

下拉刷新就调用SwipeRefreshLayout相关就好了,那么加载更多呢?这个就要自己去写相关的布局了。然后第一个问题,什么时候加载更多??因为RecycleView有各种布局,所以判断最后一个也是要区分不同的adapter的!

  • 2、加载更多有多少种情况?
    大致有三种,正在加载更多;加载更多错误;没有更多数据了;

具体实现

1、监听滑动,满足条件开始加载更多。

        @Override    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {        if (null != scrollListener) {            scrollListener.onScrolled(SwipeRefreshRecycleView.this, dx, dy);        }        if (null == manager) {            throw new RuntimeException("you should call setLayoutManager() first!!");        }        if (null == adapter) {            throw new RuntimeException("you should call setAdapter() first!!");        }        if (manager instanceof LinearLayoutManager) {            int lastCompletelyVisibleItemPosition = ((LinearLayoutManager) manager).findLastCompletelyVisibleItemPosition();            if (adapter.getItemCount() > 1 && lastCompletelyVisibleItemPosition >= adapter.getItemCount() - 1 && adapter.isHasMore()) {                adapter.isLoadingMore();                if (null != listener) {                    listener.onLoadMore();                }            }            int position = ((LinearLayoutManager) manager).findFirstVisibleItemPosition();            if (lastTitlePos == position) {                return;            }            lastTitlePos = position;        }        if (manager instanceof StaggeredGridLayoutManager) {            int[] itemPositions = new int[2];            ((StaggeredGridLayoutManager) manager).findLastVisibleItemPositions(itemPositions);            int lastVisibleItemPosition = (itemPositions[1] != 0) ? ++itemPositions[1] : ++itemPositions[0];            if (lastVisibleItemPosition >= adapter.getItemCount()  && adapter.isHasMore()) {                adapter.isLoadingMore();                if (null != listener) {                    listener.onLoadMore();                }            }        }    }

2、定义自己的加载更多的ViewHolder。

3.定义相关的方法实时更新ViewHolder的三种状态。

public class NewBottomViewHolder extends RecyclerView.ViewHolder{    @Bind(R.id.footer_container)    public LinearLayout contaier;    @Bind(R.id.progressbar)    ProgressBar pb;    @Bind(R.id.content)    TextView content;    @Nullable    private final SwipeRefreshRecycleView.OnRefreshLoadMoreListener mListener;    public NewBottomViewHolder(View itemView, SwipeRefreshRecycleView.OnRefreshLoadMoreListener listener) {        super(itemView);        ButterKnife.bind(this,itemView);        mListener = listener;    }    public void bindDateView(int state) {        switch (state) {            case AdapterLoader.STATE_LASTED:                contaier.setVisibility(View.VISIBLE);                contaier.setOnClickListener(null);                pb.setVisibility(View.GONE);                content.setText("---  没有更多了  ---");                break;            case AdapterLoader.STATE_LOADING:                contaier.setVisibility(View.VISIBLE);                content.setText("加载更多!!");                contaier.setOnClickListener(null);                pb.setVisibility(View.VISIBLE);                break;            case AdapterLoader.STATE_ERROR:                contaier.setVisibility(View.VISIBLE);                pb.setVisibility(View.GONE);                content.setText("--- 加载更多失败点击重试 ---");                contaier.setOnClickListener(new View.OnClickListener() {                    @Override                    public void onClick(View v) {                        if (mListener != null) {                            mListener.onLoadMore();                        }                        content.setText("加载更多!!");                        pb.setVisibility(View.VISIBLE);                    }                });                break;        }    }}

4.定义相关扩展方法便于用户自己定义底部布局及相关状态处理。

这里就必须详细讲讲Adapter里面的相关方法了!

getItemCount(),在RecycleView知道它一共有多少数量的Item需要展示,返回0之后不会执行剩余的方法!

onCreateViewHolder(ViewGroup parent, int viewType),某种Type的Holder第一次创建的时候会调用该方法,当然没有复用的时候也会去创建,一旦复用了,改方法不会再执行了!

onBindViewHolder(RecyclerView.ViewHolder holder, int position),每一次更新对应itemView的时候都会调用该方法,所以在该方法中要实时的刷新数据!(因为存在复用,所以刷新的时候一定要彻底!!!

以上三个方法是必须实现的,因为在父类adapter里是抽象滴!

还有一个方法也比较重要:

getItemViewType(int position),这个方法是返回对应pos的类型的,如果你只有一个类型,不需要重写该方法,默认返回的是0。

是不是这么说起来比ListView还要爽一点儿?不用去判断什么convertView==null!

@Overridepublic final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {    switch (viewType) {        case TYPE_BOTTOM:            if (loadMore != null) {                RecyclerView.ViewHolder holder = onBottomViewHolderCreate(loadMore);                if (holder == null) {                    throw new RuntimeException("You must impl onBottomViewHolderCreate() and return your own holder ");                }                return holder;            } else {                return new BottomViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_footer, parent, false));            }        default:            return onViewHolderCreate(parent, viewType);    }}@Overridepublic final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {    if (getItemViewType(position) == TYPE_BOTTOM) {        loadState = loadState == STATE_ERROR ? STATE_ERROR : isHasMore() ? STATE_LOADING : STATE_LASTED;        if (loadMore != null) {            try {                onBottomViewHolderBind(holder, loadState);            } catch (Exception e) {                e.printStackTrace();            }        } else {            try {                ((BottomViewHolder) holder).bindDateView(loadState);            } catch (Exception e) {                e.printStackTrace();            }        }    } else {        onViewHolderBind(holder, position);    }}

这里在RefreshRecycleAdapter<T>中实现了刚刚说的三个方法,并且相关的已经加final修饰了!所以之后你只需要实现如下方法来完成你自己的item填充就好了:

   RecyclerView.ViewHolder onViewHolderCreate(ViewGroup parent, int viewType);void onViewHolderBind(RecyclerView.ViewHolder holder, int position);

对于加载更多的几种状态的更改,提供如下的相关方法!

boolean isHasMore();void isLoadingMore();void loadMoreError();

对于创建自己制定的加载更多的布局,提供如下方法扩展!

void setLoadMoreView(View view);RecyclerView.ViewHolder onBottomViewHolderCreate(View loadMore);void onBottomViewHolderBind(RecyclerView.ViewHolder holder, int loadState);

还没有说的那就是数据源相关的方法。提供了set和append两种方式!

void setList(List<T> data);void appendList(List<T> data);@Overridepublic final void appendList(List<T> data) {    int positionStart = list.size();    list.addAll(data);    int itemCount = list.size() - positionStart;    if (positionStart == 0) {        notifyDataSetChanged();    } else {        notifyItemRangeInserted(positionStart + 1, itemCount);    }}

还是那话,这些方法都是RefreshRecycleAdapter<T>里面写好的,我们写自己的adapter时更本不用去care!只需要去调用setList()或者appendList()就好了!!

说到这里不得不提提RecycleView刷新数据的相关方法和坑!

notifyDataSetChanged()的基础上, RecycleView增加了一系列的方法用于增删改。。

notifyItemInserted();notifyItemRangeInserted();notifyItemChanged();notifyItemRangeChanged();notifyItemRemoved();notifyItemRangeRemoved();

在使用的过程中,发现调用notifyItemChanged()之后不会去执行onBindViewHolder(),(坑3 坑4)这个就导致刷新没有触发了!最后搜到的结果是因为mRecyclerView.setItemAnimator(new DefaultItemAnimator())引起的,解决方案是复写相关方法

@Overridepublic boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull List<Object> payloads) {    return true;}

小结

首先通过addList()或者appendList()的方法设置相关数据源。

滑动过程中需要加载更多时回调相关方法,并在adapter中通知相关状态刷新。

 adapter.isLoadingMore(); if (null != listener) {    listener.onLoadMore(); }

加载错误的时候调用相关的方法通知状态改变。

adapter.loadMoreError();

创建BottomHolder的时候判断有没有设置自定义的view,如果有,那么就去走子类的onBottomViewHolderCreate()方法创建自定义的Bottomholder,然后实时更新相关数据!

    @Overridepublic final void setLoadMoreView(@NonNull View view) {    loadMore = view;}if (loadMore != null) {    RecyclerView.ViewHolder holder = onBottomViewHolderCreate(loadMore);    if (holder == null) {        throw new RuntimeException("You must impl onBottomViewHolderCreate() and return your own holder ");    }    return holder;        } 

最后在onBottomViewHolderBind(RecyclerView.ViewHolder holder, int state)的方法中执行bindDateView(state)实时刷新相关的状态。

public void bindDateView(int state) {    switch (state) {        case AdapterLoader.STATE_LASTED:            contaier.setVisibility(View.VISIBLE);            contaier.setOnClickListener(null);            pb.setVisibility(View.GONE);            content.setText("---  没有更多了  ---");            break;        case AdapterLoader.STATE_LOADING:            contaier.setVisibility(View.VISIBLE);            content.setText("加载更多!!");            contaier.setOnClickListener(null);            pb.setVisibility(View.VISIBLE);            break;        case AdapterLoader.STATE_ERROR:            contaier.setVisibility(View.VISIBLE);            pb.setVisibility(View.GONE);            content.setText("--- 加载更多失败点击重试 ---");            contaier.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    if (mListener != null) {                        mListener.onLoadMore();                    }                    content.setText("加载更多!!");                    pb.setVisibility(View.VISIBLE);                }            });            break;    }}

PS 最后还有默认是不显示scrollBar的问题,这个问题,似乎必须在xml里面配置,不能代码直接new RecycleView。然后可以使用相关代码控制ScrollBar是否显示!

mRecyclerView.setVerticalScrollBarEnabled(true)<android.support.v7.widget.RecyclerView    android:id="@+id/recycle_view"    android:scrollbars="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent" />

另外对于特性8、10这里就不详细介绍了,滑动删除和拖拽排序在TouchHelperCallback中有相关支持!方法如下:

@Overridepublic boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {    if (callBack != null) {        callBack.onItemMove(viewHolder.getAdapterPosition(),                target.getAdapterPosition());    }    return true;}@Overridepublic void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {    if (callBack != null) {        callBack.onItemDismiss(viewHolder.getAdapterPosition());    }}

详细的可以参照相关Demo-FangShiActivity


gradle快速集成

  compile 'com.lovejjfg.powerrecycle:powerrecycle:1.0.0'

相关下载

演示Demo下载

项目中的使用

—- Edit By Joe —-

1 0
原创粉丝点击