BottomSheet滑动卡顿:如何根据slideOffset修改View内边距不影响滑动?
我之前也碰到过一模一样的情况——在BottomSheetBehavior的onSlide回调里频繁修改View的padding,直接把BottomSheet的滑动变得慢吞吞的。核心原因其实很直观:每次调用setPadding()都会触发requestLayout(),这个操作会让View重新计算布局、测量尺寸,而onSlide是每帧滑动都会触发的高频回调,短时间内大量的布局计算会占满UI线程,自然就拖慢了滑动的流畅度。
下面是几个亲测有效的解决方法,按推荐程度排序:
方法一:自定义View重写Padding获取方法(最优解)
这种方式完全避免了频繁触发requestLayout(),只需要重写View的padding获取方法,让它根据slideOffset动态计算padding值,每次更新只会触发轻量的重绘(invalidate()),不会影响布局计算。
- 首先自定义一个继承自
NestedScrollView的类:
public class DynamicPaddingScrollView extends NestedScrollView { private float slideOffset = 0f; private final int maxHorizontalPadding = 10; // 你想要的最大padding值 public DynamicPaddingScrollView(Context context) { super(context); } public DynamicPaddingScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public DynamicPaddingScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void updateSlideOffset(float offset) { this.slideOffset = offset; invalidate(); // 仅触发重绘,不触发布局计算 } @Override public int getPaddingLeft() { return (int) (maxHorizontalPadding * slideOffset); } @Override public int getPaddingRight() { return getPaddingLeft(); } }
- 把布局里的
NestedScrollView替换成这个自定义View,然后在onSlide回调里直接更新偏移量:
@Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { dynamicPaddingScrollView.updateSlideOffset(slideOffset); }
这个方法的性能开销最小,几乎不会对BottomSheet的滑动产生任何影响。
方法二:仅在Padding确实变化时调用setPadding
如果不想自定义View,也可以通过减少setPadding()的调用次数来优化——只有当目标padding和当前padding不一致时,才调用方法,避免不必要的布局计算:
@Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { int targetPadding = (int) (10 * slideOffset); // 检查当前padding是否和目标值一致,不一致才更新 if (nestedScrollView.getPaddingLeft() != targetPadding) { nestedScrollView.setPadding(targetPadding, 0, targetPadding, 0); } }
这个方法能减少一部分无效调用,但如果slideOffset每帧都有细微变化(比如从0.1到0.11),还是会触发布局计算,优化效果不如方法一,但实现起来最简单。
方法三:用Translation模拟Padding效果(适合特定场景)
如果你的需求只是让内容看起来有左右缩进的效果,而不是真正需要修改View的padding(比如不需要影响View的点击区域、布局尺寸),可以用translationX来模拟:
把NestedScrollView放在一个FrameLayout容器里,然后在onSlide里调整它的平移量:
@Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { float translation = 10 * slideOffset; // 左右同时缩进的效果:让View向右平移translation,同时给内容右侧留出同样的空间 nestedScrollView.setTranslationX(translation); nestedScrollView.setPadding(0, 0, (int)(translation * 2), 0); }
这个方法的性能也很好,因为setTranslationX()只会触发重绘,不会触发布局计算,但效果和真正的padding有差异,需要根据你的实际需求选择。
内容的提问来源于stack exchange,提问作者user8395760

