源码地址

recyclerview已经代替了listview,它可以实现线性、网络、瀑布流三种样式的局部,很方便使用。但是它没有listview可以添加头部和底部的方法,那上拉加载、下拉刷新怎么实现呢,还好它可以通过viewType来选定在某个特定位置显示对应的布局。我这里指定了四种类型,header、item、footer、endView(没有更多显示的布局)。

如何方便的让getItemViewType在某个位置返回指定的类型呢,我觉得在getItemViewType方法里通过加载的itemCount和position判断很头疼,而且也容易出现bug。我是在数据里直接加了viewType类型,通过this.list.get(position).getViewType()返回指定的类型,你只要在设置数据的时候设置好就完美解决了。

再来说说网格、瀑布流这两种布局在头部和底部如何只显示一条充满宽度的样式。查看源码可理解问什么要这么做,我在代码里也有注释。

看效果:

//用于GridLayoutManager header footer只显示一行@Overridepublic void onAttachedToRecyclerView(RecyclerView recyclerView){super.onAttachedToRecyclerView(recyclerView);RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();if (manager instanceof GridLayoutManager) {final GridLayoutManager layout = ((GridLayoutManager) manager);GridLayoutManager.SpanSizeLookup mGridSpanSizeLookup = new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {Log.e("GridSpanSizeLookup", "GridSpanSizeLookup:" + position);if (list.get(position).getViewType() == 2||list.get(position).getViewType()==0||list.get(position).getViewType()==3) {return layout.getSpanCount();} else {//The number of spans occupied by the item at the provided position,Default Each item occupies 1 span.//在某个位置的item所占用的跨度的数量,默认情况下占用一个跨度。return 1;}}};layout.setSpanSizeLookup(mGridSpanSizeLookup);}}
//用于StaggeredGridLayoutManager header footer只显示一行@Overridepublic void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {super.onViewAttachedToWindow(holder);ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();if(layoutParams!=null&&layoutParams instanceof StaggeredGridLayoutManager.LayoutParams){StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) layoutParams;int position = holder.getLayoutPosition();if(list.get(position).getViewType() == 2||list.get(position).getViewType()==0||list.get(position).getViewType()==3){// 如果方向是纵向的,视图将充满整个宽度,方向为横向,视图将充满整个高度。params.setFullSpan(true);}}}

这样就完美解决这两种布局的特殊性了;

如何下拉刷新呢?,我是通过当列表滑动到顶部时,此时header的高度为0,下拉时通过滑动的距离动态设置header的高度来实现下拉刷新。

先看下改变header高度的临界值,也就是可下拉的条件。主要是当firstVisibleItemPosition<=1时获取firstView.getTop()的距离大于0都满足时条件成立,可下拉。

/*** 列表可下拉的条件* @return*/public boolean isRefresh(){switch (layoutState){case 0:l_manager = (LinearLayoutManager)getLayoutManager();firstVisibleItemPosition=  l_manager .findFirstVisibleItemPosition();break;case 1:g_manager = (GridLayoutManager)getLayoutManager();firstVisibleItemPosition = g_manager .findFirstVisibleItemPosition();break;case 2:s_manager = (StaggeredGridLayoutManager)getLayoutManager();int[] fPosoitions = s_manager .findFirstVisibleItemPositions(new int[2]);firstVisibleItemPosition = fPosoitions[0];break;}manager = getLayoutManager();if(firstVisibleItemPosition<=1){firstView = manager.findViewByPosition(1);if(firstView!=null&&firstView.getTop()>=0){return  true;}}return false;}

通过RecyclerView的onTouchEvent方法获取下拉的距离。refreshState = 0;//0 下拉,1 松开 2 刷新 3 刷新完成 ,pullDistance和refreshCritical 当refreshState为1和2的临界值。

