RecyclerView实现上拉加载,下拉刷新
2015-11-24 18:01
323 查看
前言
最近在使用RecyclerView,之所以不使用ListView是因为RecyclerView可以十分方便的实现横向的列表显示,这就是我抛弃ListView的原因。但是当使用纵向的RecyclerView的时候,问题就出现了。因为ListView在很多情况下是要上拉加载数据的,突然换到了RecyclerView就不知道该怎么办了。当然网上也有一些实现了屏蔽所包含View类型的上拉加载控件,但是我还是想要自己去继承RecyclerView来实现这个功能,那么就去干吧!实现
因为之前写过ListView的加载,那时候实现的时候是给ListView加一个FooterView,然后控制FooterView显示的时机就可以了,那么RecyclerView可不可以呢?答案是“不可以”!因为RecyclerView没有添加FooterView的方法,根本不能添加何谈控制时机啥的。那么就换个思路吧,有了!既然不能添加FooterView,那么我就把最后一个Item项变成FooterView,这样实现的效果和添加一个FooterView是一样的。思路是这样的,那么该怎么去做呢?很明显是要在Adapter上下功夫,在方法onCreateViewHolder中,我们返回的不是正常的ViewHolder而是我们定义的一个表示最后一个Item的ViewHolder,就叫FooterHolder吧。但是我们该怎么判断什么时候返回FooterHolder,什么时候返回正常Holder呢?
这个时候就要用到getItemViewType(int position)方法,这个方法传入的是Item的position,我们可以做如下的判断:
if (position + 1 == getItemCount()){ return TYPE_FOOTER; } else { return TYPE_ITEM; }
意思是说如果position+1等于总共的Item数量,那么当前Item表示的就是最后一个Item了,也就是我们要显示的加载界面。
然后再回到onCreateViewHolder(ViewGroup parent, int viewType)方法,这里传入了viewType方法,也就是getItemViewType得到的值,我们可以利用这个值来进行判断了,如下所示:
if(viewType==TYPE_ITEM){ //返回正常的Holder }else if(viewType==TYPE_FOOTER){ //返回FooterHolder }
好了,能够将FooterHolder加入RecyclerView的最后一项了,那么接下来的就是怎样正确的将它显示出来。
由ListView我们知道,当滑动到最后一项的时候,我们就将它显示,所以我们要对滑动进行监听,检测什么时候滑到最后一项。我们就新建一个类PTRecyclerView让它继承RecyclerView,然后在类的内部调用setOnScrollListener方法,这样就可以对滑动事件进行监听。传入的OnScrollListener我们需要覆盖其中的两个方法,如下所示:
this.setOnScrollListener(new OnScrollListener(){ @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState){ super.onScrollStateChanged(recyclerView, newState); LinearLayoutManager manager = (LinearLayoutManager) getLayoutManager(); if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == manager.getItemCount()){ //调用加载更多的方法 } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy){ super.onScrolled(recyclerView, dx, dy); lastVisibleItem = ((LinearLayoutManager)getLayoutManager()).findLastVisibleItemPosition(); } });
在onScrolled方法中,我用lastVisibleItem 来记录当前可见得最后一项的位置,这是为onScrollStateChanged方法服务的。在onScrollStateChanged方法中,利用lastVisibleItem 我们判断是不是可见的最后一项是RecyclerView的最后一项,如果是的话,那么就应该显示正在加载的界面,然后调用加载更多的方法。
当然在RecyclerView内部我们我们不能直接调用加载更多的方法,而是应该用一个接口,然后在Activity中实现此接口,通过接口来实现加载更多,把加载交给Activity层来进行操作。
这样能够实现加载更多,但是这样并不是就结束了。进行加载的话我们还需要对加载的结果进行处理,如果加载失败怎么办,如果没有更多数据了该怎么办,所以还需要后续的处理。
我想要显示的是下面的三种情况:
那么分别在什么时候显示呢?因为这涉及到最后一项的界面的改变,我们要拿到这个界面的对象,在PTRecyclerView内部我们是拿不到这个对象的,或者说就算拿到了也不行,我们不能把操作放在PTRecyclerView中,它只是告诉我们什么时候该加载,至于怎么加载,加载结果如何都不是它负责的事情了。所以我们要在Adapter内部来实现。
这里我使用了别人写好的一个通用Adapter,一个RecyclerView就要对应一个Adapter,又很多的方法和逻辑又是一样的,所以就把Adapter进行封装,屏蔽了那些一样的内容,只把不一样的内容让你去实现。所以我就在这个基础上加入加载的功能,目的也是实现封装,拿过来就能使用,去除很多重复的代码。
我们来看一下FooterHolder的部分实现:
public FooterHolder(View itemView){ super(itemView); mFooterTextView = (TextView) itemView.findViewById(R.id.tv_footer); mProgressBar = (ProgressBar) itemView.findViewById(R.id.footer_progressbar); mFooterTextView.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v){ if(mAdapter!=null&&mAdapter.getLoadState()==BaseRecyclerViewAdapter.STATE_FAILURE){ mFooterTextView.setText("加载中···"); mProgressBar.setVisibility(View.VISIBLE); mAdapter.reload(); mAdapter.setLoadState(BaseRecyclerViewAdapter.STATE_LOADING); } } }); }
从图中我们可以看到加载界面有一个TextView和一个ProgressBar,在构造方法中,我对TextView设置了点击监听,判断如果当前的加载状态是加载失败的话,那么点击之后就要重新进行加载。
在Activity中,我们实现那个加载的接口中的方法loadMore:
@Override public void loadMore(){ new Handler().postDelayed(new Runnable() { @Override public void run() { int result = new Random().nextInt(3); if(result==1){ //加载成功,处理数据 for(int i='A';i<='Z';i++) mDatas.add((char)i+""); mAdapter.notifyDataSetChanged(); mAdapter.finishLoad(BaseRecyclerViewAdapter.STATE_SUCCESS); }else if(result==0){ //加载失败 mAdapter.finishLoad(BaseRecyclerViewAdapter.STATE_FAILURE); }else if(result==2){ //没有更多数据了 mAdapter.finishLoad(BaseRecyclerViewAdapter.STATE_FINISH); } } },3000); }
这里我就简单模拟了一下加载的结果,加载成功就把数据加到列表中,然后调用notifyDataSetChanged方法,还有加载失败和没有更多数据的处理。
当然PTRecyclerView这个类可以选择要不要实现上拉加载的功能,这样一个类就可以实现两个功能了。我提供了一个方法,由Adapter实现。
private boolean mCanLoadMore = false; //说明是否实现上拉加载功能 public void setCanLoadMore(boolean loadMore){ mCanLoadMore = loadMore; }
最后我们来看一下效果:
源码下载
相关文章推荐
- 关于eclipse闪退的解决方案
- 在已经排好序的线性表中插入一个数,还是升序
- javascript变量作用域
- POJ 2299 Ultra-QuickSort 【树状数组求逆序数】
- Tomcat 生产服务器性能优化
- Unity C# 反编译
- 1-1-03:对齐输出
- Fleet(集群管理器)
- [转] Android LocalService与RemoteService理解
- zabbix实现mysql数据库的监控(一)
- 亚稳态分析
- DIV+CSS实操五:经管系网页内容模块内容添加(二)
- HQL查询
- c# 基础部分 (基本数据类型-- 表达式)
- 1-1 09:字符菱形
- linux mutt发送报表
- android studio 报 Error:(79) Error parsing XML: not well-formed (invalid token)
- java IO流文件的读写具体实例
- 论垃圾邮件危害性及U-Mail邮件系统必杀技
- StringBuffer setLength 和 append对capacity的影响