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

将RecyclerView Adapter中ViewHolder的ButterKnife替换为View Binding求助

用ViewBinding替换RecyclerView Adapter中的ButterKnife实现方案

我正在重构一款旧漫画应用,希望在RecyclerView Adapter类中使用View Binding替代ButterKnife,但转换过程中遇到阻碍,无法顺利完成。以下是原实现代码,求解决思路:

package mowmow.adapter;

import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.example.comic_app.R;
import com.squareup.picasso.Picasso;

import java.util.ArrayList;
import java.util.List;

import mowmow.database.aService;
import mowmow.database.dRetrofit;
import mowmow.model.TrendingResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class adTrendingComic extends RecyclerView.Adapter<adTrendingComic.ViewHolder> {

    @NonNull
    @Override
    public adTrendingComic.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_row, parent, false);
        ButterKnife.bind(this, itemView);
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull adTrendingComic.ViewHolder holder, int position) {
        getPosterPaths();
        Picasso.get()
                .load(posterPaths.get(position))
                .placeholder(R.drawable.cloud_black_24dp).resize(350, 450)
                .into(holder.trendingComicPosterIV);

    }

    private TrendingResponse trendingComicResponse;
    private Context myContext;
    private List<String> posterPaths = new ArrayList<>();

    public adTrendingComic(TrendingResponse trendingComicResponse, Context context){
        this.trendingComicResponse = trendingComicResponse;
        this.myContext = context;
    }

    private void getPosterPaths(){
        for(int i = 0; i < trendingComicResponse.getTop().size(); i++){
            String posterUrlWithoutSize = trendingComicResponse
                    .getTop()
                    .get(i)
                    .getImageUrl()
                    .replace("/r/100x140", "");
            posterPaths.add(posterUrlWithoutSize);
        }
    }

    @Override
    public int getItemCount() {
        return trendingComicResponse.getTop().size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.trendingPosterIV)
        ImageView trendingComicPosterIV;
        private Intent intent;
        private aService service = dRetrofit.getData().create(aService.class);

        private ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
            intent = new Intent(myContext, DetailsActivity.class);
            trendingComicPosterIV.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int malId = trendingComicResponse.getTop().get(getAdapterPosition()).getMalId();
                    Call<ComicResponse> call = service.getComicByID(malId);
                    call.enqueue(new Callback<ComicResponse>() {

                        @Override
                        public void onResponse(@NonNull Call<ComicResponse> call, @NonNull Response<ComicResponse> response) {
                            if(response.body() != null) {
                                TempEnumForComics enumForComic = TempEnumForComics.INSTANCE;
                                enumForComic.setComic(response.body());

                                myContext.startActivity(intent);
                            }
                            else{

                                Toast.makeText(myContext, "No result", Toast.LENGTH_LONG).show();
                            }
                        }

                        @Override
                        public void onFailure(@NonNull Call<ComicResponse> call, @NonNull Throwable t) {
                            Toast.makeText(myContext, "No result", Toast.LENGTH_LONG).show();
                        }
                    });
                }
            });
        }
    }
}

转换步骤说明

  1. 开启ViewBinding
    在模块级build.gradle(或build.gradle.kts)中启用ViewBinding:

    android {
        ...
        buildFeatures {
            viewBinding true
        }
    }
    
  2. 替换ViewHolder控件绑定逻辑
    布局image_row.xml会自动生成对应的ImageRowBinding类,用该类替代ButterKnife的@BindView注解与绑定代码。

  3. 修改onCreateViewHolder方法
    通过ImageRowBinding.inflate()直接加载布局并生成绑定对象,传入ViewHolder,无需手动处理View创建和ButterKnife绑定。

  4. 移除ButterKnife相关代码
    删除所有@BindView注解、ButterKnife.bind()调用,以及项目中ButterKnife的依赖。

  5. 修复原代码隐藏bug
    原代码在onBindViewHolder中重复调用getPosterPaths(),会导致posterPaths列表重复添加数据,将该方法移到构造函数中仅执行一次。

修改后的完整代码

package mowmow.adapter;

import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.example.comic_app.R;
import com.example.comic_app.databinding.ImageRowBinding;
import com.squareup.picasso.Picasso;

import java.util.ArrayList;
import java.util.List;

import mowmow.database.aService;
import mowmow.database.dRetrofit;
import mowmow.model.ComicResponse;
import mowmow.model.TrendingResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class adTrendingComic extends RecyclerView.Adapter<adTrendingComic.ViewHolder> {

    private final TrendingResponse trendingComicResponse;
    private final Context myContext;
    private final List<String> posterPaths = new ArrayList<>();

    public adTrendingComic(TrendingResponse trendingComicResponse, Context context){
        this.trendingComicResponse = trendingComicResponse;
        this.myContext = context;
        // 仅初始化一次海报路径列表
        getPosterPaths();
    }

    @NonNull
    @Override
    public adTrendingComic.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // 使用ViewBinding加载布局
        ImageRowBinding binding = ImageRowBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
        return new ViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull adTrendingComic.ViewHolder holder, int position) {
        Picasso.get()
                .load(posterPaths.get(position))
                .placeholder(R.drawable.cloud_black_24dp)
                .resize(350, 450)
                .into(holder.binding.trendingPosterIV);
    }

    private void getPosterPaths(){
        for(int i = 0; i < trendingComicResponse.getTop().size(); i++){
            String posterUrlWithoutSize = trendingComicResponse
                    .getTop()
                    .get(i)
                    .getImageUrl()
                    .replace("/r/100x140", "");
            posterPaths.add(posterUrlWithoutSize);
        }
    }

    @Override
    public int getItemCount() {
        return trendingComicResponse.getTop().size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        private final ImageRowBinding binding;
        private final Intent intent;
        private final aService service = dRetrofit.getData().create(aService.class);

        private ViewHolder(ImageRowBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
            intent = new Intent(myContext, DetailsActivity.class);
            
            binding.trendingPosterIV.setOnClickListener(v -> {
                int malId = trendingComicResponse.getTop().get(getAdapterPosition()).getMalId();
                Call<ComicResponse> call = service.getComicByID(malId);
                call.enqueue(new Callback<ComicResponse>() {

                    @Override
                    public void onResponse(@NonNull Call<ComicResponse> call, @NonNull Response<ComicResponse> response) {
                        if(response.body() != null) {
                            TempEnumForComics enumForComic = TempEnumForComics.INSTANCE;
                            enumForComic.setComic(response.body());
                            myContext.startActivity(intent);
                        } else {
                            Toast.makeText(myContext, "No result", Toast.LENGTH_LONG).show();
                        }
                    }

                    @Override
                    public void onFailure(@NonNull Call<ComicResponse> call, @NonNull Throwable t) {
                        Toast.makeText(myContext, "No result", Toast.LENGTH_LONG).show();
                    }
                });
            });
        }
    }
}

关键改动说明

  • ViewHolder构造函数:接收ImageRowBinding对象,通过binding.getRoot()获取根View,同时持有binding对象用于访问控件。
  • onCreateViewHolder:直接使用ViewBinding的inflate方法生成布局绑定,无需手动处理View创建和ButterKnife绑定。
  • 控件访问:所有控件通过binding.xxx的方式获取,替代原有的ButterKnife注解控件。
  • 数据初始化优化:将getPosterPaths()移到构造函数中,避免每次绑定Item时重复添加数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 06:15:34