public boolean onTouchEvent(MotionEvent e) {if(isRefresh()){switch (e.getAction()){case MotionEvent.ACTION_DOWN:disY = e.getRawY();disX = e.getRawX();break;case MotionEvent.ACTION_MOVE:float offsetY = e.getRawY()-disY;float offsetX = e.getRawX()-disX;disY = e.getRawY();disX = e.getRawX();distanceY = distanceY + offsetY;if(offsetY>0){refreshState = 0;if(distanceY>pullDistance){refreshState = 1;}changeRefreshViewHeight((int)distanceY,refreshState);}break;case MotionEvent.ACTION_UP:if(distanceY>pullDistance){scroller.startScroll(0,(int)distanceY,0,-(int)distanceY+refreshCritical);}else{scroller.startScroll(0,(int)distanceY,0,-(int)distanceY);}invalidate();break;default:break;}}return super.onTouchEvent(e);}

改变头部高度的代码

 /*** 改变头部的高度* @param distance* @param state*/public void changeRefreshViewHeight(int distance,int state){refreshView = manager.getChildAt(0);if(refreshView!=null&&refreshView instanceof HeaderView){HeaderView headerView = (HeaderView) refreshView;headerView.setViewHeaderHeight(distance,state);}}/*** 设置高度* @param height*/public void setViewHeaderHeight(int height,int refreshState){Log.e("height","height:"+height);if(height<0){return;}ll_layout.setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL);LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)ll_layout.getLayoutParams();params.height = height;params.gravity = Gravity.BOTTOM;ll_layout.setLayoutParams(params);tv_refresh.setGravity(Gravity.BOTTOM);LinearLayout.LayoutParams pt = (LinearLayout.LayoutParams)tv_refresh.getLayoutParams();/if(refreshState == 0){tv_refresh.setText("下拉刷新...");animation.setFillAfter(false);isStartAnimation = false;progressbar.setVisibility(View.GONE);iv_arrow.setVisibility(View.VISIBLE);pt.gravity = Gravity.BOTTOM;}else if(refreshState == 2){//如果不清除动画,箭头将无法隐藏iv_arrow.clearAnimation();tv_refresh.setText("正在刷新...");progressbar.setVisibility(View.VISIBLE);iv_arrow.setVisibility(View.GONE);pt.gravity = Gravity.CENTER;}else if(refreshState == 1){if(!isStartAnimation){isStartAnimation = true;animation.setFillAfter(true);iv_arrow.startAnimation(animation);}tv_refresh.setText("松开刷新...");progressbar.setVisibility(View.GONE);iv_arrow.setVisibility(View.VISIBLE);pt.gravity = Gravity.BOTTOM;}tv_refresh.setLayoutParams(pt);}

当下拉手指送开时,会启动如下代码:

if(distanceY>pullDistance){scroller.startScroll(0,(int)distanceY,0,-(int)distanceY+refreshCritical);}else{scroller.startScroll(0,(int)distanceY,0,-(int)distanceY);}invalidate();//scroller滚动完成调用的代码public void computeScroll() {super.computeScroll();if(scroller.computeScrollOffset()){distanceY = scroller.getCurrY();Log.e("startY","startY:"+distanceY);if(distanceY == refreshCritical){refreshState = 2;changeRefreshViewHeight((int) (distanceY),refreshState);if(recyclerviewPullRefreshListener!=null){recyclerviewPullRefreshListener.recyclerviewPullRefresh();}}else if(distanceY<refreshCritical&&refreshState ==3){changeRefreshViewHeight((int) (distanceY),refreshState);}else {refreshState = 1;changeRefreshViewHeight((int) (distanceY),refreshState);}}}

当刷新完成是我做了一点让刷新完成停留一会的小操作,为了让效果美观一点。

/*** 刷新完成*/public void refreshFinish(){new Thread(){@Overridepublic void run() {try {refreshState = 3;handler.sendEmptyMessage(0);Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}handler.sendEmptyMessage(1);}}.start();}private MyHandler handler = new MyHandler();public class MyHandler extends Handler {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if(msg.what == 0){if(refreshView == null){refreshView = manager.getChildAt(0);}if(refreshView instanceof HeaderView){HeaderView headerView = (HeaderView) refreshView;headerView.end();}}else if(msg.what == 1){scroller.startScroll(0,(int)distanceY,0,-(int)distanceY,pullDistance);invalidate();}}}

