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

如何修复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回调的错误使用,导致状态错乱:

  1. onBindViewHolder只设置了选中状态的背景色,未重置未选中状态的背景,复用View时会保留之前的选中样式
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:07:12