之前为了开发需求,学习了NestedScrolling机制,并使用CoordinatorLayout、AppBarLayout、RecyclerView配合实现了相关的效果,还写了一篇关于分析原理的文章关于CoordinatorLayout AppBarLayout原理的一些分析,当时做完需求以后,内心其实是一只有种遗憾的,因为在使用RecyclerView时,对于向上滑动的fling效果其实是有问题的,滑动起来的感觉并不是连贯的,可以看一下这个效果:
这个效果的体验其实并不是太好,因为向下fling时我们希望的效果是父布局可以在RecyclerView到达顶部时,如果没有消耗完fling时可以由父布局消耗从而继续滑动的,可是由于之前的实现方式,并没有办法用常规的办法达到这一点,Google也意识到了这个问题,所以在Android 8.0 扩展了NestedScrolling的相关接口,使得滑动可以变得更加顺畅了,效果如下,简直如丝般顺滑:
在分析相关的原理之前,如果你们急需解决项目中的这种滑动不顺畅,可以先把解决方法告诉大家,那就是把compileSdkVersion升到26然后使用26.1.0的相关控件
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion '26.0.2'
.......
}
dependencies {
....
compile 'com.android.support:recyclerview-v7:26.1.0'
compile 'com.android.support:support-v4:26+'
compile 'com.android.support:design:26.1.0'
.....
}
这样改好之后,不需要改动一行代码,即可完美实现如丝般顺滑的fling。
问题发生的原因
RecyclerView中,fling的调用发生在滑动事件MotionEvent.ACTION_UP时,在25+版本之前RecycleView只是在fling的开始之前通知了Parent是否消耗fling以及将fling分发到parent,这只能做到Parent和RecyclerView同时fling或者Parent自己fling,在RecyclerView的fling过程中并没有通知Parent,在RecyclerView fling结束之后,Parent不能拿到剩余未消耗的距离,所以导致了这个不能连贯滑动的问题,25+版本的RecyclerView的fling方法如下:
public boolean fling(int velocityX, int velocityY) {
if (mLayout == null) {
Log.e(TAG, "Cannot fling without a LayoutManager set. " +
"Call setLayoutManager with a non-null argument.");
return false;
}
if (mLayoutFrozen) {
return false;
}
final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
final boolean canScrollVertical = mLayout.canScrollVertically();
if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
velocityX = 0;
}
if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
velocityY = 0;
}
if (velocityX == 0 && velocityY == 0) {
// If we don't have any velocity, return false
return false;
}
if (!dispatchNestedPreFling(velocityX, velocityY)) {
final boolean canScroll = canScrollHorizontal || canScrollVertical;
dispatchNestedFling(velocityX, velocityY, canScroll);
if (mOnFlingListener !=