android - PagedListAdapter 将 newItem 作为 oldItem 传递给 DiffUtil.ItemCallback
问题描述
我在 Google 的 RoomWithAView 代码实验室中添加了一个复选框和分页库,并且对 DiffUtil.ItemCallback 的调用似乎将实体的更新版本传递给 oldItem 和 newItem 参数。
我的复选框选中状态由数据库中名为“isSelected”的布尔字段驱动,当单击该行时该字段会更新,这应该会导致复选框状态发生变化。
问题是当我更新“isSelected”字段时(例如从 false 到 true),以下日志打印对这两个项目都返回 true。我的复选框状态没有改变,因为 areContentsTheSame 返回 true 并且未调用 onBindViewHolder。我可以强制它返回 false,但我想了解出了什么问题:
private static DiffUtil.ItemCallback<WordEntity> DIFF_CALLBACK =
new DiffUtil.ItemCallback<WordEntity>() {
@Override
public boolean areItemsTheSame(WordEntity oldItem, WordEntity newItem) {
Log.i("CLEAN_LOG","areItemsTheSame: " +
Boolean.toString(oldItem.getWordId()==newItem.getWordId()));
return oldItem.getWordId() == newItem.getWordId();
}
@Override
public boolean areContentsTheSame(WordEntity oldItem, WordEntity newItem) {
Log.i("CLEAN_LOG","oldItem: " +
Boolean.toString(oldItem.getIsSelected()));
Log.i("CLEAN_LOG","newItem: " +
Boolean.toString(newItem.getIsSelected()));
Log.i("CLEAN_LOG","areContentsTheSame: " +
Boolean.toString(oldItem.getIsSelected() == newItem.getIsSelected()));
return oldItem.getIsSelected() == newItem.getIsSelected();
}
};
这是我的 PagedListAdapter:
public static class WordListAdapter extends PagedListAdapter<WordEntity, WordListAdapter.WordViewHolder> {
protected WordListAdapter() {
super(DIFF_CALLBACK);
setHasStableIds(true);
}
@NonNull
@Override
public WordViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_item, parent, false);
return new WordViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull WordViewHolder holder, int position) {
WordEntity current = getItem(position);
if (current != null) {
holder.bindTo(current);
}
}
private static DiffUtil.ItemCallback<WordEntity> DIFF_CALLBACK =
new DiffUtil.ItemCallback<WordEntity>() {
@Override
public boolean areItemsTheSame(WordEntity oldItem, WordEntity newItem) {
Log.i("CLEAN_LOG","areItemsTheSame: " +
Boolean.toString(oldItem.getWordId()==newItem.getWordId()));
return oldItem.getWordId() == newItem.getWordId();
}
@Override
public boolean areContentsTheSame(WordEntity oldItem, WordEntity newItem) {
Log.i("CLEAN_LOG","oldItem: " +
Boolean.toString(oldItem.getIsSelected()));
Log.i("CLEAN_LOG","newItem: " +
Boolean.toString(newItem.getIsSelected()));
Log.i("CLEAN_LOG","areContentsTheSame: " +
Boolean.toString(oldItem.getIsSelected() == newItem.getIsSelected()));
return oldItem.getIsSelected() == newItem.getIsSelected();
}
};
@Override
public long getItemId(int position) {
WordEntity current = getItem(position);
return current.mWordId;
}
class WordViewHolder extends RecyclerView.ViewHolder {
TextView wordItemView;
CheckBox checkBox;
LinearLayout viewForeground;
public void bindTo(WordEntity word) {
wordItemView.setText(word.mWord);
checkBox.setChecked(word.mIsSelected);
}
private WordViewHolder(View itemView) {
super(itemView);
viewForeground = itemView.findViewById(R.id.viewForeground);
wordItemView = itemView.findViewById(R.id.textView);
checkBox = itemView.findViewById(R.id.checkBox);
checkBox.setClickable(false);
viewForeground.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
final WordEntity thisWord = getItem(getAdapterPosition());
if (thisWord != null) {
Toast.makeText(context,
"You long-clicked: " + thisWord.getWord(),
Toast.LENGTH_LONG).show();
}
// returning false here will alow onClickListener to trigger as well
return true;
}
});
viewForeground.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final WordEntity thisWord = getItem(getAdapterPosition());
if (thisWord != null) {
if (thisWord.getIsSelected()) {
thisWord.setIsSelected(false);
} else {
thisWord.setIsSelected(true);
}
mWordViewModel.update(thisWord);
}
}
});
}
}
}
这是我的观察者:
mWordViewModel = ViewModelProviders.of(this).get(WordViewModel.class);
mWordViewModel.getAllWords().observe(this, new Observer<PagedList<WordEntity>>() {
@Override
public void onChanged(@Nullable final PagedList<WordEntity> words) {
// Update the cached copy of the words in the adapter.
adapter.submitList(words);
if (words != null) {
wordCount = words.size();
} else {
wordCount = 0;
}
Log.i(LOG_TAG,"Word Count: " + Integer.toString(wordCount));
}
});
- 所有 Room 数据库更新都正常进行
- 根据日志,当点击一行时,似乎 areItemsTheSame 被调用了两次,并且 areContentsTheSame 被调用了一次
我期待 oldItem.getIsSelected() 为假,new.Item.getIsSelected() 为真,然后 onBindViewHolder 将被解雇。我还希望 areItemsTheSame 和 areContentsTheSame 只为每个项目调用一次。
有人可以帮助我了解这里出了什么问题以及我的期望是否符合应该发生的情况吗?
这是我的示例应用程序的 GitHub: https ://github.com/DanglaGT/RoomWithAViewPaging
解决方案
我和你遇到了同样的程序,多亏了这个答案,我终于发现要使用Paging Library
,你需要确保PagedList
和它都是旧item
对象的新对象。
有我的解决方案:
if (thisWord.getIsSelected()) {
thisWord.setIsSelected(false);
} else {
thisWord.setIsSelected(true);
}
WordEntity newWord = copyNewObject(thisWord);
mWordViewModel.update(newWord);
推荐阅读
- python - 自定义词形还原词并附加到 WordNetLemmatizer
- sql - 尝试使用 dplyr 在 R 中的 SQL 数据库中选择/过滤数据
- mysql - 如何在 mysql 中使用一个选择查询在两列之间切换值?
- symfony - Symfony Doctrine SortBy ToMany 关系对象
- html - 如何使用 XPATH 在跨度类下创建所有文本的连接字符串
- java - 升级 Android Firebase 库会导致崩溃
- ios - 如何将带有图像数据的 CMSampleBuffer 转换为适合通过网络连接发送的格式?
- javascript - 在 Chrome 扩展程序中未定义 parentNode
- reactjs - MobX,Store 不可用,请确保它是由某些提供商提供的
- c# - C#:使用 PDFsharp 创建 PDF 表单 (AcroForm)