NestedScrollView 与 RecyclerView 的嵌套使用问题,在网上看到的都是说给 RecyclerView 设置 setNestedScrollingEnabled(false)。但是这个方法会有一个问题,
那就是要求 RecyclerView 的高度是 wrap_content 的 (要不然高度太小了,底部的 Item View 滑动不出来),即有多少 Item View,就得要有相应的多大高度。但这不是我想要的,一是这会导致 ViewHolder 不能被回收!!!RecyclerView 就失去了 Recycle 的意义!!! 二是在向顶部快速 fling 滑动的时候,整体滑动效果不连贯。 有人还称这个方法很优雅,其实一点也不优雅。。。
上视频看看使用前,使用后分别是什么样的效果:
before
after
(原创作品,转载请声明出处:https://blog.csdn.net/hegan2010/article/details/113103751)
为了解决这个问题,使得 NestedScrollView 与 RecyclerView 可以双滑动,并且不产生滑动冲突,写了一个布局管理器。
原理是,在RecyclerView的可见区域顶部低于 NestedScrollView 可见区域顶部时,减少 RecyclerView 在Y轴上消费掉滑动距离,或者设为0,或者为两者高度距离差,让NestedScrollView 消费掉更多的滑动距离。
实现代码如下(继承自GridLayoutManager,可以根据实际情况修改为继承自哪个LayoutManager,只要重载了 scrollVerticallyBy 方法就行):
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import androidx.core.view.ScrollingView;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.lang.reflect.Field;
public class AutoScrollGridLayoutManager extends GridLayoutManager {
public AutoScrollGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public AutoScrollGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
public AutoScrollGridLayoutManager(Context context, int spanCount, int orientation,
boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
if (dy <= 0) {
return super.scrollVerticallyBy(dy, recycler, state);
}
RecyclerView recyclerView = getRecyclerView();
if (recyclerView == null) {
return super.scrollVerticallyBy(dy, recycler, state);
}
ViewGroup parentView = getParentScrollingView(recyclerView);
if (parentView == null) {
return super.scrollVerticallyBy(dy, recycler, state);
}
Rect parentVisibleRect = new Rect();
parentView.getGlobalVisibleRect(parentVisibleRect);
Rect recyclerVisibleRect = new Rect();
recyclerView.getGlobalVisibleRect(recyclerVisibleRect);
int verticalOffset = recyclerVisibleRect.top - parentVisibleRect.top;
if (verticalOffset <= 0) {
return super.scrollVerticallyBy(dy, recycler, state);
} else {
// verticalOffset > 0
if (dy <= verticalOffset) {
return 0;
} else {
// dy > verticalOffset
return super.scrollVerticallyBy(dy - verticalOffset, recycler, state);
}
}
}
public RecyclerView getRecyclerView() {
try {
Field field = RecyclerView.LayoutManager.class.getDeclaredField("mRecyclerView");
field.setAccessible(true);
return (RecyclerView) field.get(this);
} catch (ReflectiveOperationException e) {
return null;
}
}
public static ViewGroup getParentScrollingView(View view) {
do {
ViewParent parent = view.getParent();
if (parent == null || !(parent instanceof ViewGroup)) {
return null;
}
ViewGroup parentView = (ViewGroup) parent;
if (parentView instanceof ScrollingView) {
return parentView;
}
view = parentView;
} while (true);
}
}