如何正确获取RecyclerView Holder数据并通过Intent传递(优化监听写法)
解决RecyclerView中onBindViewHolder内设置监听器的不良实践问题
你提到的在onBindViewHolder里设置点击监听器确实是需要优化的点——每次RecyclerView复用Item时都会创建新的监听器实例,既浪费内存也不符合ViewHolder的复用设计理念。下面是修改后的完整AdapterDoctrines代码,同时解决了获取数据传递Intent的问题:
public class AdapterDoctrines extends RecyclerView.Adapter<AdapterDoctrines.DoctrinesHolder> { private Context mContext; public List<ModelGreatDoctrines> doctrines; public AdapterDoctrines(Context mContext, List<ModelGreatDoctrines> doctrines) { this.mContext = mContext; this.doctrines = doctrines; } @NonNull @Override public DoctrinesHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View viewDoctrineCard = LayoutInflater.from(mContext) .inflate(R.layout.card_doctrine, parent, false); return new DoctrinesHolder(viewDoctrineCard); } @Override public void onBindViewHolder(@NonNull DoctrinesHolder holder, int position) { // 只负责绑定数据,监听器已经在ViewHolder里初始化好了 ModelGreatDoctrines currentDoctrine = doctrines.get(position); holder.textViewTitle.setText(currentDoctrine.doctrineTitle); holder.textViewDesc.setText(currentDoctrine.doctrineSummary); // 如果有其他数据需要绑定,继续在这里添加 } @Override public int getItemCount() { return doctrines.size(); } class DoctrinesHolder extends RecyclerView.ViewHolder implements View.OnClickListener { CardView mCardView; AppCompatTextView textViewTitle; AppCompatTextView textViewDesc; AppCompatButton buttonRead; AppCompatButton buttonDownload; public DoctrinesHolder(View itemView) { super(itemView); mCardView = itemView.findViewById(R.id.card_great_doctrine); textViewTitle = itemView.findViewById(R.id.card_doctrine_title); textViewDesc = itemView.findViewById(R.id.card_doctrine_desc); buttonRead = itemView.findViewById(R.id.card_doctrine_read); buttonDownload = itemView.findViewById(R.id.card_doctrine_download); // 在这里一次性设置监听器,ViewHolder复用的时候不会重复创建 mCardView.setOnClickListener(this); buttonRead.setOnClickListener(this); buttonDownload.setOnClickListener(this); } @Override public void onClick(View view) { int id = view.getId(); // 获取当前Item对应的数据位置 int currentPosition = getBindingAdapterPosition(); // 检查位置是否有效(避免ViewHolder复用导致的位置错误) if (currentPosition == RecyclerView.NO_POSITION) return; ModelGreatDoctrines currentDoctrine = doctrines.get(currentPosition); switch (id) { case R.id.card_great_doctrine: // 处理卡片点击逻辑 break; case R.id.card_doctrine_read: // 跳转页面并传递数据 Intent intent = new Intent(mContext, ViewerDoctrineActivity.class); // 传递需要的数据,比如标题、摘要等 intent.putExtra("DOCTRINE_TITLE", currentDoctrine.doctrineTitle); intent.putExtra("DOCTRINE_SUMMARY", currentDoctrine.doctrineSummary); // 可以根据你的Model类添加更多需要传递的字段 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); mContext.startActivity(intent); break; case R.id.card_doctrine_download: // 处理下载逻辑,同样可以使用currentDoctrine获取下载所需数据 // PERFORM DOWNLOAD USING DOWNLOAD MANAGER break; } } } }
关键优化点说明:
- 监听器初始化移到ViewHolder构造方法:只在ViewHolder创建时设置一次监听器,复用Item时不会重复创建,节省内存开销。
- 通过
getBindingAdapterPosition()获取当前位置:替代直接用position参数,避免ViewHolder复用导致的位置错误(如果用旧版本RecyclerView,也可以用getAdapterPosition())。 - 数据绑定与事件处理分离:
onBindViewHolder只负责数据填充,事件逻辑全部放在ViewHolder的onClick方法里,代码结构更清晰。 - 直接在点击事件中获取数据:通过当前位置从数据源
doctrines中拿到对应的数据对象,再传递给Intent,不需要依赖ViewHolder中的View来获取数据(避免View复用导致的数据不一致)。
内容的提问来源于stack exchange,提问作者RoCkDevstack
相关产品推荐
相关产品推荐

