You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GridLayoutManager结合setSpanSizeLookup导致RecyclerView滚动卡顿求助

解决GridLayoutManager加SpanSizeLookup后的滚动卡顿问题

我之前做类似的图片画廊应用时也遇到过一模一样的问题,咱们先找到卡顿的根源,再一步步优化:

核心卡顿原因:频繁的IO操作

你现在的getItemViewType方法里,每次都会创建File对象并调用exists()判断文件是否存在——而setSpanSizeLookup的getSpanSize方法会频繁调用getItemViewType,RecyclerView滚动时会大量触发这个逻辑,磁盘IO操作是非常耗时的,这就是导致滚动卡顿的罪魁祸首。

具体优化方案

1. 预缓存ViewType,避免实时IO

在Adapter初始化时就提前计算好所有位置的ViewType并缓存,滚动时直接读取缓存值即可:

修改你的GalleryAdapter,添加缓存ViewType的列表:

public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.GalleryViewHolder> {
    public static final int VIEW_TYPE_HEADER = 2;
    public static final int VIEW_TYPE_ITEM = 1;
    private Context context;
    private ArrayList<String> imageList;
    private ArrayList<String> uriList = new ArrayList<>();
    private ArrayList<String> selectAllList = new ArrayList<>();
    private ArrayList<String> videoList = new ArrayList<>();
    private VideoThumbLoader thumbLoader = new VideoThumbLoader();
    // 新增:缓存所有位置的ViewType
    private List<Integer> cachedViewTypes;

    public GalleryAdapter(Context context, ArrayList<String> imageList) {
        this.context = context;
        this.imageList = imageList;
        // 预计算并缓存ViewType
        cachedViewTypes = new ArrayList<>(imageList.size());
        for (String path : imageList) {
            File file = new File(path);
            cachedViewTypes.add(file.exists() ? VIEW_TYPE_ITEM : VIEW_TYPE_HEADER);
        }
    }

    // 修改getItemViewType,直接返回缓存值
    @Override
    public int getItemViewType(int position) {
        return cachedViewTypes.get(position);
    }

    // 其他原有代码保持不变...
}

2. 移除不必要的RecyclerView缓存设置

你当前设置的setDrawingCacheEnabled和setDrawingCacheQuality其实没必要,RecyclerView本身已经有成熟的视图缓存机制,手动开启DrawingCache会额外占用内存,反而可能影响性能:

// 移除这几行代码
// recyclerView.setDrawingCacheEnabled(true);
// recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);

3. 优化图片/视频加载逻辑

  • 用Glide统一加载视频缩略图,替代自定义的VideoThumbLoader,Glide自带缓存和线程池优化,能减少卡顿:
// 替换原有视频缩略图加载代码
if (getMimeType(file).contains("video")) {
    holder.video.setVisibility(View.VISIBLE);
    videoList.add(imageList.get(holder.getBindingAdapterPosition()));
    // 用Glide加载视频缩略图
    Glide.with(holder.image.getContext())
         .load(file)
         .apply(RequestOptions.centerCropTransform())
         .into(holder.image);
}
  • 给Glide添加磁盘缓存策略,避免重复加载:
Glide.with(holder.image.getContext())
     .load(new File(uri.getPath()))
     .apply(RequestOptions.centerCropTransform()
             .diskCacheStrategy(DiskCacheStrategy.ALL)) // 添加缓存策略
     .thumbnail(1f)
     .into(holder.image);

4. 优化SpanSizeLookup的实现(可选)

可以把SpanSizeLookup提取成成员变量,避免每次创建匿名内部类,虽然影响不大,但能让代码更整洁:

// 在Activity里定义成员变量
private GridLayoutManager.SpanSizeLookup spanSizeLookup = new GridLayoutManager.SpanSizeLookup() {
    @Override
    public int getSpanSize(int position) {
        switch (adapter.getItemViewType(position)) {
            case GalleryAdapter.VIEW_TYPE_HEADER:
                return 4;
            case GalleryAdapter.VIEW_TYPE_ITEM:
                return 1;
            default:
                return 1;
        }
    }
};

// 然后在onCreate里设置
manager.setSpanSizeLookup(spanSizeLookup);

为什么这些优化能解决问题?

预缓存ViewType彻底消除了滚动时的频繁磁盘IO操作,这是卡顿的核心原因;优化图片加载能减少UI线程的阻塞;移除不必要的缓存设置能降低内存占用,让RecyclerView的原生缓存机制更好地工作。

按照这些步骤修改后,你再测试滚动500张图片的列表,应该就能恢复流畅了。

内容的提问来源于stack exchange,提问作者jitendra purohit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:04:55