RecyclerView加载百条以上视频后滚动卡顿问题求助
解决RecyclerView加载大量视频卡顿的优化方案
1. 实现ViewHolder控件缓存,避免重复findViewById
当前代码每次onBindViewHolder都重新查找所有控件,属于主线程高频耗时操作,必须将控件引用缓存到ViewHolder中:
public class Recyclerview1Adapter extends RecyclerView.Adapter<Recyclerview1Adapter.ViewHolder> { private ArrayList<HashMap<String, Object>> _data; // 全局缓存字体和Drawable private static Typeface spartanRegular; private static Typeface spartanBold; private static GradientDrawable cardBgDrawable; public Recyclerview1Adapter(ArrayList<HashMap<String, Object>> _arr, Context context) { _data = _arr; // 初始化全局缓存资源,仅执行一次 initCachedResources(context); } private void initCachedResources(Context context) { spartanRegular = Typeface.createFromAsset(context.getAssets(), "fonts/spartan_regular.ttf"); spartanBold = Typeface.createFromAsset(context.getAssets(), "fonts/spartan_bold.ttf"); cardBgDrawable = new GradientDrawable(); cardBgDrawable.setCornerRadius(18); cardBgDrawable.setColor(0xFF272731); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater _inflater = LayoutInflater.from(parent.getContext()); // 正确加载布局,无需手动设置LayoutParams View _v = _inflater.inflate(R.layout.video_item, parent, false); return new ViewHolder(_v); } // 缓存所有控件引用 public class ViewHolder extends RecyclerView.ViewHolder { LinearLayout linear1, linear2, linear4, linear3, linear7, linear5, linear10; CardView cardview2; ImageView imageview1, imageview3; TextView textview1, textview2, textview3, textview4; public ViewHolder(View v) { super(v); // 一次性查找所有控件 linear1 = v.findViewById(R.id.linear1); linear2 = v.findViewById(R.id.linear2); linear4 = v.findViewById(R.id.linear4); cardview2 = v.findViewById(R.id.cardview2); linear3 = v.findViewById(R.id.linear3); linear7 = v.findViewById(R.id.linear7); imageview1 = v.findViewById(R.id.imageview1); textview1 = v.findViewById(R.id.textview1); textview2 = v.findViewById(R.id.textview2); linear5 = v.findViewById(R.id.linear5); textview3 = v.findViewById(R.id.textview3); textview4 = v.findViewById(R.id.textview4); linear10 = v.findViewById(R.id.linear10); imageview3 = v.findViewById(R.id.imageview3); // 一次性设置固定样式 textview1.setTypeface(spartanRegular); textview1.setTextSize(8); _shapeRadius(textview1, "#000000", 6); textview2.setTypeface(spartanBold); textview3.setTypeface(spartanRegular); textview4.setTypeface(spartanBold); _shapeRadius(textview4, "#272731", 8); cardview2.setBackground(cardBgDrawable); } }
2. 移除动态创建CardView的操作,改用XML布局实现
每次onBindViewHolder动态修改布局层级会触发多次View添加/移除和布局测量,是卡顿核心原因之一。直接在video_item.xml中将ImageView包裹在CardView里,删除以下代码:
androidx.cardview.widget.CardView cardview1 = new androidx.cardview.widget.CardView(VideoListFragmentActivity.this.getContext()); cardview1.setCardElevation(0); cardview1.setCardBackgroundColor(0xFF272731); cardview1.setRadius(18); ViewGroup imageParent = ((ViewGroup)imageview1.getParent()); imageParent.removeView(imageview1); cardview1.addView(imageview1); imageParent.addView(cardview1);
3. 提前预处理数据,避免主线程做解析/转换
将视频时长格式化、文件大小转换、文件名提取等操作,提前在MediaStore数据获取阶段完成,存入_data集合,onBindViewHolder仅负责赋值:
数据准备阶段示例:
// 遍历MediaStore游标时预处理数据 HashMap<String, Object> videoData = new HashMap<>(); long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.DURATION)); videoData.put("formattedDuration", stringForTime((int)(duration / 1000))); String path = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA)); videoData.put("videoName", Uri.parse(path).getLastPathSegment()); long size = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.SIZE)); videoData.put("formattedSize", bytesIntoHumanReadable(size)); // 存入原有数据 videoData.put("videoPath", path); videoData.put("formattedDate", cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATE_MODIFIED))); allVideosList.add(videoData);
修改后的onBindViewHolder:
@Override public void onBindViewHolder(ViewHolder _holder, final int _position) { HashMap<String, Object> videoItem = _data.get(_position); // 直接使用预处理好的数据 _holder.textview1.setText(videoItem.get("formattedDuration").toString()); _holder.textview2.setText(videoItem.get("videoName").toString()); _holder.textview3.setText(videoItem.get("formattedDate").toString()); _holder.textview4.setText(videoItem.get("formattedSize").toString()); String path = videoItem.get("videoPath").toString(); // Glide加载优化 Glide.with(_holder.itemView.getContext()) .load(Uri.fromFile(new File(path))) .transition(DrawableTransitionOptions.withCrossFade()) .placeholder(R.drawable.video_background) .error(R.drawable.video_background) .fitCenter() .skipMemoryCache(true) // 跳过内存缓存,避免内存过载 .diskCacheStrategy(DiskCacheStrategy.ALL) // 启用磁盘缓存 .into(_holder.imageview1); }
4. 用系统缩略图替代Glide解码视频
使用MediaStore生成的缩略图Uri,比Glide自行解码视频更快:
数据准备阶段获取缩略图:
long videoId = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media._ID)); Uri thumbnailUri = ContentUris.withAppendedId(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, videoId); videoData.put("thumbnailUri", thumbnailUri);
Glide加载时替换为:
Glide.with(_holder.itemView.getContext()) .load(videoItem.get("thumbnailUri")) .transition(DrawableTransitionOptions.withCrossFade()) .placeholder(R.drawable.video_background) .error(R.drawable.video_background) .fitCenter() .into(_holder.imageview1);
5. RecyclerView基础优化配置
在初始化RecyclerView时添加:
// 固定Item高度,避免重复测量 recyclerView.setHasFixedSize(true); // 设置预加载数量 LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setInitialPrefetchItemCount(10); recyclerView.setLayoutManager(layoutManager); // 增大缓存数量 recyclerView.setItemViewCacheSize(20); recyclerView.setDrawingCacheEnabled(true); recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
内容的提问来源于stack exchange,提问作者Ayan Mishra
相关产品推荐
相关产品推荐

