如何阻止我的 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
方法被多次调用。
回收站视图是这样处理的:
- 列表中的第一项将是唯一的,因为
uniqueDates
是空的。因此它将被添加到列表中。
- 其他项目将正确添加,如您在第一个屏幕截图中所见
- 当您向下滚动时,
onBindViewHolder
方法将再次为每个项目执行
- 因为
uniqueDates
列表已经包含第一个日期,因为它是在第一步中添加的,所以现在此项将被识别为非唯一日期
- 滚动后将显示错误的列表,如您在第二个屏幕截图中所见
解决方案:
您将不得不添加一个以另一种方式标识唯一日期的逻辑,该逻辑独立于 onBindViewholder
方法
或
您必须添加代码,删除特定点的日期,以便列表每次都将第一项标识为唯一的,而不仅仅是第一次。
您的 getItemViewType
和 onBindViewHolder
有一些问题。
@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;
在我的 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
方法被多次调用。
回收站视图是这样处理的:
- 列表中的第一项将是唯一的,因为
uniqueDates
是空的。因此它将被添加到列表中。 - 其他项目将正确添加,如您在第一个屏幕截图中所见
- 当您向下滚动时,
onBindViewHolder
方法将再次为每个项目执行 - 因为
uniqueDates
列表已经包含第一个日期,因为它是在第一步中添加的,所以现在此项将被识别为非唯一日期 - 滚动后将显示错误的列表,如您在第二个屏幕截图中所见
解决方案:
您将不得不添加一个以另一种方式标识唯一日期的逻辑,该逻辑独立于 onBindViewholder
方法
或
您必须添加代码,删除特定点的日期,以便列表每次都将第一项标识为唯一的,而不仅仅是第一次。
您的 getItemViewType
和 onBindViewHolder
有一些问题。
@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;