如何修复RecyclerView搭配GridLayoutManager的异常选中问题?
RecyclerView在Android 5.1.1上选中状态异常问题排查与修复
问题现象
- 应用启动时显示正常
- 点击6、12、18、24选项后,选中状态显示符合预期
- 点击6取消选中时,选项5被莫名选中,且无法直接取消;需先取消12、18、24的选中状态后,才能取消选项5的选中状态
测试环境
- 设备:Android 5.1.1
- 项目配置:Android Studio 2022.2.1,最低支持SDK版本设为Android 5
疑问
这是系统版本导致的问题吗?如何让应用在Android 5上正常运行?
相关代码
RecyclerViewAdapter代码
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> { public interface ActionListenerCallback { void onItemClick(View v, int index); } private static ActionListenerCallback callback; public void setClickListener(ActionListenerCallback itemClickListener) { this.callback = itemClickListener; } private String[] mData; private boolean[] mStates; private LayoutInflater mInflater; RecyclerViewAdapter(Context context, String[] data, boolean[] states) { this.mInflater = LayoutInflater.from(context); this.mData = data; this.mStates = states; } public void setStates(boolean[] states) { this.mStates = states; } @Override @NonNull public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = mInflater.inflate(R.layout.grid_cell, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.myTextView.setText(mData[position]); if (mStates[position] == true) holder.itemView.setBackgroundColor(Color.parseColor("#FFB3EBF2")); } @Override public int getItemCount() { return mData.length; } public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { TextView myTextView; ViewHolder(View itemView) { super(itemView); myTextView = itemView.findViewById(R.id.cellText); itemView.setOnClickListener(this); } @Override public void onClick(View view) { if (callback != null) callback.onItemClick(view, getAdapterPosition()); } } String getItem(int id) { return mData[id]; } }
MainActivity代码
public class MainActivity extends AppCompatActivity { private AppBarConfiguration appBarConfiguration; private ActivityMainBinding binding; RecyclerViewAdapter adapter; boolean[] stateArray = new boolean[24]; String[] days = {"Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; int day_index; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String[] data = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24"}; RecyclerView recyclerView = findViewById(R.id.rvHours); int numberOfColumns = 6; recyclerView.setLayoutManager(new GridLayoutManager(this, numberOfColumns)); recyclerView.addItemDecoration(new SpacesItemDecoration(5)); adapter = new RecyclerViewAdapter(this, data, stateArray); adapter.setClickListener(new RecyclerViewAdapter.ActionListenerCallback() { @Override public void onItemClick(View view, int position) { stateArray[position] = !stateArray[position]; adapter.setStates(stateArray); adapter.notifyDataSetChanged(); } }); recyclerView.setAdapter(adapter); TextView view = (TextView)findViewById(R.id.tvDay); view.setText(days[0]); } public void DayDownClick(View view) { if (day_index > 0) day_index--; TextView v = (TextView)findViewById(R.id.tvDay); v.setText(days[day_index]); } public void DayUpClick(View view) { if (day_index < days.length-1) day_index++; TextView v = (TextView)findViewById(R.id.tvDay); v.setText(days[day_index]); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
问题原因与修复方案
核心原因
不是Android 5系统版本的问题,而是RecyclerView的View复用机制未被正确处理,加上Adapter中static回调的错误使用,导致状态错乱:
onBindViewHolder只设置了选中状态的背景色,未重置未选中状态的背景,复用View时会保留之前的选中样式static修饰的callback会导致多个Adapter实例共享同一回调,引发逻辑混乱,还可能造成内存泄漏
修复步骤
1. 完善View状态重置
修改RecyclerViewAdapter的onBindViewHolder方法,同时处理选中和未选中状态:
@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.myTextView.setText(mData[position]); if (mStates[position]) { holder.itemView.setBackgroundColor(Color.parseColor("#FFB3EBF2")); } else { // 恢复默认背景,可替换为你的布局默认背景色或资源 holder.itemView.setBackgroundColor(Color.TRANSPARENT); } }
2. 移除回调的static修饰
将RecyclerViewAdapter中的private static ActionListenerCallback callback;改为非static:
private ActionListenerCallback callback;
3. 优化列表刷新方式(可选)
替换notifyDataSetChanged()为单Item刷新,提升性能并减少复用问题:
在MainActivity的点击回调中修改:
@Override public void onItemClick(View view, int position) { stateArray[position] = !stateArray[position]; // 无需调用setStates,数组为引用传递,直接刷新对应位置 adapter.notifyItemChanged(position); }
验证说明
完成上述修改后,RecyclerView的选中状态会在Android 5.1.1设备上正常工作,核心是解决了View复用的状态残留问题,修正了错误的static变量使用。
内容的提问来源于stack exchange,提问作者Shaun Ross
相关产品推荐
相关产品推荐

