【问题标题】:PagedListAdapter does not update list if just the content of an item changes如果只是项目的内容发生变化,PagedListAdapter 不会更新列表
【发布时间】:2019-02-02 14:00:36
【问题描述】:

我正在使用 Room 和 Paging 库来显示类别。

我的实体:

@Entity(tableName = Database.Table.CATEGORIES)
data class Category(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = ID) var id: Long = 0,
    @ColumnInfo(name = NAME) var name: String = "",
    @ColumnInfo(name = ICON_ID) var iconId: Int = 0,
    @ColumnInfo(name = COLOR) @ColorInt var color: Int = DEFAULT_COLOR
)

我的 DAO:

@Query("SELECT * FROM $CATEGORIES")
fun getPagedCategories(): DataSource.Factory<Int, Category>

@Update
fun update(category: Category)

我的回购:

val pagedCategoriesList: LiveData<PagedList<Category>> = categoryDao.getPagedCategories().toLiveData(Config(CATEGORIES_LIST_PAGE_SIZE))

我的视图模型:

val pagedCategoriesList: LiveData<PagedList<Category>>
    get() = repository.pagedCategoriesList

我的适配器:

class CategoriesAdapter(val context: Context) : PagedListAdapter<Category, CategoriesAdapter.CategoryViewHolder>(CategoriesDiffCallback()) {

    //region Adapter

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
        return CategoryViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_category, parent, false))
    }

    override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
        holder.bind(getItem(position)!!)
    }

    //endregion

    //region Methods

    fun getItemAt(position: Int): Category = getItem(position)!!

    //endregion

    inner class CategoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        private val iconHelper = IconHelper.getInstance(context)

        fun bind(category: Category) {
            with(itemView) {
                txvCategoryItemText.text = category.name
                imvCategoryItemIcon.setBackgroundColor(category.color)
                iconHelper.addLoadCallback {
                    imvCategoryItemIcon.setImageDrawable(iconHelper.getIcon(category.iconId).getDrawable(context))
                }
            }
        }
    }

    class CategoriesDiffCallback : DiffUtil.ItemCallback<Category>() {

        override fun areItemsTheSame(oldItem: Category, newItem: Category): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Category, newItem: Category): Boolean {
            return oldItem == newItem
        }
    }
}

还有我的片段:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    categoryViewModel = ViewModelProviders.of(this).get(CategoryViewModel::class.java)

    adapter = CategoriesAdapter(requireContext())
    categoryViewModel.pagedCategoriesList.observe(this, Observer(adapter::submitList))
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    ViewCompat.setTooltipText(fabNewCategory, getString(R.string.NewCategory))

    with(mRecyclerView) {
        layoutManager = GridLayoutManager(requireContext(), 4)
        itemAnimator = DefaultItemAnimator()
        addItemDecoration(SpacesItemDecoration(resources.getDimensionPixelSize(R.dimen.card_default_spacing)))

        addOnItemTouchListener(OnItemTouchListener(requireContext(), this, this@CategoriesFragment))
    }

    mRecyclerView.adapter = adapter

    fabNewCategory.setOnClickListener(this)
}

在插入、删除或加载类别时一切正常。 但是当我更新单个实体的颜色或文本时,列表没有更新,尽管提交列表被正确调用。

我调试了整个过程,发现了问题: 提交列表后,调用AsyncPagedListDiffer#submitList。我比较了之前的列表(AsyncPagedListDiffer 中的mPagedList)和新列表(AsyncPagedListDiffer#submitList 中的pagedList)。我在那里编辑的项目是相同的,并且已经保存了新数据。所以DiffUtil 比较所有内容,尽管显示的列表没有更新,但项目已经相等。

如果列表是参考,它会解释为什么适配器列表中的数据已经刷新,但是我该如何解决这个问题呢?

【问题讨论】:

  • 您能分享一下您的CategoriesAdapter 实现吗?你是如何实现DiffUtil.ItemCallback的?
  • @SanlokLee 查看我的编辑

标签: android kotlin android-recyclerview android-adapter android-paging


【解决方案1】:

我认为问题不在于您加载新数据的方式,而在于更新数据。虽然您没有向我们展示您触发项目更新的部分或实际更新是如何发生的,但我猜,如果我错了,抱歉,您可能会像这样直接编辑列表元素:

category = adapter.getItemAt(/*item position*/)
category.name = "a new name"
category.color = 5
categoryViewModel.update(category)


相反,您应该创建一个新的 Category 对象,而不是修改现有对象,如下所示:

prevCategory = adapter.getItemAt(/*put position*/) // Do not edit prevCategory!
newCategory = Category(id=prevCategory.id, name="a new name", color=5, iconId=0)
categoryViewModel.update(newCategory)


每次您想要进行最小的更改时创建一个全新的对象的想法一开始可能并不那么明显,但是这种反应式实现依赖于每个事件独立于其他事件的假设。使您的数据类不可变或有效不可变将防止此问题。

为了避免这种错误,我喜欢做的是,我总是将我的数据类中的每个字段都设为 final。

@Entity(tableName = Database.Table.CATEGORIES)
data class Category(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = ID) val id: Long = 0,
    @ColumnInfo(name = NAME) val name: String = "",
    @ColumnInfo(name = ICON_ID) val iconId: Int = 0,
    @ColumnInfo(name = COLOR) @ColorInt val color: Int = DEFAULT_COLOR
)

【讨论】:

  • 嗯,就是这样.. 非常感谢!
  • 我有一个类似的问题,我尝试修复了几天。我知道这个问题与引用与对象本身有关,但无论我尝试什么,它都不起作用(包括制作对象的副本)......不知何故,我不明白这是如何工作的!
  • 解决方案不合适。我们想将 ListAdapter 与 LiveData 一起使用。当 liveData 即将用新的更新列表通知适配器时,第二次更新列表项有什么意义。这只是一个被黑的解决方案。
  • 抱歉,我还是不明白为什么在调用“submitList”之前 ListAdapter 已经有了全新的列表。
  • 这里是凌晨 2:30,一直在尝试解决这个问题至少 6 小时,这正是它,事后看来,这是有道理的,因为当前对象保存在当前正在使用的列表中,这意味着新旧列表之间的任何比较都将包含新列表中存在的旧列表中的更改。
【解决方案2】:

除非您显示包含 DiffUtill.itemcallaback 的 Dao 和 pagedlistadapter 类,否则没有人能够回答您的问题。 我给你看一些可能有帮助的代码。

  1. 你必须像这样在你的 DAO 接口中实现更新:

    @更新 有趣的 updateUsers(data: MyData)

如果你有这个方法之后你检查你的差异调用,如下所示:

companion object {
    val videosDiffCallback = object : DiffUtil.ItemCallback<Item>(){
        override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
            return oldItem.id == newItem.id //Called to decide whether two objects(new and old items) represent the same item.
        }

        override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
            return oldItem == newItem //Called to decide whether two items have the same data.
        }
    }
}
//oldItem   Value: The item in the old list.
//newItem   Value: The item in the new list.

【讨论】:

  • 对不起,我认为更新 dao 并不重要,因为它是标准房间代码。请看看我的编辑!
【解决方案3】:

我已经在 PagedListAdapter 上进行了 RnD,并使用 Room 数据库进行了自定义分页。 点击here,你会发现我的实现。希望这会对你有所帮助。

谢谢。

【讨论】:

    猜你喜欢
    • 2021-10-23
    • 2012-06-17
    • 2017-11-11
    • 1970-01-01
    • 2022-07-13
    • 1970-01-01
    • 2017-09-28
    • 2021-08-14
    • 1970-01-01
    相关资源
    最近更新 更多