【Android 教你打造独一无二的刷新加载框架】幽沉谢世事,俯默窥唐虞。这篇文章主要讲述Android 教你打造独一无二的刷新加载框架相关的知识,希望能为你提供帮助。
其实早在去年七月,
群里小伙伴就有让我共享这个。但我当时绝的技术不纯熟。代码有bug什么的。没有写出来。现在感觉整理的差不多了。就写出来让大家看看,
有问题一起讨论解决。
说到刷新加载,
我们第一个想到啥,
对了就是swiperefreshlayout,
还有什么SuperSwiperefreshlayout,
XRecyclerView等等。反正老多了,
我还是之前那句话,
不管用什么,
我们需要知道他的原理。
打造框架开始 对于刷新加载的实现,
你们第一个想到的是什么?
是用swiperefresh然后在recyclerview底部加一个不同type?
还是用事件拦截呢?
那必须是事件拦截啊,
虽然现在swiperefreshlayout很火,
基本很多app都能看到他。但是遇到那种坑比公司说刷新要用自己公司logo你也没辙。对把。。好了,
感觉得罪了好多公司,
不管他,
我们继续。
如果有小伙伴长期看我博客,
应该知道我前面有一篇是写过事件拦截的。没错,
就是 从源码角度分析嵌套滑动机制NestedScrolling
对于nestedscrolling不了解的同学可以看完在继续下文。
我们先看下我们的效果图:

文章图片
老铁, 没毛病。下面我介绍如何实现的。
下拉刷新 首先我们给出如下几个参数, 后面要用:
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());
//传递滚动到倒数第几行
//}
//}
//}
}
这样我们变实现了自己的刷新加载框架, 代码我已上传到github: https://github.com/sw950729/SWPullRecyclerLayout
至于使用方法如下:
jcenter:
compile '
com.angel:SWPullRecyclerLayout:1.0.0'
maven:
<
dependency>
<
groupId>
com.angel<
/groupId>
<
artifactId>
SWPullRecyclerLayout<
/artifactId>
<
version>
1.0.0<
/version>
<
type>
pom<
/type>
<
/dependency>
明天正式上班, 上班之前把这篇干货分享给大家。依旧是那2句话。不管用什么我们需要知道原理。还有就是有什么不懂的提出来。可以一起讨论。
推荐阅读
- Android图片加载框架最全解析,Glide的基本用法
- 使用Tkinter创建多重选择
- 算法(创建一个具有左-子-右-兄弟表示的树)
- 创建WYSIWYG文档编辑器|自然语言编程
- 用Python创建你的第一个应用程序
- 一个创意的C++程序,用于缩放整数
- 处理中的创意编程|S2(洛伦茨吸引子)
- 处理中的创意编程|S1(Random Walker)
- 瑞士信贷面试经历|实习校园(浦那)