如何控制GridLayoutManager布局下RecyclerView的滚动速度并限制快速滚动加载未加载图片?
嘿,我之前也碰到过GridLayoutManager搭配RecyclerView时滚动过快的问题,结合你的场景(5000+本地图片、担心快速滚动触发大量加载),给你几个亲测有效的解决方案:
GridLayoutManager本身没有直接调整滚动速度的API,但我们可以通过自定义它的Scroller来降低fling(快速滑动后的惯性滚动)速度。创建一个自定义的GridLayoutManager,重写createScroller方法替换成自定义的OverScroller,通过系数降低速度:
public class SlowGridLayoutManager extends GridLayoutManager { // 系数越小,滚动越慢,可根据需求调整(比如0.5f-0.8f) private static final float SCROLL_SPEED_FACTOR = 0.6f; public SlowGridLayoutManager(Context context, int spanCount) { super(context, spanCount); } public SlowGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { super(context, spanCount, orientation, reverseLayout); } @Override public RecyclerView.Scroller createScroller(Context context) { return new SlowScroller(context); } private class SlowScroller extends OverScroller { public SlowScroller(Context context) { super(context); } @Override public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY) { // 给滑动速度乘以阻尼系数,降低惯性滚动的速度 super.fling(startX, startY, (int)(velocityX * SCROLL_SPEED_FACTOR), (int)(velocityY * SCROLL_SPEED_FACTOR), minX, maxX, minY, maxY, overX, overY); } } }
之后把你原来的GridLayoutManager替换成这个SlowGridLayoutManager,就能明显减慢手动快速滑动后的惯性滚动速度。
smoothScrollToPosition的滚动速度 针对smoothScrollToPosition的滚动速度,我们可以在自定义的GridLayoutManager里重写该方法,使用自定义的LinearSmoothScroller来控制速度:
在刚才的SlowGridLayoutManager中添加以下代码:
@Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { LinearSmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) { @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { // 数值越大,滚动越慢,比如20f(可根据需求调整) // 这个值的单位是"毫秒/每像素",所以值越大,滚动相同距离耗时越久 return 20f / displayMetrics.densityDpi; } @Override protected int getVerticalSnapPreference() { // 滚动到目标位置的顶部对齐,也可以用SNAP_TO_CENTER居中对齐 return SNAP_TO_START; } }; smoothScroller.setTargetPosition(position); startSmoothScroll(smoothScroller); }
这样调用recyclerView.smoothScrollToPosition(position)时,就会按照你设定的速度平滑滚动,完全适配GridLayoutManager。
你的核心痛点之一是快速滚动时触发大量Glide加载,其实可以通过RecyclerView的滚动监听,在滚动时暂停Glide请求,停止滚动后再恢复,同时优化Glide的加载配置:
1. 添加滚动监听控制Glide加载
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); Context context = recyclerView.getContext(); if (newState == RecyclerView.SCROLL_STATE_IDLE) { // 滚动停止,恢复Glide加载 Glide.with(context).resumeRequests(); } else { // 正在滚动,暂停Glide所有请求 Glide.with(context).pauseRequests(); } } });
2. 优化Glide加载配置
在你的onBindViewHolder里,添加占位图并优化裁剪策略,减少绘制压力:
@Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { GlideApp.with(context) .load(arrayList.get(position).getUri()) .placeholder(R.drawable.placeholder_image) // 添加占位图,滚动时显示 .error(R.drawable.error_image) // 加载失败时的占位图 .apply(RequestOptions.overrideOf(180,180).centerCrop()) // 固定尺寸+裁剪,减少绘制开销 .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .into(holder.img); }
这样快速滚动时,RecyclerView只会显示占位图,不会触发真实图片的加载,只有当滚动停止后,Glide才会开始加载可见区域的图片,彻底解决前台加载压力的问题。
内容的提问来源于stack exchange,提问作者Anirudhdhsinh Jadeja

