Как обновить одну вьюшку в списке RecyclerView при изменении данных
Что проверяют
Знаете ли вы точечные уведомления адаптера вместо notifyDataSetChanged() и понимаете ли, как на них реагирует RecyclerView: перепривязка, анимация, переиспользование холдеров. Следом спрашивают про payload и про ListAdapter с DiffUtil.
Как отвечать
Базовый путь: изменить элемент в данных адаптера и вызвать notifyItemChanged(position). Список перепривяжет только эту позицию через onBindViewHolder, остальные строки не тронет.
Что отличает хороший ответ:
- payload:
notifyItemChanged(position, payload)приходит в перегрузкуonBindViewHolder(holder, position, payloads). Еслиpayloadsне пуст, обновляете одно поле, например счётчик лайков, а не всю строку с картинкой. ЗаодноDefaultItemAnimatorпереиспользует тот же холдер и не играет перекрёстное затухание, из-за которого строка «моргает»; ListAdapterиDiffUtil: позиции не считаются руками. Вы описываетеDiffUtil.ItemCallbackсareItemsTheSame(по id) иareContentsTheSame(по содержимому), при желанииgetChangePayload, и отдаёте новый список вsubmitList. Разница считается в фоновом потоке, нужныеnotifyItem*адаптер вызывает сам;- неизменяемые данные: элементы делают
data class, изменённый получают черезcopy(), вsubmitListпередают новый список; - позиция из клика: берут
bindingAdapterPositionв момент клика и проверяют наRecyclerView.NO_POSITION. Позиция, запомненная при привязке, к клику может устареть.
Где спотыкаются
Вызывают notifyDataSetChanged() на каждое изменение. Пропадают анимации, перепривязывается весь видимый экран, а lint прямо предупреждает об этом.
Мутируют объект внутри списка и передают в submitList тот же экземпляр списка. ListAdapter видит ту же ссылку и ничего не делает, а если объект общий, DiffUtil сравнивает его сам с собой и разницы не находит.
Меняют вьюшку напрямую через findViewHolderForAdapterPosition. При прокрутке холдер переиспользуется, и правка пропадает или всплывает в чужой строке.