CoordinatorLayout|CoordinatorLayout + AppBarLayout + RecyclerView,滚动到最底部延迟的坑
随着开发功能越来越复杂,我们使用的控件也越来越多。今天给大家带来一个找了好久解决掉的坑。
目前我们的APP首页使用了联动控件,CoordinatorLayout + AppBarLayout + RecyclerView。这个组合相信用过的都知道,坑其实挺多的,有造成屏幕抖动的,有造成部分白屏的,还有就是我解决的这个,快速滚动到底部,延迟很久才会回调RecyclerView的onScrollStateChanged。可能还有别的坑,目前就这几个。
先说一下原因吧,具体不知道是design的哪个版本升级,里面引入了惯性下滑的概念,滑动的速度越快,下滑的时间越长,就会让我们的界面实际上已经滑动到最底部了,但是还是卡在了下滑的过程之中,等惯性下滑的触发结束以后,才会触发RecyclerView的onScrollStateChanged。就会导致我们的RecyclerView明明已经滑动到最底部,需要去加载下一页数据,却一直卡着不动。
解决方法:其实明白了原因,网上关键字一搜解决方法还是很多的。主要是当碰到这个问题的时候,一直没有准确定位到原因。
分享一下我的调查和处理吧
1.我先是认为是应用的部分UI相关的处理比较复杂导致了应用的卡顿,然后将所有RecyclerView对于Ui相关的处理全部注释掉,发现没有任何改善。
2.分析是因为接口请求等太多太复杂,但是由于接口全部都是异步的,测试的时候心里预期80%不是这个引起的,注释完代码然后测试,结果正合我的预期。
3.由于RecyclerView中也实现了很多onScrollStateChanged 和onScroll 方法的重写,当时是怀疑自己写的onScroll 或者onScrollStateChanged方法内部处理导致了super的onScroll 和onScrollStateChanged 被拦截。同样注释代码测试,仍然不是。
4.最后看了一下所有代码都快注释完了,然后重新分析是不是布局导致的。然后看了这个联动控件,猜想可能是AppBarLayout 的滚动事件和RecyclerView的滚动事件相互冲突了。然后我把CoordinatorLayout注释了,运行,发现RecyclerView的回调正常了。然后有了上述问题原因的分析。最后成功解决了问题。
下面附上解决方法。自定义一个behavior类继承系统的AppBarLayout.Behavior ,加速结束惯性下滑的回调,当滑动到顶,或者滑动到底就认为滑动结束。下面附上代码。
public class ScrollAppBarLayoutBehavior extends AppBarLayout.Behavior {public ScrollAppBarLayoutBehavior() {
super();
}public ScrollAppBarLayoutBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dx, int dy, int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
stopNestedScrollIfNeeded(dy, child, target, type);
}@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
stopNestedScrollIfNeeded(dyUnconsumed, child, target, type);
}private void stopNestedScrollIfNeeded(int dy, AppBarLayout child, View target, int type) {
if (type == ViewCompat.TYPE_NON_TOUCH) {
final int currOffset = getTopAndBottomOffset();
if ((dy < 0 && currOffset == 0) || (dy > 0 && currOffset == -child.getTotalScrollRange())) {
ViewCompat.stopNestedScroll(target, ViewCompat.TYPE_NON_TOUCH);
}
}
}
}
重写的类写好以后,在xml里面AppBarLayout控件中使用我们自定义的ScrollAppBarLayoutBehavior即可。
———————————————重要分割线———————————————————
补充一下.看到很多留言说,关于数据不够多或者空白的时候,上滑的时候,会导致大片空白问题.以及可能会滑动不回来的问题.
【CoordinatorLayout|CoordinatorLayout + AppBarLayout + RecyclerView,滚动到最底部延迟的坑】之前我这边没有复现,一直也比较忙,没时间帮忙看.最近在用的时候,正好也碰到了.把我这边的解决方案和大家共享一下.
一个个说.如果是空白界面,一般我们会使用统一的空白界面.由于显示着空白界面隐藏了recycleview,空白页面没有实现滑动功能,所以在整个头部滑动到最顶上的时候,很可能就滑不下来了.我这边做了最简单的处理.
直接增加了一个是否允许滑动的开关.
public void setEnable(boolean enable) {
if (appbar == null) {
return;
}
View mAppBarChildAt = appbar.getChildAt(0);
AppBarLayout.LayoutParams mAppBarParams = (AppBarLayout.LayoutParams) mAppBarChildAt.getLayoutParams();
if (enable) {
mAppBarParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
| AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
} else {
mAppBarParams.setScrollFlags(0);
}
mAppBarChildAt.setLayoutParams(mAppBarParams);
}
此时可能会出现一个bug,这个appbar底部会有透明框.在xml 设置app:elevation="0dp" 就可以解决了.
同理,如果数据不够多的时候,可以理解为顶部折叠控件完全折叠,当然就只剩下底部的recycleview了.这个控件就是把顶部当成一个折叠控件.那么最好的解决方案,当然也是数据量少的时候,直接禁止上滑就好了.禁止滑动的逻辑就在上面.
推荐阅读
- 一文彻底搞懂|一文彻底搞懂 Design 设计的 CoordinatorLayout 和 AppbarLayout 联动,让 Design 设计更简单~
- CoordinatorLayout+RecyclerView使用遇到的问题
- CoordinatorLayout问题汇总
- 3.1.3_CoordinatorLayout原理
- 理解RecyclerView(七)—RecyclerView配合使用CoordinatorLayout及Behavior的嵌套滑动机制
- 《Android进阶之光》笔记|《Android进阶之光》Design Support Library常用控件(二)(CoordinatorLayout)
- UI|嵌套滑动--NestedScroll-项目实例(淘宝首页缺陷),及CoordinatorLayout 和 AppbarLayout 联动原理
- CoordinatorLayout实现自定义头条卡住的解决方案
- 初探CoordinatorLayout+AppBarLayout
- Design之CoordinatorLayout+TabLayout+RecyclerView&CollapsingToolbarLayout