Android实现RecyclerView下拉刷新效果
程序员文章站
2022-04-13 22:49:59
本文为大家分享了android实现recyclerview下拉刷新效果的具体代码,供大家参考,具体内容如下
思路
realpullrefreshview继承了一...
本文为大家分享了android实现recyclerview下拉刷新效果的具体代码,供大家参考,具体内容如下
思路
- realpullrefreshview继承了一个linearlayout
- 里面放置了一个刷新头布局,将其margin_top设置为负的刷新头的高度的
- 再添加一个recyclerview
- 触摸事件分发机制,当在特定条件下让realpullrefreshview拦截触摸事件,否则的话,不拦截,让recyclerview自己去处理触摸事件
- 在手指下拉时,定义好不同的状态state,在不同状态下,处理不同的显示,这里讲不同状态下的刷新头如何显示,抽象为一个接口,用户可以实现这个接口,自定义刷新头的布局和动画
- 加载更多的功能是利用recyclerview的多type布局实现的
- 难点在于触摸事件的拦截,和认真处理各种滑动的问题
使用
xml
<com.example.apple.quickdemo.realview.view.realpullrefreshview android:id="@+id/real_pull_refresh_view" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:refresh_header_view="@layout/headerview"/>
这是headerview
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ff0" android:orientation="horizontal" android:gravity="center"> <textview android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@color/coloraccent" android:visibility="visible" android:id="@+id/tv" android:gravity="center" android:text="下拉刷新" android:textsize="21sp"/> <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" android:id="@+id/iv" /> </linearlayout>
代码
mrealpullrefreshview.setlayoutmanager(mlayoutmanager); mrealpullrefreshview.setadapter(mmyadapte); //用户可以自定义自己的刷新头布局和动画 //mrealpullrefreshview.setonpullshowviewlistener(new gifonpullshowviewlisterner(mrealpullrefreshview)); mrealpullrefreshview.setonpulllistener(new realpullrefreshview.onpulllistener() { @override public void onrefresh() { mhandler.postdelayed(new runnable() { @override public void run() { mbodies.add(0, new body("新数据"+i++,100)); mrealpullrefreshview.refreshfinish(); } }, 3000); } @override public void onloadmore() { final list<body> more=new arraylist<body>(); mhandler.postdelayed(new runnable() { @override public void run() { for (int i = 0; i < 3; i++) { more.add(new body("more+++"+i,100)); } mbodies.addall(more); mrealpullrefreshview.loadmrefinish(); } }, 1500); } });
自定义刷新头布局和动画
package com.example.apple.quickdemo.realview.show; import android.animation.objectanimator; import android.animation.valueanimator; import android.util.log; import android.view.view; import android.widget.imageview; import android.widget.textview; import com.example.apple.quickdemo.r; import com.example.apple.quickdemo.realview.view.realpullrefreshview; /** * created by apple on 2017/7/9. */ public class implonpullshowviewlistener implements realpullrefreshview.onpullshowviewlistener { private textview mtv; private imageview miv; private objectanimator mani; view mheaderview; public implonpullshowviewlistener(realpullrefreshview realpullrefreshview) { mheaderview = realpullrefreshview.getrefreshheaderview(); mtv = (textview) mheaderview.findviewbyid(r.id.tv); miv = (imageview) mheaderview.findviewbyid(r.id.iv); mani = objectanimator.offloat(miv, "rotation", -15, 15).setduration(300); mani.setrepeatcount(valueanimator.infinite); mani.setrepeatmode(valueanimator.reverse); } @override public void onpulldownrefreshstate(int scrolly, int headviewheight,int deltay) { mtv.settext("下拉刷新"); float f = -((float) scrolly / (float) headviewheight); log.e("tag", f+ ""); log.e("tag", -scrolly + "scrolly"); miv.setscalex(f); miv.setscaley(f); } @override public void onreleaserefreshstate(int scrolly, int deltay) { mtv.settext("松手刷新"); } @override public void onrefreshingstate() { mtv.settext("正在刷新"); miv.setscalex(1.0f); miv.setscaley(1.0f); mani.start(); } @override public void ondefaultstate() { if (mani.isrunning()){ mani.end(); miv.setrotation(0); } } }
源码
package com.example.apple.quickdemo.realview.view; import android.content.context; import android.content.res.typedarray; import android.support.annotation.nullable; import android.support.v7.widget.gridlayoutmanager; import android.support.v7.widget.linearlayoutmanager; import android.support.v7.widget.recyclerview; import android.support.v7.widget.staggeredgridlayoutmanager; import android.util.attributeset; import android.util.log; import android.view.layoutinflater; import android.view.motionevent; import android.view.velocitytracker; import android.view.view; import android.view.viewgroup; import android.widget.linearlayout; import android.widget.scroller; import android.widget.toast; import com.example.apple.quickdemo.r; import com.example.apple.quickdemo.realview.show.implonpullshowviewlistener; import static android.content.contentvalues.tag; /** * created by apple on 2017/7/7. * 下拉刷新 */ public class realpullrefreshview extends linearlayout { private int mtouchslop; // 分别记录上次滑动的坐标 private int mlastx = 0; private int mlasty = 0; // 分别记录上次滑动的坐标(onintercepttouchenvent) private int mlastxintercept = 0; private int mlastyintercept = 0; private scroller mscroller; private velocitytracker mvelocitytracker; private recyclerview.adapter madapter; public recyclerview getrecyclerview() { return mrecyclerview; } private recyclerview mrecyclerview; private int default = 0; private final int pull_down_refresh = 1; private final int release_refresh = 2; private final int refreshing = 3; private final int load_more = 4; private int state = default; private int rfreshheaderwidth; private int refreshheadviewheight; private onpulllistener monpulllistener; private view mrefreshheaderview; private recyclerview.layoutmanager mlayoutmanager; int refreshheadviewid; public void setlayoutmanager(recyclerview.layoutmanager manager) { this.mlayoutmanager = manager; mrecyclerview.setlayoutmanager(mlayoutmanager); } public void setadapter(recyclerview.adapter adapter) { this.madapter = adapter; mrecyclerview.setadapter(madapter); } public view getrefreshheaderview() { return mrefreshheaderview; } public void setonpullshowviewlistener(onpullshowviewlistener onpullshowviewlistener) { monpullshowviewlistener = onpullshowviewlistener; } private onpullshowviewlistener monpullshowviewlistener; public void setonpulllistener(onpulllistener onpulllistener) { monpulllistener = onpulllistener; } public realpullrefreshview(context context) { super(context); initview(context); } public realpullrefreshview(context context, @nullable attributeset attrs) { super(context, attrs); initattrs(context, attrs); // ★★★★★一个坑initattrs方法里的typedarray去获取属性时,第一次获取的属性全是0,他会马上重走一次构造方法,再次获取一次,才能获得正确的值 // 如果第一次获取的值为0,则不去initview if (refreshheadviewid != 0) { initview(context); } } public realpullrefreshview(context context, @nullable attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); initattrs(context, attrs); if (refreshheadviewid != 0) { initview(context); } } private void initattrs(context context, attributeset attrs) { typedarray typedarray = context.obtainstyledattributes(attrs, r.styleable.realpullrefreshview); try { refreshheadviewid = typedarray.getresourceid(r.styleable.realpullrefreshview_refresh_header_view, 0); } finally { typedarray.recycle(); } } private void initview(context context) { mscroller = new scroller(getcontext()); mvelocitytracker = velocitytracker.obtain(); // 添加headerview // ★ ★ ★ ★ ★ 注意不要用这个方法inflate布局,会导致layout的所有属性失效,height、width、margin // 原因见 http://blog.csdn.net/zhaokaiqiang1992/article/details/36006467 // ★ ★ ★ ★ ★ mrefreshheaderview = minflater.inflate(r.layout.headerview, null); mrefreshheaderview = layoutinflater.from(context).inflate(refreshheadviewid, this, false); addview(mrefreshheaderview); // } // 以下代码主要是为了设置头布局的margintop值为-headerviewheight // 注意必须等到一小会才会得到正确的头布局宽高 postdelayed(new runnable() { @override public void run() { log.e("q11", refreshheadviewheight + "qqqqqqqqqq " + mrefreshheaderview.getheight()); rfreshheaderwidth = mrefreshheaderview.getwidth(); refreshheadviewheight = mrefreshheaderview.getheight(); marginlayoutparams lp = new linearlayout.layoutparams(rfreshheaderwidth, refreshheadviewheight); lp.setmargins(0, -refreshheadviewheight, 0, 0); mrefreshheaderview.setlayoutparams(lp); } }, 100); // 添加recyclerview mrecyclerview = new recyclerview(context); addview(mrecyclerview, layoutparams.match_parent, viewgroup.layoutparams.match_parent); // 这里我提供了一个默认的显示效果,如果用户不使用mrealpullrefreshview.setonpullshowviewlistener的话,会默认使用这个 // 用户可以实现onpullshowviewlistener接口,去实现自己想要的显示效果 monpullshowviewlistener = new implonpullshowviewlistener(this); setloadmore(); } private void setloadmore() { // 当目前的可见条目是所有数据的最后一个时,开始加载新的数据 mrecyclerview.addonscrolllistener(new recyclerview.onscrolllistener() { @override public void onscrolled(recyclerview recyclerview, int dx, int dy) { super.onscrolled(recyclerview, dx, dy); int lastcompletelyvisibleitemposition = -1; if (mlayoutmanager instanceof linearlayoutmanager) { linearlayoutmanager manager = (linearlayoutmanager) mlayoutmanager; lastcompletelyvisibleitemposition = manager.findlastvisibleitemposition(); } else if (mlayoutmanager instanceof gridlayoutmanager) { gridlayoutmanager manager = (gridlayoutmanager) mlayoutmanager; lastcompletelyvisibleitemposition = manager.findlastvisibleitemposition(); } else if (mlayoutmanager instanceof staggeredgridlayoutmanager) { staggeredgridlayoutmanager manager = (staggeredgridlayoutmanager) mlayoutmanager; lastcompletelyvisibleitemposition = manager.findlastvisibleitempositions(new int[manager.getspancount()])[0]; } if (lastcompletelyvisibleitemposition + 1 == madapter.getitemcount()) { if (monpulllistener != null && state == default) { state = load_more; monpulllistener.onloadmore(); } } } }); } @override public boolean onintercepttouchevent(motionevent event) { boolean intercepted = false; int x = (int) event.getx(); int y = (int) event.gety(); switch (event.getaction()) { case motionevent.action_down: { intercepted = false; /*if (state!=default||state!=refreshing){ if (!mscroller.isfinished()) { mscroller.abortanimation(); }}*/ break; } case motionevent.action_move: { int deltax = x - mlastxintercept; int deltay = y - mlastyintercept; int firstcompletelyvisibleitemposition = -1; if (mlayoutmanager instanceof linearlayoutmanager) { linearlayoutmanager manager = (linearlayoutmanager) mlayoutmanager; firstcompletelyvisibleitemposition = manager.findfirstcompletelyvisibleitemposition(); } else if (mlayoutmanager instanceof gridlayoutmanager) { gridlayoutmanager manager = (gridlayoutmanager) mlayoutmanager; firstcompletelyvisibleitemposition = manager.findfirstcompletelyvisibleitemposition(); } else if (mlayoutmanager instanceof staggeredgridlayoutmanager) { staggeredgridlayoutmanager manager = (staggeredgridlayoutmanager) mlayoutmanager; firstcompletelyvisibleitemposition = manager.findfirstcompletelyvisibleitempositions(new int[manager.getspancount()])[0]; } // ******************这里说明什么规则下,拦截,其余代码不要动了,其余代码指的是处理滑动冲突的代码*************** if (firstcompletelyvisibleitemposition == 0 && deltay > 0 && math.abs(deltay) > math.abs(deltax)) {//拉倒最顶部,继续往下拉,将拉出头布局,要父布局拦截 intercepted = true; } else if (getscrolly() < 0) {//表示头布局已经向下拉出来,头布局已经显示了,要父布局拦截 intercepted = true; } else if (deltay < 0) { intercepted = false;//不要父布局拦截了 } else { intercepted = false;//不要父布局拦截了 } // ******************什么规则下,拦截*************** break; } case motionevent.action_up: { intercepted = false; break; } default: break; } log.d(tag, "intercepted=" + intercepted); mlastx = x; mlasty = y; mlastxintercept = x; mlastyintercept = y; return intercepted; } /** * 下面不同布局,不同的滑动需求 * * @param event * @return */ @override public boolean ontouchevent(motionevent event) { mvelocitytracker.addmovement(event); int x = (int) event.getx(); int y = (int) event.gety(); switch (event.getaction()) { case motionevent.action_down: { if (!mscroller.isfinished()) { mscroller.abortanimation(); } break; } case motionevent.action_move: { int deltax = x - mlastx; int deltay = y - mlasty; if (getscrolly() > 0) { //防止在正在刷新状态下,上拉出空白 } else if (getscrolly() <= 0 && getscrolly() > -refreshheadviewheight * 5) { // 最多下拉到头布局高度5倍的距离 scrollby(0, -deltay / 2); } if (getscrolly() > -refreshheadviewheight && state != refreshing) {//头布局显示不全时,为下拉刷新pull_down_refresh状态 state = pull_down_refresh; if (monpullshowviewlistener != null) { monpullshowviewlistener.onpulldownrefreshstate(getscrolly(), refreshheadviewheight, deltay); } } if (getscrolly() < -refreshheadviewheight && state != refreshing) {//头布局完全显示时,为释放刷新release_refresh状态 state = release_refresh; if (monpullshowviewlistener != null) { monpullshowviewlistener.onreleaserefreshstate(getscrolly(), deltay); } } break; } case motionevent.action_up: { final int scrolly = getscrolly(); //松手时,根据所处的状态,让布局滑动到不同的地方,做不同的操作 switch (state) { case pull_down_refresh: state = default; //头布局没有完全显示,完全隐藏头布局 smoothscrollby(0, -scrolly); break; case release_refresh: state = refreshing; smoothscrollby(0, -refreshheadviewheight - scrolly); if (monpullshowviewlistener != null) { monpullshowviewlistener.onrefreshingstate(); } if (monpulllistener != null) { monpulllistener.onrefresh(); } break; case refreshing: if (getscrolly() < -refreshheadviewheight) { smoothscrollby(0, -refreshheadviewheight - scrolly); } else { smoothscrollby(0, -scrolly); } break; } mvelocitytracker.clear(); break; } default: break; } mlastx = x; mlasty = y; return true; } /** * 当用户使用完下拉刷新回调时,需要调用此方法,将头不去隐藏,将state恢复 */ public void refreshfinish() { smoothscrollby(0, 0 - getscrolly()); getrecyclerview().getadapter().notifydatasetchanged(); state = default; if (monpullshowviewlistener != null) { monpullshowviewlistener.ondefaultstate(); } toast.maketext(getcontext(), "刷新成功!", toast.length_short).show(); } /** * 当用户使用完加载更多后回调时,需要调用此方法,将state恢复 */ public void loadmrefinish() { getrecyclerview().getadapter().notifydatasetchanged(); state = default; toast.maketext(getcontext(), "加载成功了!", toast.length_short).show(); } /** * 在500毫秒内平滑地滚动多少像素点 * * @param dx * @param dy */ private void smoothscrollby(int dx, int dy) { mscroller.startscroll(0, getscrolly(), 0, dy, 500); invalidate(); } @override public void computescroll() { if (mscroller.computescrolloffset()) { scrollto(mscroller.getcurrx(), mscroller.getcurry()); postinvalidate(); } } /** * 释放资源 */ @override protected void ondetachedfromwindow() { mvelocitytracker.recycle(); super.ondetachedfromwindow(); } // *************** // ***************** /** * 回调接口 */ public interface onpulllistener { /** * 当下拉刷新正在刷新时,这时候可以去请求数据,记得最后调用refreshfinish()复位 */ void onrefresh(); /** * 当加载更多时 */ void onloadmore(); } /** * 回调接口,可以通过下面的回调,自定义各种状态下的显示效果 * 可以根据下拉距离scrolly设计动画效果 */ public interface onpullshowviewlistener { /** * 当处于下拉刷新时,头布局显示效果 * * @param scrolly 下拉的距离 * @param headviewheight 头布局高度 * @param deltay movey-lastmovey,正值为向下拉 */ void onpulldownrefreshstate(int scrolly, int headviewheight, int deltay); /** * 当处于松手刷新时,头布局显示效果 * * @param scrolly 下拉的距离 * @param deltay movey-lastmovey,正值为向下拉 */ void onreleaserefreshstate(int scrolly, int deltay); /** * 正在刷新时,页面的显示效果 */ void onrefreshingstate(); /** * 默认状态时,页面显示效果,主要是为了复位各种状态 */ void ondefaultstate(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。