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

自定义RecyclerView滚动卡顿求助:已用ViewHolder仍卡顿

解决RecyclerView滚动卡顿与代码编译错误问题

嘿,我先帮你搞定最显眼的编译错误,再一步步解决滚动卡顿的核心问题:

一、先修复编译错误:RecyclerView被当成ListView使用了

你在ListViewActivity里犯了个小错误:XML布局里明明是RecyclerView,但你却用ListView类型去引用它,而且CustomAdapter是给RecyclerView用的,和ListView的Adapter完全不兼容,这就是报错的原因。

修复后的代码如下:

// 把ListView改成RecyclerView类型
RecyclerView recyclerView = findViewById(R.id.listView);
// 注意!RecyclerView必须设置LayoutManager才能显示内容,这很容易忘
recyclerView.setLayoutManager(new LinearLayoutManager(this));
CustomAdapter customAdapter = new CustomAdapter(ListViewActivity.this, immagineDei, nomeDei);
recyclerView.setAdapter(customAdapter);

二、解决RecyclerView滚动卡顿问题

你现在用setImageResource()加载drawable时,系统会在主线程完成图片解码操作——60张图片的话,滚动时主线程被阻塞,自然就卡顿了。下面是几种优化方案,按推荐程度排序:

1. 使用成熟的图片加载库(强推)

Glide、Picasso这类库会自动帮你处理后台解码、内存缓存、磁盘缓存、图片缩放等优化,完全不用自己写复杂的线程逻辑,省心又高效。

以Glide为例:

首先在模块级的build.gradle里添加依赖:

dependencies {
    implementation 'com.github.bumptech.glide:glide:4.16.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
}

然后修改CustomViewHolder的bind方法:

void bind(int imageId, String name) {
    Glide.with(itemView.getContext())
         .load(imageId)
         .into(mFlag); // 自动处理后台解码和缓存
    mName.setText(name);
}

你还可以加个占位图或者错误图,提升用户体验:

Glide.with(itemView.getContext())
     .load(imageId)
     .placeholder(R.drawable.placeholder_image) // 加载中的占位图
     .error(R.drawable.error_image) // 加载失败时显示的图
     .into(mFlag);

2. 手动在后台线程解码图片(不推荐,适合不想用第三方库的场景)

如果你不想引入第三方库,可以用Coroutine或者AsyncTask在后台解码图片,再回到主线程设置。但这种方式需要自己处理缓存,否则每次滚动都会重复解码,反而可能更糟:

用Coroutine的示例(需要Java Coroutine支持):

void bind(int imageId, String name) {
    mName.setText(name);
    // 后台线程解码图片
    CoroutineScope(Dispatchers.IO).launch(() -> {
        Drawable drawable = ResourcesCompat.getDrawable(itemView.getResources(), imageId, null);
        // 缩放图片到ImageView的实际尺寸,避免加载过大的Bitmap占用内存
        Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
        Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, mFlag.getWidth(), mFlag.getHeight(), true);
        // 回到主线程设置图片
        CoroutineScope(Dispatchers.Main).launch(() -> {
            mFlag.setImageBitmap(scaledBitmap);
        });
    });
}

3. 其他辅助优化点

  • 给ImageView固定宽高:在listview_item.xml里给ImageView设置明确的android:layout_width和android:layout_height,避免RecyclerView反复计算布局尺寸,拖慢滚动速度。
  • 开启RecyclerView的稳定ID:在CustomAdapter里添加setHasStableIds(true),并重写getItemId方法,帮助RecyclerView更高效地复用ViewHolder:
    public CustomAdapter(Context context, int[] immagineDio, String[] nomeDio){
        // ... 原有代码
        setHasStableIds(true);
    }
    
    @Override
    public long getItemId(int position) {
        return immagineDio[position]; // 用图片ID作为稳定标识,比position更可靠
    }
    
  • 减少过度绘制:在开发者选项里开启「显示过度绘制」,优化你的item布局,减少不必要的嵌套和背景,也能提升滚动流畅度。

三、完整修复后的代码示例

CustomAdapter.java

public class CustomAdapter extends RecyclerView.Adapter<CustomViewHolder> {
    private Context context;
    private int[] immagineDio;
    private String[] nomeDio;

    public CustomAdapter(Context context, int[] immagineDio, String[] nomeDio){
        this.context = context;
        this.immagineDio = immagineDio;
        this.nomeDio = nomeDio;
        setHasStableIds(true); // 开启稳定ID
    }

