onBindViewHolder(VH, position) 在使用 notifyItemChanged() 时不正确地调用旧的和新的 ViewHolder 副本

onBindViewHolder(VH, position) improperly calling the old and new ViewHolder copies when using notifyItemChanged()

EDIT2: 我在 holder.showTaskRecyclerViewfalse 的情况下明确表示要隐藏视图,以便明确地覆盖两者 trueonBindViewHolder 中的 false 个案例。我仍然有同样的问题。

EDIT1: 我应该补充一点,如果我使用 notifyDataSetChanged() 而不是 notifyItemChanged()tasksRecyclerView 的显示和隐藏效果很好,但这会禁用动画并且成本更高。

我的 ViewHolder 中有一个名为 tasksRecyclerView 的 RecyclerView,它应该在单击视图时显示和隐藏。 (这是主 RecyclerView 内部的 RecyclerView,可以这么说。):

 public class ViewHolder extends RecyclerView.ViewHolder {
    public ImageView routineStateImgView;
    public TextView alarmTextView;
    public TextView routineNameTextView;
    public RecyclerView tasksRecyclerView;
    public boolean showTaskRecyclerView = false;

    public ViewHolder(View routineItemView){
        super(routineItemView);

        routineItemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //toggle showing TaskRecyclerView
                showTaskRecyclerView = !showTaskRecyclerView;
                notifyItemChanged(getAdapterPosition());
            }
        });
        routineStateImgView = (ImageView) routineItemView.findViewById(R.id.routineStateImgView);
        alarmTextView = (TextView) routineItemView.findViewById(R.id.alarmTextView);
        routineNameTextView = (TextView) routineItemView.findViewById(R.id.routineNameTextView);
        tasksRecyclerView = (RecyclerView) routineItemView.findViewById(R.id.tasksRecyclerView);
    }

    public ImageView getRoutineStateImgView() {
        return routineStateImgView;
    }
    public TextView getAlarmTextView(){
        return alarmTextView;
    }
    public TextView getRoutineNameTextView(){
        return routineNameTextView;
    }
    public RecyclerView getTasksRecyclerView(){
        return tasksRecyclerView;
    }
}

目前代码中的 onClick 设置为切换 ViewHolder 中定义的变量 (showTaskRecyclerView),然后调用 notifyItemChanged(getAdapterPosition()) 以使用动画更新项目更改。

调用 OnBindViewHolder 时,它会检查切换变量的状态(showTaskRecyclerView,再次)并根据变量隐藏或取消隐藏 RecyclerView。您可以在代码中的 Log.d 方法行之后的代码中看到此逻辑:

