PagedListAdapter не обновляет список, если меняется только содержимое элемента
Я использую библиотеки 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))
Моя 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)
}
Все работает при вставке, удалении или просто загрузке категорий. Но когда я обновляю цвет или текст отдельной сущности, список не обновляется, хотя список отправки вызывается правильно.
Я отладил весь процесс и обнаружил проблему: после отправки списка, AsyncPagedListDiffer#submitList
называется. Я сравнил предыдущий список (mPagedList
в AsyncPagedListDiffer
) и новый список (pagedList
в AsyncPagedListDiffer#submitList
). Элементы, которые я там редактировал, равны и уже содержат новые данные. Так DiffUtil
сравнивает все и элементы уже равны, хотя отображаемый список не обновляется.
Если список является справочным, это объясняет, почему данные уже обновлены в списке адаптеров, но как мне решить проблему?
3 ответа
Я думаю, что проблема не в том, как вы загружаете новые данные, а в обновлении данных. Хотя вы еще не показали нам ту часть, где вы запускаете обновление элемента или как происходит фактическое обновление, я полагаю, извините, если я ошибся, возможно, вы непосредственно редактировали элемент списка следующим образом:
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)
Идея создания совершенно нового свежего объекта каждый раз, когда вы хотите внести даже малейшее изменение, поначалу может показаться не столь очевидной, но неизменность является ключевой частью функционально-реактивного программирования, и многие реактивные компоненты полагаются на неизменность. обрабатываемых данных.
Что мне нравится делать, чтобы избежать такой ошибки, я всегда делаю каждое поле в своем классе данных окончательным.
@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
)
Никто не сможет ответить на ваш вопрос, если вы не покажете свой класс Dao и pagedlistadapter, который содержит DiffUtill.itemcallaback. Я покажу вам некоторый код, который может помочь.
вы должны реализовать обновление в вашем интерфейсе DAO следующим образом:
@Update fun updateUsers (данные: MyData)
если у вас есть этот метод после этого, вы проверяете ваш diffcall, как показано ниже:
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.
Я сделал RnD для PagedListAdapter и пользовательский пейджинг с базой данных Room. Нажмите здесь, и вы найдете мою реализацию. Надеюсь, что это поможет вам.
Благодарю.