刷新的接口如下,Activity里别忘注册监听,否则就用不了了。

//设置监听public void setRecyclerviewPullRefreshListener(RecyclerviewPullRefreshListener recyclerviewPullRefreshListener,int layoutState){this.recyclerviewPullRefreshListener = recyclerviewPullRefreshListener;this.layoutState = layoutState;}//下拉刷新private RecyclerviewPullRefreshListener recyclerviewPullRefreshListener;public interface RecyclerviewPullRefreshListener{public void recyclerviewPullRefresh();}

再来说说上拉加载更多,这个相对就简单一点了,通过RecyclerView的OnScrollListener监听,当滑动到if(lastVisibleItem >= totalItemCount-1&&!isOnLoad)时调用loadMore()方法刷新适配器就可以,而刷新footer的时候,调用notifyItemChanged()方法更方便。我这里也做了当没有跟多的时候显示我到底了,不可再上拉操作。

recyclerview.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(RecyclerView recyclerView, int newState) {super.onScrollStateChanged(recyclerView, newState);if(newState == RecyclerView.SCROLL_STATE_DRAGGING){int totalItemCount  = layout.getItemCount();int lastVisibleItem = layout.findLastVisibleItemPosition();if(lastVisibleItem >= totalItemCount-1&&!isOnLoad){loadMore();}}}public void onScrolled(RecyclerView recyclerView, int dx, int dy){super.onScrolled(recyclerView,dx,dy);Log.e("newStatedy","newStatedy:"+dy);}});public void updateAdapter(List<RecyclerViewBean> list){if(list==null||list.size()==0){isLoadEnd = true;list.add(new RecyclerViewBean("",3));}this.list.addAll(list);notifyDataSetChanged();}private boolean isLoadEnd = false;public void updateFooterItem(RecyclerViewBean recyclerViewBean){this.recyclerViewBean = recyclerViewBean;this.list.add(recyclerViewBean);notifyItemChanged(getItemCount());}public void removeFooterItem(){if(recyclerViewBean!=null){this.list.remove(recyclerViewBean);notifyItemChanged(getItemCount());recyclerViewBean = null;}}public boolean getIsLoadEnd(){return isLoadEnd;}

有兴趣的同学直接看项目吧:源码地址

