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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 16:45:23