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
相关产品推荐
相关产品推荐

