RecyclerView的上拉加载,下拉刷新
2016-03-29 18:20
260 查看
项目中最常使用的listview,其重要程度不言而喻。RecyclerView是对ListView的升级版,据说性能要比ListView高,而且功能要更强大。比如一个RecyclerView可以直接实现瀑布流,而且有着默认的动画效果。
上拉加载,下拉刷新是RecyclerView的拓展的非常实用的功能,在git上也有很多例子,但大都有非常繁琐的逻辑,自带好多不需要的功能。比如我们仅仅需要刷新功能就要导入一个jar包,直接的增加了安装包的体积。现在把RecyclerView的刷新功能抽离出来,做一个纯净的刷新列表,而且代码执行效率要高这么一点点。虽然说纯净,但是和市面上的刷新列表基本类似。
首先直接定义一个XRecyclerView继承RecyclerView,重写他的三个构造方法。
所以我们先自定义一个底部布局LoadingMoreFooter继承Linearlayout,里面是一个居中显示的ProgressBar和一个TextView,添加一个方法setState(int state),来判定当前刷新的状态
RecyclerView的上拉加载,原理很简单,无非就是当滑动到底部的时候,如果有数据,并且允许加载,就请求数据添加到adapter,而RecyclerView需要做的就是当加载的时候,在底部显示正在加载来提醒用户。判断是否滑动到底部,并且进行加载
写到这个地方,基本的上拉加载的逻辑就搞定了,然后就是处理细节。我们需要把底部布局LoadingMoreFooter加载到RecyclerView,这时候重写setAdapter(Adapter adapter)方法来添加一个adapter。我们这个需要自定义一个adapter来把底部布局加进去。自定义的Adapter如下
回到setAdapter(Adapter adapter)方法
定义一个mDataObserver
都是直接调用父类的方法就可以。
这样一个RecyclerView的上拉加载逻辑就全部搞定了,这是极其简单的封装方法,所以逻辑没有多么的复杂。
好的,来回顾一下逻辑:重写RecyclerView进行滑动监听,当滑动到底部的时候通过重写setAdapter来将底部视图加载出来,最后对mAdapter进行数据更改的监听。
上拉刷新的逻辑更简单,因为有谷歌的SwipeRefreshLayout,所以实现起来就简单很多,首先来看布局文件
直接用SwipeRefreshLayout将我们刚才自定义的RecyclerView包裹起来,然后swipeRefreshLayout.setOnRefreshListener(this)进行监听,实现onRefresh()接口来实现加载的逻辑。
在刚进入页面就进行刷新
就这样,一个简单实用的RecyclerView上拉加载,下拉刷新就实现了。没有多余的布局文件,极其简便。当我们项目中有多个RecyclerView并且要求上拉加载,下拉刷新的时候,我们可以定义一个抽象类,只通过修改itemView的布局就能实现。
下一篇将对自定义的XRecyclerView进行拓展,可以随意添加头布局。
上拉加载,下拉刷新是RecyclerView的拓展的非常实用的功能,在git上也有很多例子,但大都有非常繁琐的逻辑,自带好多不需要的功能。比如我们仅仅需要刷新功能就要导入一个jar包,直接的增加了安装包的体积。现在把RecyclerView的刷新功能抽离出来,做一个纯净的刷新列表,而且代码执行效率要高这么一点点。虽然说纯净,但是和市面上的刷新列表基本类似。
首先直接定义一个XRecyclerView继承RecyclerView,重写他的三个构造方法。
public XRecylcerView(Context context) { this(context, null); } public XRecylcerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public XRecylcerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); }init(Context mContext)方法用来初始化底部加载的view
所以我们先自定义一个底部布局LoadingMoreFooter继承Linearlayout,里面是一个居中显示的ProgressBar和一个TextView,添加一个方法setState(int state),来判定当前刷新的状态
public void setState(int state) { switch (state) { // 刷新中 case STATE_LAODING: progressCon.setVisibility(View.VISIBLE); mText.setText("正在刷新"); this.setVisibility(View.VISIBLE); break; // 刷新完成 case STATE_COMPLETE: mText.setText("刷新完成"); this.setVisibility(View.GONE); break; // 没有更多数据 case STATE_NOMORE: mText.setText("没有更多数据啦"); progressCon.setVisibility(View.GONE); this.setVisibility(View.VISIBLE); break; } }回到XRecyclerView,实现init
private void init(Context context) { mContext = context; // loadingMoreEnabled为下拉的开关 if (loadingMoreEnabled) { LoadingMoreFooter footerView = new LoadingMoreFooter(mContext); addFootView(footerView); mFootViews.get(0).setVisibility(GONE); } }
RecyclerView的上拉加载,原理很简单,无非就是当滑动到底部的时候,如果有数据,并且允许加载,就请求数据添加到adapter,而RecyclerView需要做的就是当加载的时候,在底部显示正在加载来提醒用户。判断是否滑动到底部,并且进行加载
/** * 监听滑动,来定位当前滑动到哪个地方 * * @param state */ @Override public void onScrollStateChanged(int state) { super.onScrollStateChanged(state); if (state == RecyclerView.SCROLL_STATE_IDLE && mLoadingListener != null && !isLoadingData && loadingMoreEnabled) { LayoutManager layoutManager = getLayoutManager(); int lastVisibleItemPosition; if (layoutManager instanceof GridLayoutManager) { lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into); lastVisibleItemPosition = findMax(into); } else { lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); } if (layoutManager.getChildCount() > 0 && lastVisibleItemPosition >= layoutManager.getItemCount() - 1 && layoutManager.getItemCount() > layoutManager.getChildCount() && !isnomore) { View footView = mFootViews.get(0); isLoadingData = true; if (footView instanceof LoadingMoreFooter) { ((LoadingMoreFooter) footView).setState(LoadingMoreFooter.STATE_LAODING); } else { footView.setVisibility(View.VISIBLE); } mLoadingListener.onLoadMore(); // 一个回调接口,用来加载数据 } } }
写到这个地方,基本的上拉加载的逻辑就搞定了,然后就是处理细节。我们需要把底部布局LoadingMoreFooter加载到RecyclerView,这时候重写setAdapter(Adapter adapter)方法来添加一个adapter。我们这个需要自定义一个adapter来把底部布局加进去。自定义的Adapter如下
private class WrapAdapter extends RecyclerView.Adapter<ViewHolder> { private RecyclerView.Adapter adapter; private ArrayList<View> mFootViews; private int headerPosition = 0; public WrapAdapter(ArrayList<View> footViews, RecyclerView.Adapter adapter) { this.adapter = adapter; this.mFootViews = footViews; } @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if(manager instanceof GridLayoutManager) { final GridLayoutManager gridManager = ((GridLayoutManager) manager); gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return (isFooter(position)) ? gridManager.getSpanCount() : 1; } }); } } @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { super.onViewAttachedToWindow(holder); ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); if(lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams && (isFooter( holder.getLayoutPosition())) { StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp; p.setFullSpan(true); } } public boolean isFooter(int position) { return position < getItemCount() && position >= getItemCount() - mFootViews.size(); } public int getFootersCount() { return mFootViews.size(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_FOOTER) { return new SimpleViewHolder(mFootViews.get(0)); } return adapter.onCreateViewHolder(parent, viewType); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (isHeader(position)) { return; } int adjPosition = position; int adapterCount; if (adapter != null) { adapterCount = adapter.getItemCount(); if (adjPosition < adapterCount) { adapter.onBindViewHolder(holder, adjPosition); return; } } } @Override public int getItemCount() { if (adapter != null) { return getFootersCount() + adapter.getItemCount(); } else { return getFootersCount(); } } @Override public int getItemViewType(int position) { if(isFooter(position)){ return TYPE_FOOTER; } int adjPosition = position; int adapterCount; if (adapter != null) { adapterCount = adapter.getItemCount(); if (adjPosition < adapterCount) { return adapter.getItemViewType(adjPosition); } } return TYPE_NORMAL; } @Override public long getItemId(int position) { if (adapter != null) { int adjPosition = position - getHeadersCount(); int adapterCount = adapter.getItemCount(); if (adjPosition < adapterCount) { return adapter.getItemId(adjPosition); } } return -1; } private class SimpleViewHolder extends RecyclerView.ViewHolder { public SimpleViewHolder(View itemView) { super(itemView); } } }就是一个继承自RecyclerView.Adapter的adapter,主要用于根据类型加载不同的布局,普通的itemView和“正在加载”的底部提示。
回到setAdapter(Adapter adapter)方法
/** * 重写Adapter,通过状态判断是否显示“正在加载” * * @param adapter */ @Override public void setAdapter(Adapter adapter) { this.mAdapter = adapter; this.mWrapAdapter = new WrapAdapter(mFootViews, mAdapter);// 定义WrapAdapter super.setAdapter(mWrapAdapter);// 通过父类方法将自定义的Adapter重新设置进去 mAdapter.registerAdapterDataObserver(mDataObserver);//请看下面分析 }查看super.setAdapter()方法,会找到adapter.registerAdapterDataObserver(mObserver)方法,当adapter里面的数据发生改变时会即时监听并且更新。那为什么还要把mAdapter再设置一遍呢?其实当我们调用通过我们自定义的RecyclerView来调用setAdapter方法时,只有当WrapAdapter数据改变的时候,才会有更新,而当我们仅仅只更新mAdapter里面的数据的时候,如果不监听,我们看到的itemView并没有改变。
定义一个mDataObserver
private final RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { mWrapAdapter.notifyDataSetChanged(); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { mWrapAdapter.notifyItemRangeInserted(positionStart, itemCount); } @Override public void onItemRangeChanged(int positionStart, int itemCount) { mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount); } @Override public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount, payload); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { mWrapAdapter.notifyItemRangeRemoved(positionStart, itemCount); } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { mWrapAdapter.notifyItemMoved(fromPosition, toPosition); } };
都是直接调用父类的方法就可以。
这样一个RecyclerView的上拉加载逻辑就全部搞定了,这是极其简单的封装方法,所以逻辑没有多么的复杂。
好的,来回顾一下逻辑:重写RecyclerView进行滑动监听,当滑动到底部的时候通过重写setAdapter来将底部视图加载出来,最后对mAdapter进行数据更改的监听。
上拉刷新的逻辑更简单,因为有谷歌的SwipeRefreshLayout,所以实现起来就简单很多,首先来看布局文件
<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.baiyyyhjl.pullrecyclerview.recyclerview.XRecylcerView android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="wrap_content" /> </android.support.v4.widget.SwipeRefreshLayout>
直接用SwipeRefreshLayout将我们刚才自定义的RecyclerView包裹起来,然后swipeRefreshLayout.setOnRefreshListener(this)进行监听,实现onRefresh()接口来实现加载的逻辑。
在刚进入页面就进行刷新
swipeRefreshLayout.post(new Runnable() { @Override public void run() { swipeRefreshLayout.setRefreshing(true); // TODO 获取数据 } });
就这样,一个简单实用的RecyclerView上拉加载,下拉刷新就实现了。没有多余的布局文件,极其简便。当我们项目中有多个RecyclerView并且要求上拉加载,下拉刷新的时候,我们可以定义一个抽象类,只通过修改itemView的布局就能实现。
下一篇将对自定义的XRecyclerView进行拓展,可以随意添加头布局。
相关文章推荐
- 好玩的linux命令
- angular先加载页面再执行事件,特别在动态生成id,然后做echarts等图表
- Qt自定义委托在QTableView中绘制控件、图片、文字(内容比较全)
- BZOJ1218 [HNOI2003] 激光炸弹
- 第五周技术博客
- noip2002 矩形覆盖
- JFinal+maven+freemarker入门教程
- FFmpeg的一些关键的数据结构(二)
- Mockito:一个强大的用于Java开发的模拟测试框架
- Python 实现简单的爬虫功能并保存到本地
- Nginx 500错误总结
- GCD 之线程死锁
- JVM远程DEBUG(JPDA )
- 运维监控报警短信功能 可否实现部分重要业务升级为电话的方式?
- 关于屏幕适配问题的解决方案
- Java数据类型中String、Integer、int相互间的转换
- FFmpeg的一些关键的数据结构(一)
- JVM远程DEBUG(JPDA )
- jQuery 效果 - 滑动
- Linux---centos编译安装mysql5.6