个人原创,转载请注明出处http://blog.csdn.net/u012402124/article/details/78210639
2018年10月25日更新
让大家花费时间看文章却没有解决需求,随着bug的增多内心的愧疚感逐渐增强,但几个月前的代码确实看不下去,bug也不想解决了,于是直接重写了。实现思路未变,依旧是监听滑动,根据位置做出处理,但实现方式做了修改,相比于之前的方式使用起来更加简单。
暂时只支持LinearLayout,不支持GridLayoutManager和StaggeredLayoutManager
布局文件,如下,不多说:
<com.oy.wrapperrecyclerview.widget.xRecyclerView android:id="@+id/gank_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never"/>Adapter不能再继承RecyclerView.Adapter,需要继承自xRecyclerView.xAdapter,同时实现三个方法,getxItemCount、onCreatexViewHolder和onBindxViewHolder,按照实现RecyclerView.Adapter中的getItemCount、onCreateViewHolder和onBindViewHolder的方式即可。
将2中xRecyclerView.xAdapter的实现类的实例通过xRecyclerView.setAdapter设置进去。
通过xRecyclerView.setListener监听下拉刷新和上拉加载动作。
下拉刷新或上拉加载完成后需要调用xRecyclerView.stopRefreshing或xRecyclerView.stopLoadingMore来更新显示状态。
一共这5步,简化了不少。因为还不支持GridLayoutManager所以把原来看图片的网格布局改成线性的了,不过话说回来看大图里的妹子真不错 ?。
代码已更新到Github,如有问题:oynix@foxmail.com
以下为原文
纵观多数App,下拉刷新和上拉加载更多是很常见的功能,但是谷歌官方只有一个SwipeRefreshLayout用来下拉刷新,上拉加载更多还要自己做。
基于RecyclerView简单封装了这两个操作,下拉刷新支持LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager;上拉加载更多只支持前两者。
(下拉刷新)
(上拉加载更多 – LinearLayoutManager)
(上拉加载更多 – GridLayoutManager)
1.1 布局文件
// 用SwipeRefreshLayout包裹RecyclerView <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/gank_swipe_refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/gank_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never"/> </android.support.v4.widget.SwipeRefreshLayout>1.2 给SwipeRefreshLayout 添加监听 增加触发刷新时的操作(比如重新请求数据)
SwipeRefreshLayout swipeRefreshLayout = findViewById(); swipeRefreshLayout.setOnRefreshListener(new swipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { // do something, such as re-request from server or other } });1.3 刷新操作(重新请求数据)完成后要回调来停止隐藏刷新动画(中上方圆形悬浮进度条旋转动画)
swipeRefreshLayout.setRefreshing(false); 1至此下拉刷新完成
2.上拉加载3步走2.1 初始化AdapterWrapper和SwipeToLoadHelper
// adapter是你自己为RecyclerView写的Adapter RecyclerView.Adapter adapter = new YourOwnAdapter(); AdapterWrapper adapterWrapper = new AdapterWrapper(adapter); RecyclerView recyclerView = findViewById(); // 将RecyclerView和刚创建的adapterWrapper传入 SwipeToLoadHelper helper = new SwipeToLoadHelper(recyclerView, adapterWrapper);2.2 设置加载动作触发后的监听
helper.setLoadMoreListener(new SwipeToLoadHelper.LoadMoreListener() { @Override public void onLoad() { // do something, such as request more data from server or other. } })2.3 加载更多内容完成后要回调方法停止动画
helper.setLoadMoreFinish() 1至此上拉加载完成 (注意更新数据时要调用AdapterWrapper.notifyDataSetChanged)
RecyclerView的itemView的显示情况分为四种:
第1个可见的(部分显示或者完全显示都算可见)第1个可见的且是完整的(完全显示算作完整的)最后1个可见的最后1个可见的且是完整的 1. 回弹效果即手指抬起滑动停止,上拉加载更多部分显示时,将上拉加载更多滚动到不显示,使上面挨着它的itemView为最后1个可见且是最后1个完整可见。监听RecyclerView的滚动,当RecyclerView处于SCROLL_STATE_IDLE 状态时,获取最后1个完整可见的itemView:如果是倒数第2个item则计算该item的下边距到RecyclerView底部的距离deltaY,然后将RecyclerView向下滚动deltaY;如果是上拉加载更多则触发加载操作;其他情况不用处理。 // 关键代码 rv : recyclerView int lcp= layoutManager.findLastCompletelyVisibleItemPosition(); if (lcp == layoutManager.getItemCount() - 2) { // 倒数第2项 int fcp= layoutManager.findFirstCompletelyVisibleItemPosition(); View child = layoutManager.findViewByPosition(lcp); int deltaY = rv.getBottom() - rv.getPaddingBottom() - child.getBottom(); // fcp为0时说明列表滚动到了顶部, 不再滚动 if (deltaY > 0 && fcp!= 0) { rv.smoothScrollBy(0, -deltaY); } } else if (lcp== layoutManager.getItemCount() - 1) { // 最后一项完全显示, 触发操作, 执行加载更多操作 if (listener!= null) { listener.onLoad(); } } 2. 添加底部加载更多itemView2.1 AdapterWrapper重写了getItemCount方法,保证得到itemView的数量包括加载更多。当是LinearLayoutManager类型时直接加1;当是GridLayoutManager类型时,如果需要则先将列表最后一行填满,再加1。比如:列表每行有3个itemView,最后一行只有1个,这时就需要先加2,再加1,来保证加载更多占据完整的一行。
// 关键代码 其中的adapter为构造函数中传入的原生RecyclerView.Adapter if (adapterType == ADAPTER_TYPE_LINEAR) { // 线性布局 return adapter.getItemCount() + 1; } else { // 网格布局 spanCount为每行itemView的个数 int remain = adapter.getItemCount() % spanCount; // 余数 if (remain == 0) { return adapter.getItemCount() + 1; } // 余数不为0时,先凑满再加1 return adapter.getItemCount() + 1 + (spanCount - remain); }2.2 AdapterWrapper重写了getItemViewType方法,当是最后一个位置时返回ITEM_TYPE_LOAD
// 关键代码 public int getItemViewType(int position) { // 位置是最后一个时, wrapper进行拦截 if (osition == getItemCount() - 1) { return ITEM_TYPE_LOAD;// 要避免和原生adapter返回值可能重复 } // 其他情况交给原生adapter处理 return adapter.getItemViewType(position); }2.3 AdapterWrapper重写了onCreateViewHolder方法,当类型为ITEM_TYPE_LOAD时返回加载更多的ViewHolder,其他情况交给原生的adapter处理。
// 关键代码 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == ITEM_TYPE_LOAD) { return new LoadMoreHolder(); } else { return adapter.onCreateViewHolder(parent, viewType); } }2.4 AdapterWrapper重写了onBindViewHolder,这里有三种可能的情况:1. 正常的数据项itemView,交给adapter处理;2. GridView的空白itemView,隐藏处理;3. 底部的加载更多,目前不需要做什么处理。
// 关键代码 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (position == getItemCount() - 1) { } else if (position < adapter.getItemCount()){ adapter.onBindViewHolder(holder, position); } else { holder.itemView.setVisibility(View.INVISIBLE); } }转载于:https://www.cnblogs.com/it-tsz/p/11373263.html
相关资源:JAVA上百实例源码以及开源项目