PagedListAdapter не обновляет список, если меняется только содержимое элемента - PullRequest
0 голосов
/ 02 февраля 2019

Я использую библиотеки Room и Paging для отображения категорий.

My Entity:

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

My DAO:

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

@Update
fun update(category: Category)

My Repo:

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 ]

0 голосов
/ 03 февраля 2019

Я думаю, что проблема не в том, как вы загружаете новые данные, а в обновлении данных.Хотя вы еще не показали нам ту часть, где вы запускаете обновление элемента или как происходит фактическое обновление, я полагаю, извините, если я ошибся, возможно, вы непосредственно редактировали элемент списка следующим образом:

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
)
0 голосов
/ 05 февраля 2019

Я сделал RnD для PagedListAdapter и пользовательский пейджинг с базой данных Room.Нажмите здесь , и вы найдете мою реализацию.Надеюсь, это поможет вам.

Спасибо.

0 голосов
/ 03 февраля 2019

Никто не сможет ответить на ваш вопрос, если вы не покажете свой класс Dao и pagedlistadapter, который содержит DiffUtill.itemcallaback.Я покажу вам некоторый код, который может помочь.

  1. вам необходимо реализовать обновление в интерфейсе DAO следующим образом:

    @ Update fun 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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...