RecyclerView三种布局下的上拉加载 下拉刷新相关推荐

  1. 教你如何使用SwipeRefreshLayout来构建一个上拉加载下拉刷新框架

    前言: 基本上所以的移动端应用都有Listview(当然RecyclerView也一样),那必不可少的都会嵌入一个上拉加载下拉刷新的功能.这样能大大的减少用户的流量消耗,同样对于用户也有更好的用户体验 ...

  2. php微信小程序向下滑动,微信小程序功能实现:上滑加载下拉刷新

    本篇文章给大家带来的内容是关于微信小程序功能实现:上滑加载下拉刷新,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 之前谈到文章列表的数据加载,是一次性全部加载,这样是不友好的.这章介 ...

  3. listview上拉加载上一页 下拉加载下一页共通处理

    先什么都不说了,上效果图: 第一页默认显示: 上拉加载下一页: 拉至一定高度: 松开 加载中: 下拉加载上一页: 下拉至一定高度: 松开 加载中: 代码已经上传:http://download.csd ...

  4. ionic上拉加载-下拉刷新

    ionic上拉加载-下拉刷新 1.上拉加载 <ion-infinite-scroll on-infinite="loadOlderStories()" distance=&q ...

  5. Mint-ui中loadmore(上拉加载下拉刷新)组件在ios中滑动会触发点击事件的解决方法...

    bug说明: Mint-ui中loadmore(上拉加载下拉刷新)组件 在 使用fastclick的情况下 ,在ios设备中滑动会触发点击事件: 解决方法: 我是按需引入,去项目中找到loadmore ...

  6. php mescroll,mescroll.js上拉加载下拉刷新组件使用详解

    本文实例为大家分享了上拉加载下拉刷新组件mescroll.js的具体代码,供大家参考,具体内容如下 使用注意事项: 1.引入的时候出问题及时看官方给出的解决方案(基本上都必须看): 2.react中一 ...

  7. ios 上拉加载 下拉刷新

    在一款 App应用中有的时候会用到上拉加载下拉刷新的功能,本人觉得SVPullToRefresh很好用(可以用在UIScrollView上,包括UITableview和UICollectionView ...

  8. html下拉加载原理,GitHub - gavinjzx/wxPull: 原生JS实现微信公众号或网页使用下拉加载和上拉刷新...

    原生JS实现微信公众号或网页使用下拉加载和上拉刷新 微信浏览器打开网页显示网址安全信息解决办法,网上很多办法,也找了很久,但是最新的很多用不了. 先看看效果,是不是亲想要的,可以跳过,以免浪费宝贵时间 ...

  9. html下拉加载实现原理,GitHub - sybiele/wxPull: 原生JS实现微信公众号或网页使用下拉加载和上拉刷新...

    原生JS实现微信公众号或网页使用下拉加载和上拉刷新 微信浏览器打开网页显示网址安全信息解决办法,网上很多办法,也找了很久,但是最新的很多用不了. 先看看效果,是不是亲想要的,可以跳过,以免浪费宝贵时间 ...

  10. android圆形点击效果,Android 三种方式实现自定义圆形页面加载中效果的进度条

    [实例简介] Android 三种方式实现自定义圆形页面加载中效果的进度条 [实例截图] [核心代码] ad376a86-a9aa-49bc-8cea-321bcff2c0c3 └── AnimRou ...

最新文章

  1. 【汇总】细数VSCode中那些能够真正意义提升开发效率、鲜为人知的快捷键
  2. 2021全国高校计算机能力挑战赛(决赛)Java
  3. 【Objective-C学习笔记】变量和基本的数据类型
  4. Open Train 10394
  5. 【JavaScript基础】js中关于声明提前的几个误区
  6. linux 网卡绑定updelay,Linux 配置双网卡绑定实现负载均衡
  7. NetBeans 时事通讯(刊号 # 60 - Jun 21, 2009)
  8. WeihanLi.Npoi 1.21.0 Released
  9. 学习网页栅格系统的几篇好文
  10. 悬浮窗java_Java制作一个简易的悬浮窗/PPT悬浮窗
  11. kali linux2.0下MariaDB修改密码
  12. C/C++面试题—序列化二叉树
  13. 自考那些事儿(九):再次学操作系统
  14. [ 物联网篇 ] ESP32 开发板测试亚马逊语音助手Alexa
  15. ICG博弈_威佐夫博弈(Wythoff Game)及证明
  16. matlab 画图 方程,matlab 画图与解方程
  17. SRAM和DRAM详解
  18. 2008年java占有率_2008年中国城市GDP排名 目前最精确的数据(国家统计局排名):...
  19. 程序员因接外包坐牢!两万字长文揭露心酸真实经历
  20. 我是如何一步步获取房东的WiFi后台管理密码的【社工思路】

热门文章

  1. 1946计算机用途,计算机在我们的工作、生活中的作用越来越大, 你知道计算机的起源于发展吗?请就计算机的发明时间(1946年)、大小、用途等...
  2. python爬虫脚本 初级入门爬虫英雄联盟所有皮肤_Python爬虫实战,60行代码爬取英雄联盟全英雄全皮肤,找寻曾今那些被删除的绝版皮肤...
  3. 2345手机助手 v1.2 官方版
  4. 时差,不同国家之间的大概时差
  5. 虚拟服务器修改教程,【新挑战】十二职业虚拟机一键端图文架设修改教程
  6. 小米路由器,设置自定义Samba路径,直接访问磁盘根目录
  7. 中国地质大学英语语音学习笔记(三):音节与单词变形(ed,es,ing,est,er,派生等)导致的音节数和读音变化
  8. 苹果手机注册时显示链接服务器出现问题,苹果手机出现连接到服务器时出现问题是什么回事...
  9. jauery ajax本地用法,jquery ajax基本用法
  10. QQ上接收的文件资料在哪里能打印?