Android:CoordinatorLayout中RecyclerView滚动与高度限制异常问题
解决BottomSheet中多RecyclerView的高度控制与滚动异常问题
1. 修复LinearLayoutWithVariableHeight刷新后高度为0的问题
问题原因:RecyclerView数据刷新后,父布局未触发重测量,或onMeasure逻辑未正确计算子View实际高度。
解决方案:
- 在自定义LinearLayout中,确保
onMeasure正确计算所有子View的高度; - 监听RecyclerView的Adapter数据变化,主动触发父布局重测量。
代码示例:
public class LinearLayoutWithVariableHeight extends LinearLayout { private int maxHeight; private int minHeight; private RecyclerView mBoundRecyclerView; public LinearLayoutWithVariableHeight(Context context) { super(context); init(); } // 省略其他构造方法 private void init() { // 初始化max/min高度逻辑 } @Override protected void onFinishInflate() { super.onFinishInflate(); // 遍历子View找到RecyclerView并监听数据变化 for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (child instanceof RecyclerView) { mBoundRecyclerView = (RecyclerView) child; mBoundRecyclerView.getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { requestLayout(); // 数据变化时触发重测量 } }); break; } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int measuredHeight = getMeasuredHeight(); // 限制高度在min和max区间内 int finalHeight = Math.max(minHeight, Math.min(measuredHeight, maxHeight)); setMeasuredDimension(getMeasuredWidth(), finalHeight); } }
2. 修复RecyclerViewWithMaxHeight无法滚动的问题
问题原因:自定义RecyclerView时,onMeasure错误设置了固定高度测量模式,未保留可滚动的AT_MOST模式。
解决方案:在onMeasure中,将高度测量模式设为AT_MOST,限制最大高度的同时允许内容超出时滚动。
代码示例:
public class RecyclerViewWithMaxHeight extends RecyclerView { private int maxHeight; public RecyclerViewWithMaxHeight(Context context) { super(context); } // 省略其他构造方法 public void setMaxHeight(int maxHeight) { this.maxHeight = maxHeight; requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (maxHeight > 0) { // 设置高度测量模式为AT_MOST,最大高度限制为maxHeight heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
3. 修复BottomSheet中RecyclerView触摸事件异常(仅边界可滚动)
问题原因:BottomSheetBehavior默认仅识别第一个可滚动子View,后续RecyclerView的触摸事件被拦截。
解决方案:自定义Behavior时,重写canScrollVertically/canScrollHorizontally方法,判断当前触摸的View是否为可滚动RecyclerView,返回对应滚动状态。
4. 修复自定义Behavior后RecyclerView滚到顶部且无惯性的问题
问题原因:自定义Behavior错误拦截了触摸事件,未传递惯性滚动的MotionEvent,或重置了RecyclerView的滚动状态。
解决方案:
- 在
onInterceptTouchEvent中,仅当触摸对象不是RecyclerView时才拦截事件; - 重写
onStartNestedScroll和onNestedScroll方法,不干扰RecyclerView自身的滚动逻辑。
代码示例:
public class MultiScrollRecyclerViewBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> { private RecyclerView mCurrentTouchRecyclerView; public MultiScrollRecyclerViewBottomSheetBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent event) { // 定位当前触摸点下的RecyclerView mCurrentTouchRecyclerView = findRecyclerViewUnder(child, event.getX(), event.getY()); // 触摸RecyclerView时不拦截事件,让其自行处理 if (mCurrentTouchRecyclerView != null) { return false; } return super.onInterceptTouchEvent(parent, child, event); } @Override public boolean canScrollVertically(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int direction) { // 允许纵向RecyclerView滚动 if (target instanceof RecyclerView && ((RecyclerView) target).getLayoutManager().canScrollVertically()) { return true; } return super.canScrollVertically(coordinatorLayout, child, target, direction); } @Override public boolean canScrollHorizontally(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int direction) { // 允许横向RecyclerView滚动 if (target instanceof RecyclerView && ((RecyclerView) target).getLayoutManager().canScrollHorizontally()) { return true; } return super.canScrollHorizontally(coordinatorLayout, child, target, direction); } // 递归查找触摸点下的RecyclerView private RecyclerView findRecyclerViewUnder(View root, float x, float y) { if (root instanceof RecyclerView) { Rect rect = new Rect(); root.getHitRect(rect); if (rect.contains((int) x, (int) y)) { return (RecyclerView) root; } } else if (root instanceof ViewGroup) { ViewGroup group = (ViewGroup) root; for (int i = 0; i < group.getChildCount(); i++) { RecyclerView recyclerView = findRecyclerViewUnder(group.getChildAt(i), x - group.getScrollX(), y - group.getScrollY()); if (recyclerView != null) { return recyclerView; } } } return null; } }
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

