将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(); } }); } }); } } }
转换步骤说明
开启ViewBinding
在模块级build.gradle(或build.gradle.kts)中启用ViewBinding:android { ... buildFeatures { viewBinding true } }替换ViewHolder控件绑定逻辑
布局image_row.xml会自动生成对应的ImageRowBinding类,用该类替代ButterKnife的@BindView注解与绑定代码。修改onCreateViewHolder方法
通过ImageRowBinding.inflate()直接加载布局并生成绑定对象,传入ViewHolder,无需手动处理View创建和ButterKnife绑定。移除ButterKnife相关代码
删除所有@BindView注解、ButterKnife.bind()调用,以及项目中ButterKnife的依赖。修复原代码隐藏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
相关产品推荐
相关产品推荐

