RecyclerView列表复用异常:滚动时TextView错乱、数据重复不完整排查
解决RecyclerView视图错乱与重复数据问题
嗨,我看了你遇到的RecyclerView滚动时内容错乱、重复数据的问题,这其实是RecyclerView适配器实现中很常见的错误——你把ViewHolder里的TextView声明成了适配器的全局成员变量,导致所有复用的视图共用同一组TextView引用,滚动时就会出现内容串位、重复的情况。另外还有几个小细节也需要调整,我来帮你一步步修正:
问题核心原因
- 适配器类中的
txtsessiontime、txtvolupload等TextView是全局成员,而非每个ViewHolder的独立引用。当RecyclerView复用视图时,这些全局变量会被新的ViewHolder覆盖,导致旧视图内容被错误更新。 onBindViewHolder中使用holder.getAdapterPosition()获取位置,虽然能工作,但更直接的是用方法参数里的position(适配新版本可改用holder.getBindingAdapterPosition())。- ViewHolder的类名
View和Android系统的View类重名,容易引发混淆和潜在错误。
修正后的适配器代码
public class Adapter_Usage extends RecyclerView.Adapter<Adapter_Usage.UsageViewHolder>{ Context context; ArrayList<Info_Usage> listusage; public Adapter_Usage(Context context, ArrayList<Info_Usage> listusage) { this.context = context; this.listusage = listusage; } @Override public UsageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { android.view.View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_usage, parent, false); return new UsageViewHolder(view); } @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) @Override public void onBindViewHolder(UsageViewHolder holder, int position) { Info_Usage infousage = listusage.get(position); String[] arraybilldate = infousage.strdate.split("\\s+"); // 直接使用holder中的TextView引用,确保每个视图项独立绑定数据 holder.txtusageyear.setText(arraybilldate[2]); holder.txtusageday.setText(arraybilldate[1]); holder.txtusagemon.setText(arraybilldate[0]+", "); holder.txtsessiontime.setText(infousage.strsessiontime); holder.txtvolupload.setText("Upload : "+infousage.struploads+" MB"); holder.txtvoldownload.setText("Download : "+infousage.strdownloads+" MB"); } @Override public int getItemCount() { return listusage.size(); } // 重命名ViewHolder类,避免和系统View冲突,将TextView作为内部成员 public class UsageViewHolder extends RecyclerView.ViewHolder { CustomTextView txtsessiontime, txtvolupload, txtvoldownload, txtusageyear, txtusageday, txtusagemon; public UsageViewHolder(android.view.View view) { super(view); txtusageyear = view.findViewById(R.id.txtusageyear); txtusageday = view.findViewById(R.id.txtusageday); txtusagemon = view.findViewById(R.id.txtusagemon); txtsessiontime = view.findViewById(R.id.txtsessiontime); txtvolupload = view.findViewById(R.id.txtvolupload); txtvoldownload = view.findViewById(R.id.txtvoldownload); } } }
其他优化建议
- 适配器构造函数简化:你原构造函数中的
int resource参数未被使用,直接移除即可。 - 主Activity数据更新优化:如果后续需要频繁更新列表数据,建议复用适配器对象并调用
notifyDataSetChanged()通知数据更新,避免重复创建适配器实例:
// 初始化适配器(可放在Activity的onCreate方法中) Adapter_Usage usageAdapter = new Adapter_Usage(Activity_Usage.this, listusage); recycler.setAdapter(usageAdapter); // 排序数据后更新视图 Collections.sort(listusage, new Comparator<Info_Usage>() { @Override public int compare(Info_Usage lhs, Info_Usage rhs) { int right = Integer.parseInt(rhs.getcurr()); int left = Integer.parseInt(lhs.getcurr()); return Integer.compare(right, left); // 用Integer.compare简化排序逻辑 } }); if(listusage.size() == 0) { recycler.setVisibility(View.GONE); txtnousage.setVisibility(View.VISIBLE); } else { txtnousage.setVisibility(View.GONE); recycler.setVisibility(View.VISIBLE); usageAdapter.notifyDataSetChanged(); // 通知适配器数据已更新 }
修复原理说明
RecyclerView的核心是视图复用,每个ViewHolder对应一个独立的可复用视图项。将TextView放在ViewHolder内部,能保证每个复用视图都有自己的控件引用,不会互相干扰。滚动时onBindViewHolder会正确将对应位置的数据绑定到当前ViewHolder的控件上,彻底解决内容错乱、重复的问题。
内容的提问来源于stack exchange,提问作者Joshan Tandukar
相关产品推荐
相关产品推荐

