设置 我有一个带有自定义适配器和自定义 RecyclerViewViewHolder

我知道 onCreateViewHolder() 方法在 RecyclerView 用完 ViewHolders 以回收并需要一个新的时被调用。所以我只是在那里膨胀一个布局并将它传递给一个新的 ViewHolder.
此外,一旦 onBindViewHolder() 创建或回收了新的 ViewHolderViewHolder 就负责用数据填充 RecyclerView 。所以我在那里做的是调用我的方法 holder.setNode() 将数据对象传递给 ViewHolder

我看到的行为 当 Activity 首次启动时,所有条目都是正确的。然而,当我添加新条目或删除现有条目时,事情开始变得有点有趣。

  • title TextView 始终正确设置
  • 主布局的背景颜色似乎随意更改,我假设是因为 RecyclerView 正在重用旧的
  • 与我实现的自定义 View 一样,即使我使其无效并传递了新值,这会显着改变其外观

  • 所以我想知道:为什么一旦 View 被重用,onBindViewHolder() 中的这些值就不会改变?或者如果我错了,布局随机切换的真正原因是什么?

    任务列表适配器
    class TaskListAdapter extends RecyclerView.Adapter<TaskListAdapter.TaskViewHolder> {
    
    private ArrayList<NodeHandler.DbNode> dbNodeList = new ArrayList<>();
    ...
    
    @Override
    public TaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.small_task_view, parent, false);
        return new TaskViewHolder(v);
    }
    
    @Override
    public void onBindViewHolder(TaskViewHolder holder, int position) {
        final NodeHandler.DbNode dbNode = dbNodeList.get(position);
        holder.setNode(dbNode);
        holder.wrapper.findViewById(R.id.card_details).setVisibility(View.GONE);
    }
    
    ...
    
    public static class TaskViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {
        private FrameLayout wrapper;
        private TextView title;
        private NodeHandler.DbNode dbNode;
    
        public TaskViewHolder(View view) {
            ...
        }
    
        public void setTitle(String str) {
            title.setText(str);
        }
    
        public void setMarkers(@IntRange(from = 1, to = Node.MAX_URGENCY) int urgency, @IntRange(from = 1, to = Node.MAX_IMPORTANCE) int importance) {
            if(!dbNode.isAppointment()) {
                wrapper.setBackgroundColor(ContextCompat.getColor(wrapper.getContext(), R.color.lightGray));
            }
            ((QuadrantView) wrapper.findViewById(R.id.quadrant_view)).setDimensions(importance, urgency);
            // setDimensions will invalidate the view
        }
    
        public void setNode(NodeHandler.DbNode dbNodeObject) {
            this.dbNode = dbNodeObject;
            setTitle(dbNode.toString());
            setMarkers(dbNode.getUrgency(), dbNode.getImportance());
            setTips();
        }
    }
    }
    

    让我知道这里是否还有其他重要的事情。我很乐意相应地更新问题。

    最佳答案

    一旦 View 被重用,onBindViewHolder 中的值确实会发生变化。

    布局看似随机切换的真正原因是 onBindViewHolder 当前的实现方式假定 ViewHolder 是新创建的并且是第一次绑定(bind)。 onBindViewHolder 应该以假设被绑定(bind)的 ViewHolder 正在被重用的方式实现,因此它应该:

  • 先将 ViewHolder 的所有值重置为默认值,然后再将它们设置为其他值或
  • 确保所有内容都设置在 onBindViewHolder 中,因此人们无法判断它之前是否曾绑定(bind)到其他内容。

  • 随机背景颜色变化:

    您怀疑随机背景颜色问题是由 RecyclerView 重用 ViewHolder 引起的,这是正确的。

    发生这种情况的原因是因为以下代码:
        if(!dbNode.isAppointment()) {
            wrapper.setBackgroundColor(ContextCompat.getColor(wrapper.getContext(), R.color.lightGray));
        }
    

    如果 ViewHolder 不是约会,它只设置背景。因此,如果正在重用的 ViewHolder 以前不是用于约会,而是当前用于约会,则其背景颜色将不合适。

    要解决此问题,请执行以下任一操作:
  • 在执行 if 语句之前将 ViewHolder 的背景颜色设置为某种默认颜色(根据上面提到的解决方案 1):
    wrapper.setBackgroundColor(/* default background color */);
    if(!dbNode.isAppointment()) {
        wrapper.setBackgroundColor(ContextCompat.getColor(wrapper.getContext(), R.color.lightGray));
    }
    
  • 在 if 语句中添加 else 块,将 ViewHolder 的背景颜色设置为适当的颜色(按照上面提到的解决方案 2)
    if(!dbNode.isAppointment()) {
        wrapper.setBackgroundColor(ContextCompat.getColor(wrapper.getContext(), R.color.lightGray));
    }
    else
    {
        wrapper.setBackgroundColor(/* appointment background color */);
    }
    
  • 覆盖 RecyclerView.AdaptergetItemViewType 以基于 dbNode.isAppointment() 返回不同的 View 类型,并创建不同的 ViewHolder 子类来显示它们中的每一个

  • 附言我不知道关于自定义 View 的问题可能是什么......抱歉

    10-07 22:34