Android文件夹图片视频列表滚动卡顿,求优化显示方案
解决RecyclerView展示图片/视频缩略图卡顿的优化方案
兄弟,滚动卡顿这个问题太常见了,看了你贴的代码,问题主要出在主线程同步加载缩略图和没有缓存机制这两点上——每次滚动到视频Item时,你都在UI线程里调用ThumbnailUtils.createVideoThumbnail生成缩略图,这可是实打实的耗时操作,直接把UI线程卡得动弹不得。而且每次复用ViewHolder都要重新生成一遍,完全是重复劳动,不卡才怪!
下面给你几个针对性的优化方案,从易到难,你可以按需选择:
1. 直接用成熟的图片加载库(最推荐,省心省力)
像Glide、Picasso这类库本身就封装了异步加载、内存/磁盘缓存、自动复用等优化逻辑,处理视频缩略图也不在话下,完全不用自己造轮子。
比如用Glide实现的话,把你原来的代码改成这样就行:
public void onBindViewHolder(@NonNull DetailViewHolder holder, final int position) { String filePath = listFile.get(position).toString(); if (filePath.endsWith("mp4")) { // 用Glide加载视频缩略图 Glide.with(mContext) .asBitmap() .load(Uri.fromFile(new File(filePath))) .placeholder(R.drawable.default_video_placeholder) // 加载时的占位图 .error(R.drawable.load_error) // 加载失败的占位图 .into(holder.ivIcon); holder.ivPlay.setVisibility(View.VISIBLE); } else { // 图片的话直接加载 Glide.with(mContext) .load(Uri.fromFile(new File(filePath))) .placeholder(R.drawable.default_image_placeholder) .into(holder.ivIcon); holder.ivPlay.setVisibility(View.GONE); } }
Glide会自动帮你:
- 在后台线程生成/加载缩略图,不阻塞UI
- 缓存生成好的缩略图,下次滚动直接取缓存
- 根据ImageView的大小自动压缩图片,减少内存占用
2. 自己实现异步加载+缓存(适合不想依赖第三方库的场景)
如果不想引入第三方库,那至少要把缩略图的生成放到异步线程,同时加上内存和磁盘缓存:
第一步:用异步线程生成缩略图
可以用AsyncTask或者ExecutorService来做后台任务,比如:
private class LoadThumbnailTask extends AsyncTask<String, Void, Bitmap> { private WeakReference<ImageView> imageViewRef; private String filePath; public LoadThumbnailTask(ImageView imageView, String filePath) { this.imageViewRef = new WeakReference<>(imageView); this.filePath = filePath; } @Override protected Bitmap doInBackground(String... params) { // 后台生成缩略图 return ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Video.Thumbnails.MINI_KIND); } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); ImageView imageView = imageViewRef.get(); if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); } } }
然后在onBindViewHolder里调用:
public void onBindViewHolder(@NonNull DetailViewHolder holder, final int position) { String filePath = listFile.get(position).toString(); if (filePath.contains("mp4")) { holder.ivPlay.setVisibility(View.VISIBLE); // 先设置占位图 holder.ivIcon.setImageResource(R.drawable.default_video_placeholder); // 启动异步任务加载缩略图 new LoadThumbnailTask(holder.ivIcon, filePath).execute(); } }
第二步:添加缓存机制
用LruCache做内存缓存,把生成好的缩略图存起来,下次直接取:
private LruCache<String, Bitmap> thumbnailCache; // 在初始化的时候设置缓存大小,比如内存的1/8 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; thumbnailCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // 返回Bitmap的大小(KB) return bitmap.getByteCount() / 1024; } }; // 存缓存 private void addBitmapToCache(String key, Bitmap bitmap) { if (getBitmapFromCache(key) == null) { thumbnailCache.put(key, bitmap); } } // 取缓存 private Bitmap getBitmapFromCache(String key) { return thumbnailCache.get(key); }
然后在异步任务里先查缓存,没有再生成:
@Override protected Bitmap doInBackground(String... params) { Bitmap cachedBitmap = getBitmapFromCache(filePath); if (cachedBitmap != null) { return cachedBitmap; } Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Video.Thumbnails.MINI_KIND); addBitmapToCache(filePath, bitmap); return bitmap; }
3. RecyclerView本身的基础优化
除了缩略图加载,RecyclerView本身也可以做一些优化来提升流畅度:
- 给RecyclerView设置
setHasFixedSize(true),如果你的列表高度是固定的,这样可以减少布局重计算 - 避免在
onBindViewHolder里做任何耗时操作,除了绑定数据 - 优化Item布局,减少嵌套层级,用
ConstraintLayout代替嵌套的LinearLayout/RelativeLayout,降低过度绘制 - 如果不需要Item动画,可以关闭默认动画:
recyclerView.setItemAnimator(null);
最后再提醒一句,自己写缓存和异步很容易踩内存泄漏、重复加载的坑,还是优先用Glide这类成熟库,省心又靠谱!
内容的提问来源于stack exchange,提问作者A2N
相关产品推荐
相关产品推荐

