android教你打造独一无二的上拉下拉刷新加载框架
其实早在去年七月,群里小伙伴就有让我共享这个。但我当时绝的技术不纯熟。代码有bug什么的。没有写出来。现在感觉整理的差不多了。就写出来让大家看看,有问题一起讨论解决。
说到刷新加载,我们第一个想到啥,对了就是swiperefreshlayout,还有什么superswiperefreshlayout,xrecyclerview等等。反正老多了,我还是之前那句话,不管用什么,我们需要知道他的原理。
打造框架开始
对于刷新加载的实现,你们第一个想到的是什么?是用swiperefresh然后在recyclerview底部加一个不同type?还是用事件拦截呢?那必须是事件拦截啊,虽然现在swiperefreshlayout很火,基本很多app都能看到他。但是遇到那种坑比公司说刷新要用自己公司logo你也没辙。对把。。好了,感觉得罪了好多公司,不管他,我们继续。
我们先看下我们的效果图:
老铁,没毛病。下面我介绍如何实现的。
下拉刷新
首先我们给出如下几个参数,后面要用:
private nestedscrollingparenthelper helper = null; private boolean isrefresh = true; private boolean isload = true; //滑动的总距离 private int totaly = 0; private linearlayout headerlayout = null; private myrecyclerview myrecyclerview = null; private linearlayout footerlayout = null;
既然是刷新,我们的滚动肯定是在父view之前的。所以我们需要在onnestedprescroll这个方法里面写上我们所需要改动的x,y值。
我们需要用父view去拦截它。
我们需要判断dy的值是否大于0,因为大于0是刷新操作,小于0是加载操作。然后我们需要判断recyclerview是否是纵向的而不是横向的。
public void onnestedprescroll(view target, int dx, int dy, int[] consumed) { if (isrefresh) { if (dy > 0) { if (myrecyclerview.isorientation(0)) { totaly += dy; if ((totaly / 2) <= 0) { scrollto(0, totaly / 2); consumed[1] = dy; } else { scrollto(0, 0); consumed[1] = 0; } } return; } }
上拉加载
上面我也说了onnestedprescroll这个方法中判断dy<0才是加载操作。所以综上所述,代码变成了这样:
public void onnestedprescroll(view target, int dx, int dy, int[] consumed) { if (totaly < 0 && myrecyclerview.isorientation(0) || totaly > 0 && myrecyclerview.isorientation(1)) { isfling = true; } if (isrefresh) { if (dy > 0) { if (myrecyclerview.isorientation(0)) { totaly += dy; if ((totaly / 2) <= 0) { scrollto(0, totaly / 2); consumed[1] = dy; } else { scrollto(0, 0); consumed[1] = 0; } } return; } } if (isload) { if (dy < 0) { if (myrecyclerview.isorientation(1)) { totaly += dy; if ((totaly / 2) >= 0) { scrollto(0, totaly / 2); consumed[1] = dy; } else { scrollto(0, 0); consumed[1] = 0; } } return; } } }
最后我们需要在子view滑动结束后,实行如下操作:
//子view滑动结束调用 //dyunconsumed < 0 向下滚 //dyunconsumed > 0 向上滚 public void onnestedscroll(view target, int dxconsumed, int dyconsumed, int dxunconsumed, int dyunconsumed) { if (dyunconsumed != 0) { totaly += dyunconsumed; scrollto(0, totaly / 2); } }
其实最主要的两个方法已经解决了,其他到没什么了,这边,我把nestedscrolling的8个接口的功能和自定义recyclerview放出来。已变大家参考。希望大家都能实现自己的刷新加载。告别swiperefreshlayout。
添加header和footer
这里我们参考listview自带的addheaderview和addfooterview。代码如下:
public void addheaderview(view headerview, int headerheight) { this.headerlayout.removeallviews(); this.headerlayout.addview(headerview); layoutparams layoutparams = new layoutparams(layoutparams.match_parent, headerheight); layoutparams.topmargin = -headerheight; this.headerlayout.setlayoutparams(layoutparams); } public void addfooterview(view footerview, int footerheight) { this.footerlayout.removeallviews(); this.footerlayout.addview(footerview); this.footerlayout.setlayoutparams(new layoutparams(layoutparams.match_parent, footerheight)); }
几个接口的实现
public boolean onstartnestedscroll(view child, view target, int nestedscrollaxes) { return true; } public void onnestedscrollaccepted(view child, view target, int axes) { helper.onnestedscrollaccepted(child, target, axes); } //父view拦截子view的滚动 public void onnestedprescroll(view target, int dx, int dy, int[] consumed) { if (totaly < 0 && myrecyclerview.isorientation(0) || totaly > 0 && myrecyclerview.isorientation(1)) { isfling = true; } if (isrefresh) { if (dy > 0) { if (myrecyclerview.isorientation(0)) { totaly += dy; if ((totaly / 2) <= 0) { scrollto(0, totaly / 2); consumed[1] = dy; } else { scrollto(0, 0); consumed[1] = 0; } } return; } } if (isload) { if (dy < 0) { if (myrecyclerview.isorientation(1)) { totaly += dy; if ((totaly / 2) >= 0) { scrollto(0, totaly / 2); consumed[1] = dy; } else { scrollto(0, 0); consumed[1] = 0; } } return; } } } //子view滑动结束调用 //dyunconsumed < 0 向下滚 //dyunconsumed > 0 向上滚 public void onnestedscroll(view target, int dxconsumed, int dyconsumed, int dxunconsumed, int dyunconsumed) { if (dyunconsumed != 0) { totaly += dyunconsumed; scrollto(0, totaly / 2); } } public void onstopnestedscroll(view child) { helper.onstopnestedscroll(child); if (ontouchuplistener != null) { isfling = false; ontouchuplistener.touchup(); } } public boolean onnestedfling(view target, float velocityx, float velocityy, boolean consumed) { return isfling; } public boolean onnestedprefling(view target, float velocityx, float velocityy) { return isfling; } public int getnestedscrollaxes() { return helper.getnestedscrollaxes(); }
自定义recyclerview
既然是自己写的刷新加载框架,总不能还有自定义layout中在放个recyclerview。多麻烦,自定义一个,直接放在里面,然后分别放个header和footer 就没必要每次有页面用到刷新都要写一个布局。3个布局解决整个项目的刷新和加载。话不多说,代码如下:
private class myrecyclerview extends recyclerview { private staggeredgridlayoutmanager staggeredgridlayoutmanager = null; private linearlayoutmanager linearlayoutmanager = null; private gridlayoutmanager gridlayoutmanager = null; private boolean isscrollload = false; private boolean isscrollrefresh = false; public myrecyclerview(context context) { super(context); setverticalfadingedgeenabled(false); sethorizontalfadingedgeenabled(false); setverticalscrollbarenabled(false); sethorizontalscrollbarenabled(false); setoverscrollmode(over_scroll_never); setitemanimator(new defaultitemanimator()); } private void setmylayoutmanager(layoutmanager layoutmanager) { if (layoutmanager instanceof staggeredgridlayoutmanager) { staggeredgridlayoutmanager = (staggeredgridlayoutmanager) layoutmanager; } else if (layoutmanager instanceof gridlayoutmanager) { gridlayoutmanager = (gridlayoutmanager) layoutmanager; } else if (layoutmanager instanceof linearlayoutmanager) { linearlayoutmanager = (linearlayoutmanager) layoutmanager; } setlayoutmanager(layoutmanager); if (!isvertical()) { throw new nullpointerexception("vertical!"); } } private boolean isorientation(int orientation) {//orientation,0代表向下,1代表向上 if (orientation == 0) return iscanpulldown(); else if (orientation == 1) return iscanpullup(); return false; } private boolean iscanpulldown() { return !canscrollvertically(-1); } private boolean iscanpullup() { return !canscrollvertically(1); } // private int scrollload() { // int lastitem = 0; // int itemcount = 0; // int spancount = 1; // if (staggeredgridlayoutmanager != null) { // lastitem = staggeredgridlayoutmanager.findlastvisibleitempositions(null)[0]; // itemcount = staggeredgridlayoutmanager.getitemcount(); // spancount = staggeredgridlayoutmanager.getspancount(); // } else if (linearlayoutmanager != null) { // lastitem = linearlayoutmanager.findlastvisibleitemposition(); // itemcount = linearlayoutmanager.getitemcount(); // spancount = 1; // } else if (gridlayoutmanager != null) { // lastitem = gridlayoutmanager.findlastvisibleitemposition(); // itemcount = gridlayoutmanager.getitemcount(); // spancount = gridlayoutmanager.getspancount(); // } // return ((itemcount - 1) / spancount + 1) - (lastitem / spancount + 1); // } private boolean isvertical() { if (staggeredgridlayoutmanager != null) return staggeredgridlayoutmanager.getorientation() == staggeredgridlayoutmanager.vertical; else if (linearlayoutmanager != null) return linearlayoutmanager.getorientation() == linearlayoutmanager.vertical; else if (gridlayoutmanager != null) return gridlayoutmanager.getorientation() == gridlayoutmanager.vertical; return false; } // public void onscrolled(int dx, int dy) { // if (dy > 0 && !isscrollload) { // if (olior != null) { // onscrolllistener.scrollload(sc```````` ` `` llload());//传递滚动到倒数第几行 // } // } // } }
这样我们变实现了自己的刷新加载框架,代码我已上传:swpullrecyclerlayout_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。