首页 > 解决方案 > 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));
        }
    });

我期待 oldItem.getIsSelected() 为假,new.Item.getIsSelected() 为真,然后 onBindViewHolder 将被解雇。我还希望 areItemsTheSame 和 areContentsTheSame 只为每个项目调用一次。

有人可以帮助我了解这里出了什么问题以及我的期望是否符合应该发生的情况吗?

这是我的示例应用程序的 GitHub: https ://github.com/DanglaGT/RoomWithAViewPaging

标签: androidandroid-recyclerviewandroid-roomandroid-paging

解决方案


我和你遇到了同样的程序,多亏了这个答案,我终于发现要使用Paging Library,你需要确保PagedList和它都是旧item 对象的新对象

有我的解决方案:

 if (thisWord.getIsSelected()) {
     thisWord.setIsSelected(false);
 } else {
     thisWord.setIsSelected(true);
 }

WordEntity newWord =  copyNewObject(thisWord);
mWordViewModel.update(newWord);

推荐阅读