如果只是项目的内容更改,PagedListAdapter不会更新列表 [英] PagedListAdapter does not update list if just the content of an item changes

查看:120
本文介绍了如果只是项目的内容更改,PagedListAdapter不会更新列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using the Room and Paging libraries to display categories.

我的实体:

@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))

我的ViewModel:

My ViewModel:

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)
}

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

Everything works when inserting, deleting or just loading categories. But when I'm updating a single entity's color or text, the list is not updated, though submit list is called correctly.

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

I debugged the whole process and found the problem: After submitting the list, AsyncPagedListDiffer#submitList is called. I compared the previous list (mPagedList in AsyncPagedListDiffer) and the new list (pagedListin AsyncPagedListDiffer#submitList). The items I edited there are equal and do already hold the new data. So DiffUtil compares everything and the items are already equal though the displayed list is not updated.

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

If the list is a reference, it would explain why the data is already refreshed in the adapters list, but how do I solve the issue then?

推荐答案

我认为问题不是您加载新数据的方式,而是更新数据的方式.尽管您尚未向我们展示触发项目更新的部分或实际更新的发生方式,但我猜很抱歉,如果我输入错了,您可能会像这样直接编辑列表元素:

I think the problem is not the way you are loading the new data, but updating the data. Although you haven't show us the part where you triggers item update or how the actual update happens, I am guessing, sorry if I was wrong, you might be directly editting the list element like this:

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


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


Instead, you should create a new Category object instead of modifying existing ones, like this:

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)


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


The idea of creating a whole new fresh object every time you want to make even the smallest change is something might not be so obvious at first, but this reactive implementation relies on the assumption that each event is independent of other events. Making your data class immutable, or effectively immutable will prevent this issue.

为了避免这种错误,我总是想做些什么,我总是将数据类中的每个字段都设为final.

What I like to do to avoid this kind of mistake, I always make every field in my data class 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
)

这篇关于如果只是项目的内容更改,PagedListAdapter不会更新列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