    @NonNull
    @Override
    public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new CustomViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_item, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) {
        holder.bind(immagineDio[position], nomeDio[position]);
    }

    @Override
    public int getItemCount() {
        return nomeDio.length;
    }

    @Override
    public long getItemId(int position) {
        return immagineDio[position]; // 用图片ID作为唯一标识
    }
}

CustomViewHolder.java(Glide版本)

public class CustomViewHolder extends RecyclerView.ViewHolder {
    ImageView mFlag;
    TextView mName;

    public CustomViewHolder(@NonNull View itemView) {
        super(itemView);
        mFlag = itemView.findViewById(R.id.imageView);
        mName = itemView.findViewById(R.id.textView);
    }

    void bind(int imageId, String name) {
        Glide.with(itemView.getContext())
             .load(imageId)
             .placeholder(R.drawable.placeholder)
             .error(R.drawable.error)
             .into(mFlag);
        mName.setText(name);
    }
}

ListViewActivity.java修复后

public class ListViewActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.listview_layout);
        String[] nomeDei = {"Baldr","Borr","Bragi","Dagr","Dellingr","Eir","Eostre","Forseti","Freya","Freyr","Frigg","Fulla","Gefjun","Gerðr","Gullveig","Heimdallr","Hel","Hermóðr","Höðr","Hœnir","Iðunn","Itreksjóð","Jǫrð","Kvasir","Lóðurr","Lofn","Logi","Lýtir","Máni","Mímir","Móði","Nanna","Njörun","Njörðr","Nótt","Óðr","Rán","Ríg","Sága","Sif","Signe","Sigyn","Sinfjötli","Sjöfn","Skaði","Skirnir","Snotra","Sól","Syn","Thor","Týr","Ullr","Váli","Vár","Ve","Viðarr","Víli","Vör"};
        int[] immagineDei = {
            R.drawable.profilo_baldr, R.drawable.profilo_borr, R.drawable.profilo_bragi, R.drawable.profilo_dagr, R.drawable.profilo_dellingr,
            R.drawable.profilo_eir, R.drawable.profilo_eostre, R.drawable.profilo_forseti, R.drawable.profilo_freya, R.drawable.profilo_freyr,
            R.drawable.profilo_frigg, R.drawable.profilo_fulla, R.drawable.profilo_gefjun, R.drawable.profilo_geror, R.drawable.profilo_gullveig,
            R.drawable.profilo_heimdallr, R.drawable.profilo_hel, R.drawable.profilo_hermoor, R.drawable.profilo_hoor, R.drawable.profilo_hoenir,
            R.drawable.profilo_iounn, R.drawable.profilo_itreksjoo, R.drawable.profilo_joro, R.drawable.profilo_kvasir, R.drawable.profilo_loourr,
            R.drawable.profilo_lofn, R.drawable.profilo_logi, R.drawable.profilo_lytir, R.drawable.profilo_mani, R.drawable.profilo_mimir,
            R.drawable.profilo_modi, R.drawable.profilo_nanna, R.drawable.profilo_njorun, R.drawable.profilo_njoror, R.drawable.profilo_nott,
            R.drawable.profilo_oor, R.drawable.profilo_ran, R.drawable.profilo_rig, R.drawable.profilo_saga, R.drawable.profilo_sif,
            R.drawable.profilo_signe, R.drawable.profilo_sigyn, R.drawable.profilo_sinfjotli, R.drawable.profilo_sjofn, R.drawable.profilo_skaoi,
            R.drawable.profilo_skirnir, R.drawable.profilo_snotra, R.drawable.profilo_sol, R.drawable.profilo_syn, R.drawable.profilo_thor,
            R.drawable.profilo_tyr, R.drawable.profilo_ullr, R.drawable.profilo_vali, R.drawable.profilo_var, R.drawable.profilo_ve,
            R.drawable.profilo_vidar, R.drawable.profilo_vili, R.drawable.profilo_vor
        };
        RecyclerView recyclerView = findViewById(R.id.listView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        // 可选:添加分隔线,让列表更清晰
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
        CustomAdapter customAdapter = new CustomAdapter(ListViewActivity.this, immagineDei, nomeDei);
        recyclerView.setAdapter(customAdapter);
    }
}

内容的提问来源于stack exchange,提问作者Fyd Rose

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:53:09