如何阻止我的 recyclerView 视图类型在滚动后发生变化?

How can I stop my recyclerView view type from changing after scrolling?

在我的 RecyclerView 适配器中 class 我有 2 种视图类型来显示我的查询结果:

 @Query("SELECT l.log_id, l.junction_id ,l.date, l.workout_id, l.total_weight_lifted,
         l.reps, l.set_number FROM log_entries_table 
         AS l LEFT JOIN exercise_workout_junction_table AS ej 
         ON ej.exercise_workout_id = l.junction_id WHERE ej.exercise_id = :exerciseID 
         ORDER BY substr(l.date, -4) DESC, substr(l.date, -7) DESC, (l.date) DESC")

    LiveData<List<Log_Entries>> getAllExerciseHistoryLogs(int exerciseID);

第一种视图类型用于显示日期唯一的所有日志条目:

第二种视图类型是显示与上述共享相同日期的其余日志条目:

我当前的代码工作正常,但是每次我向下滚动和 recyclerView 更新时,所有具有 'unique' 日期(应该使用第一个视图类型)的日志条目都会更改为显示第二个视图类型.

如何阻止我的 recyclerView 视图类型发生变化?

Before scroll -> After Scroll

RecyclerView 适配器


public class ExerciseHistoryAdapter2 extends RecyclerView.Adapter {

    private OnItemClickListener listener;
    private List<Log_Entries> allLogEntries = new ArrayList<>();
    private List<String> uniqueDates = new ArrayList<>();
    String logEntryDate;

    public void setExercises(List<Log_Entries> allLogEntries) {
        this.allLogEntries = allLogEntries;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View view;

        if (viewType == 0) {
            view = layoutInflater.inflate(R.layout.exercise_history_item, parent, false);
            return new ViewHolderOne(view);
        }

        view = layoutInflater.inflate(R.layout.exercise_history_item_two, parent, false);
        return new ViewHolderTwo(view);
    }

    @Override
    public long getItemId(int position) {

        return allLogEntries.get(position).getLog_id();
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        logEntryDate = allLogEntries.get(position).getDate();

        if (uniqueDates.contains(logEntryDate)) {
            // bindViewHolder2
            ViewHolderTwo viewHolderTwo = (ViewHolderTwo) holder;
            viewHolderTwo.textViewWeight.setText(String.valueOf(allLogEntries.get(position).getTotal_weight_lifted()));
            viewHolderTwo.textViewReps.setText(String.valueOf(allLogEntries.get(position).getReps()));

        } else {
            uniqueDates.add(logEntryDate);
            //bind viewholder1
            ViewHolderOne viewHolderOne = (ViewHolderOne) holder;
            viewHolderOne.textViewDate.setText(allLogEntries.get(position).getDate());
            viewHolderOne.textViewWeight.setText(String.valueOf(allLogEntries.get(position).getTotal_weight_lifted()));
            viewHolderOne.textViewReps.setText(String.valueOf(allLogEntries.get(position).getReps()));
        }
    }

    @Override
    public int getItemCount() {
        return allLogEntries.size();
    }

 @Override
    public int getItemViewType(int position) {

        logEntryDate = allLogEntries.get(position).getDate();

        if (uniqueDates.contains(logEntryDate)) {
            return 1;
        }
        return 0;
    }

    class ViewHolderOne extends RecyclerView.ViewHolder {
        private TextView textViewDate;
        private TextView textViewWeight;
        private TextView textViewReps;

        public ViewHolderOne(@NonNull View itemView) {
            super(itemView);

            textViewDate = itemView.findViewById(R.id.textView_dateH);
            textViewWeight = itemView.findViewById(R.id.textView_weightH);
            textViewReps = itemView.findViewById(R.id.textView_repss);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = getAdapterPosition();
                    if (listener != null && position != RecyclerView.NO_POSITION) {
                        listener.onItemClick(allLogEntries.get(position));
                    }
                }
            });
        }
    }

    class ViewHolderTwo extends RecyclerView.ViewHolder {
        private TextView textViewWeight;
        private TextView textViewReps;

        public ViewHolderTwo(@NonNull View itemView) {
            super(itemView);
            textViewWeight = itemView.findViewById(R.id.textView_weightH2);
            textViewReps = itemView.findViewById(R.id.textView_repss2);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = getAdapterPosition();
                    if (listener != null && position != RecyclerView.NO_POSITION) {
                        listener.onItemClick(allLogEntries.get(position));
                    }
                }
            });

        }
    }

    public interface OnItemClickListener {
        void onItemClick(Log_Entries log_entries);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }
}

我认为设置 holder.setIsRecyclable(false); 可以解决问题,因为回收器视图将不再回收项目...但这对于长列表来说不是一个好的解决方案。

编辑: 我在 onBindViewHolder() 中查看了您的代码... 我认为问题出在 uniqueDates.add(logEntryDate); 并且 onBindViewHolder 方法被多次调用。

回收站视图是这样处理的:

  1. 列表中的第一项将是唯一的,因为 uniqueDates 是空的。因此它将被添加到列表中。
  2. 其他项目将正确添加,如您在第一个屏幕截图中所见
  3. 当您向下滚动时,onBindViewHolder 方法将再次为每个项目执行
  4. 因为 uniqueDates 列表已经包含第一个日期,因为它是在第一步中添加的,所以现在此项将被识别为非唯一日期
  5. 滚动后将显示错误的列表,如您在第二个屏幕截图中所见

解决方案: 您将不得不添加一个以另一种方式标识唯一日期的逻辑,该逻辑独立于 onBindViewholder 方法

您必须添加代码,删除特定点的日期,以便列表每次都将第一项标识为唯一的,而不仅仅是第一次。

您的 getItemViewTypeonBindViewHolder 有一些问题。

@Override
public int getItemViewType(int position) {
    // type 0 = with date header
    // type 1 = without date header

    // if list is sorted chronologically
    if (position == 0) {
        return 0
    }

    String currentDate = allLogEntries.get(position).getDate();
    String previousDate = allLogEntries.get(position - 1).getDate();

    if (currentDate.equals(previousDate)) {
        return 1
    } else {
        return 0
    }
}

第一项将始终为 header,因为列表是按时间顺序排序的。对于其余项目,您需要检查当前项目的日期是否与上一个项目的日期相同。基于那个条件你 return 类型。

您不需要管理唯一日期列表。您在多次调用且未同步的函数之间共享了可变状态。只需从 onBindViewHolder

中删除这些及其引用
private List<String> uniqueDates = new ArrayList<>();
String logEntryDate;