public void onBindViewHolder(RoutinesRecyclerViewAdapter.ViewHolder holder, int position) {
    //Replace contents of view with data from item in adapterRoutinesList at position.
    Routine routine = adapterRoutinesList.get(position);
    TasksRecyclerViewAdapter mTasksRecyclerViewAdapter = null;

    if(routine == null){
        holder.getRoutineStateImgView().setImageResource(android.R.color.transparent);
        holder.getAlarmTextView().setText("");
        holder.getRoutineNameTextView().setText("");
        holder.getTasksRecyclerView().setVisibility(View.GONE);
    }else{
        DateFormat dayTimeFormat = new SimpleDateFormat("HH:mm", Locale.US);
        String dayTimeString = dayTimeFormat.format(routine.getWakeupTime().getTime());
        holder.getAlarmTextView().setText(dayTimeString);
        holder.getRoutineNameTextView().setText(routine.getName());
        if(routine.getEnableRoutine()) {
            holder.getRoutineStateImgView().setImageResource(R.drawable.check_mark);
        }
        else{
            holder.getRoutineStateImgView().setImageResource(R.drawable.x_mark);
        }

        Log.d(TAG, "position: " + position + " holder: " + holder + " show?: " + holder.showTaskRecyclerView);
        if(holder.showTaskRecyclerView) {
            TasksManager mTasksManager = adapterTasksListCache.get(routine.getId());
            if (mTasksManager == null) {
                mTasksManager = new TasksManager(routine.getId());
                mTasksManager.readTasksFromDisk(mContext);
                adapterTasksListCache.put(routine.getId(), mTasksManager);
            }

            mTasksRecyclerViewAdapter = adapterTasksViewCache.get(routine.getId());
            if (mTasksRecyclerViewAdapter == null) {
                mTasksRecyclerViewAdapter = new TasksRecyclerViewAdapter(
                        mContext,
                        position,
                        mTasksManager.getTasksListFromCache(),
                        mRequestImageCaptureCallBack,
                        mTaskEditTextListener);
                adapterTasksViewCache.put(routine.getId(), mTasksRecyclerViewAdapter);
            }
            RecyclerView.LayoutManager mTaskLayoutManager = new LinearLayoutManager(mContext);
            holder.getTasksRecyclerView().setLayoutManager(mTaskLayoutManager);
            holder.getTasksRecyclerView().setAdapter(mTasksRecyclerViewAdapter);
            holder.getTasksRecyclerView().setHasFixedSize(true);
            holder.getTasksRecyclerView().setVisibility(View.VISIBLE);
        }
        else{
            holder.getTasksRecyclerView().setVisibility(View.GONE);
        }
    }
}

我希望看到 RecyclerView 在每次点击时隐藏或取消隐藏。但是我只在每点击 2 次后看到 hide/unhide 行为。

这是点击 5 次后 Log.d 的输出:

04-16 21:33:37.026 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{263b1503 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: false
04-16 21:33:42.626 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{883eb98 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: false
04-16 21:33:44.511 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{263b1503 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: true
04-16 21:33:46.274 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{883eb98 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: true
04-16 21:33:47.938 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{263b1503 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: false
04-16 21:41:52.756 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{883eb98 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: false

我的解释是,使用 notifyItemChanged 会提示使用同一 ViewHolder 的 2 个副本来实现过渡动画目的(一个具有视图的旧状态,一个具有新状态)。但是,带有变量的副本在旧副本中发生了错误的更改。我假设我对 notifyItemChanged 的​​工作原理有错误的理解,所以我来这里寻求帮助:/

此外,我不知道是什么原因导致我点击得非常快:如果我点击得足够快(大约不到半秒),我看不到任何变化

logcat 显示如下:

04-16 21:43:54.597 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{883eb98 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: true
04-16 21:43:54.862 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{263b1503 position=0 id=-1, oldPos=-1, pLpos:-1 scrap [changeScrap] tmpDetached not recyclable(1) no parent} show?: true
04-16 21:43:55.387 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{883eb98 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: true
04-16 21:43:55.668 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{263b1503 position=0 id=-1, oldPos=-1, pLpos:-1 scrap [changeScrap] tmpDetached not recyclable(1) no parent} show?: true
04-16 21:43:55.949 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{883eb98 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: true
04-16 21:43:56.189 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{263b1503 position=0 id=-1, oldPos=-1, pLpos:-1 scrap [changeScrap] tmpDetached not recyclable(1) no parent} show?: true
04-16 21:43:56.479 13522-13522/co.edu.javeriana.faros D/RoutinesViewAdapter: position: 0 holder: ViewHolder{883eb98 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} show?: true

有什么想法吗?

您需要实现 holder.showTaskRecyclerViewfalse 时的条件逻辑。

View 保持它们的状态,因此您必须显式修改它们。将一些逻辑隐藏在那里并检查它是否有效。

我通过在 ViewHolder 之外定义一个 ListArray 并为适配器中的每个项目定义一个 showTaskRecyclerView 来解决这个问题。

这样,旧的和新的 ViewHolder 看起来是一样的,每个项目单个 showTaskRecyclerView 而不是 2 个不同步的 showTaskRecyclerView 每个项目都位于两个 [=11] =].