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

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);
        }
    }
}
关键修改说明
  1. 用唯一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
    }
    
  2. 修正点击逻辑顺序:
    先更新模型状态和集合,再保存到SharedPreferences,确保存储的是最新数据。
  3. 简化视图绑定逻辑:
    移除冗余的adapterPosition判断,直接使用onBindViewHolder的position参数,同步模型状态与视图显示。

内容的提问来源于stack exchange,提问作者Satish Bodke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:41:01