Android中点击CardView切换背景色并持久化的问题求助
问题描述
点击Card时背景色有时变化有时不变化,修改后的颜色经常无法保留。想要实现类似Facebook通知页面的效果,通过卡片颜色变化标记已读通知,但Adapter类存在问题。尝试用SharedPreferences保存颜色状态,但未达到预期效果,推测问题出在切换逻辑上。无Android开发专业经验,应用已进入最终阶段,急需解决。
原Adapter代码:
public class PyqAdapter extends RecyclerView.Adapter<PyqAdapter.ViewHolder> { private final Context mCtx; private final List<PyqModel> pyqModelList; private final int defaultBackgroundColor; private final int selectedBackgroundColor; private final Set<Integer> selectedPositions; private static final String PREFS_NAME = "PyqAllItems"; private static final String SELECTED_ITEMS_KEY = "PyqSelectedItems"; public PyqAdapter(Context mCtx, List<PyqModel> pyqModelList) { if (mCtx == null) { throw new IllegalArgumentException("Context cannot be null"); } this.mCtx = mCtx; this.pyqModelList = pyqModelList; this.selectedPositions = new HashSet<>(); // Load colors based on the current theme Resources res = mCtx.getResources(); int nightModeFlags = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) { defaultBackgroundColor = ContextCompat.getColor(mCtx, R.color.defaultBackgroundDark); selectedBackgroundColor = ContextCompat.getColor(mCtx, R.color.selectedBackgroundDark); } else { defaultBackgroundColor = ContextCompat.getColor(mCtx, R.color.defaultBackgroundLight); selectedBackgroundColor = ContextCompat.getColor(mCtx, R.color.selectedBackgroundLight); } // Load selected states from SharedPreferences SharedPreferences prefs = mCtx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); Set<String> selectedItems = prefs.getStringSet(SELECTED_ITEMS_KEY, new HashSet<>()); for (String position : selectedItems) { selectedPositions.add(Integer.parseInt(position)); } // Set selection state on models based on loaded positions for (int i = 0; i < pyqModelList.size(); i++) { PyqModel model = pyqModelList.get(i); model.setSelected(selectedPositions.contains(i)); } } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.pyq_rv_layout, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { final PyqModel temp = pyqModelList.get(position); // Set text views holder.textView.setText(temp.getPdfName()); holder.serialNumber.setText(String.valueOf(position + 1)); holder.pyqTopics.setText(temp.getPyqTopics()); // Use holder.getAdapterPosition() to get the current position int adapterPosition = holder.getAdapterPosition(); if (adapterPosition == RecyclerView.NO_POSITION) { return; } // Set background color based on selection state if (temp.isSelected()) { holder.cardView.setCardBackgroundColor(selectedBackgroundColor); } else { holder.cardView.setCardBackgroundColor(defaultBackgroundColor); } holder.cardView.setOnClickListener(v -> { SharedPreferences prefs = mCtx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); Set<String> selectedItems = new HashSet<>(); for (int pos : selectedPositions) { selectedItems.add(String.valueOf(pos)); } selectedPositions.add(adapterPosition); editor.putStringSet(SELECTED_ITEMS_KEY, selectedItems); // Notify adapter to refresh views notifyItemChanged(adapterPosition); editor.apply(); // Launch ViewPdf activity Intent i = new Intent(holder.cardView.getContext(), ViewPdf.class); i.putExtra("pdfName", temp.getPdfName()); i.putExtra("pdfUrl", temp.getPdfUri()); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); holder.cardView.getContext().startActivity(i); }); } @Override public int getItemCount() { return pyqModelList.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { CardView cardView; TextView textView, serialNumber, pyqTopics; public ViewHolder(View itemView) { super(itemView); cardView = itemView.findViewById(R.id.pyqCardView); textView = itemView.findViewById(R.id.pyqTitle); serialNumber = itemView.findViewById(R.id.serialNumber); pyqTopics = itemView.findViewById(R.id.pyqTopics); } } }
问题分析
代码存在3个核心问题导致状态异常:
- 点击逻辑顺序错误:先导出旧集合再添加新位置,导致存储的是未更新的数据
- 未同步更新模型状态:修改selectedPositions后没有更新PyqModel的selected属性,视图绑定会读取旧状态
- 依赖列表位置标记:列表排序、增删会导致位置失效,状态对应关系错乱
修改后的完整代码
public class PyqAdapter extends RecyclerView.Adapter<PyqAdapter.ViewHolder> { private final Context mCtx; private final List<PyqModel> pyqModelList; private final int defaultBackgroundColor; private final int selectedBackgroundColor; // 改用模型唯一ID存储已读状态,避免位置变化导致错误 private final Set<String> readItemIds; private static final String PREFS_NAME = "PyqAllItems"; private static final String READ_ITEMS_KEY = "PyqReadItems"; public PyqAdapter(Context mCtx, List<PyqModel> pyqModelList) { if (mCtx == null) { throw new IllegalArgumentException("Context cannot be null"); } this.mCtx = mCtx; this.pyqModelList = pyqModelList; this.readItemIds = new HashSet<>(); // 加载主题对应颜色 Resources res = mCtx.getResources(); int nightModeFlags = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) { defaultBackgroundColor = ContextCompat.getColor(mCtx, R.color.defaultBackgroundDark); selectedBackgroundColor = ContextCompat.getColor(mCtx, R.color.selectedBackgroundDark); } else { defaultBackgroundColor = ContextCompat.getColor(mCtx, R.color.defaultBackgroundLight); selectedBackgroundColor = ContextCompat.getColor(mCtx, R.color.selectedBackgroundLight); } // 从SharedPreferences加载已读状态 SharedPreferences prefs = mCtx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); Set<String> savedReadItems = prefs.getStringSet(READ_ITEMS_KEY, new HashSet<>()); readItemIds.addAll(savedReadItems); // 同步模型的已读状态 for (PyqModel model : pyqModelList) { model.setSelected(readItemIds.contains(model.getId())); } } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.pyq_rv_layout, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { final PyqModel temp = pyqModelList.get(position); // 设置文本内容 holder.textView.setText(temp.getPdfName()); holder.serialNumber.setText(String.valueOf(position + 1)); holder.pyqTopics.setText(temp.getPyqTopics()); // 根据模型状态设置背景色 holder.cardView.setCardBackgroundColor(temp.isSelected() ? selectedBackgroundColor : defaultBackgroundColor); holder.cardView.setOnClickListener(v -> { // 未标记已读时才更新状态 if (!temp.isSelected()) { temp.setSelected(true); readItemIds.add(temp.getId()); // 保存到SharedPreferences SharedPreferences prefs = mCtx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putStringSet(READ_ITEMS_KEY, readItemIds); editor.apply(); // 刷新当前项视图 notifyItemChanged(position); } // 跳转PDF页面 Intent i = new Intent(v.getContext(), ViewPdf.class); i.putExtra("pdfName", temp.getPdfName()); i.putExtra("pdfUrl", temp.getPdfUri()); v.getContext().startActivity(i); }); } @Override public int getItemCount() { return pyqModelList.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { CardView cardView; TextView textView, serialNumber, pyqTopics; public ViewHolder(View itemView) { super(itemView); cardView = itemView.findViewById(R.id.pyqCardView); textView = itemView.findViewById(R.id.pyqTitle); serialNumber = itemView.findViewById(R.id.serialNumber); pyqTopics = itemView.findViewById(R.id.pyqTopics); } } }
关键修改说明
- 用唯一ID替代列表位置:
需要在PyqModel类中添加getId()方法(返回每个PDF的唯一标识,比如文件名或服务器ID),避免列表数据变化导致状态错乱。示例PyqModel补充代码:public class PyqModel { private String id; private boolean isSelected; // 其他原有字段 public String getId() { return id; } public void setId(String id) { this.id = id; } public boolean isSelected() { return isSelected; } public void setSelected(boolean selected) { isSelected = selected; } // 其他原有getter/setter } - 修正点击逻辑顺序:
先更新模型状态和集合,再保存到SharedPreferences,确保存储的是最新数据。 - 简化视图绑定逻辑:
移除冗余的adapterPosition判断,直接使用onBindViewHolder的position参数,同步模型状态与视图显示。
内容的提问来源于stack exchange,提问作者Satish Bodke
相关产品推荐
相关产品推荐

