Как сделать так, чтобы при изменении состояния в RecyclerView перерисовывалось только нужное View, а не вся ячейка
Что проверяют
Знаете ли вы частичное обновление ячейки через payload, а не только notifyItemChanged(position). Следом обычно просят показать, как то же самое сделать через ListAdapter и DiffUtil.
Как отвечать
Обычный notifyItemChanged(position) заново вызывает onBindViewHolder для всей ячейки: перепривязываются все поля, заново грузится картинка, а DefaultItemAnimator проигрывает анимацию смены с миганием. Чтобы тронуть одно View, в уведомление передают payload:
notifyItemChanged(position, payload): payload это любой объект, например константаLIKE_CHANGEDили набор изменённых полей;onBindViewHolder(holder, position, payloads): перегрузка с третьим параметром. Еслиpayloadsне пуст, обновляете только нужное, скажем иконку лайка. Если пуст, вызываете полную привязку. Несколько payload до следующего прохода разметки приходят одним списком;DiffUtil.ItemCallback.getChangePayload(oldItem, newItem): при работе черезListAdapterиsubmitListвы сравниваете старый и новый элемент и возвращаете, что именно поменялось. Дальше адаптер сам уведомит ячейку с этим payload;- анимация: при непустом payload аниматор переиспользует тот же ViewHolder и не делает кроссфейд. Если мигание мешает и без этого, отключают
supportsChangeAnimationsуSimpleItemAnimator.
В areItemsTheSame сравнивается идентификатор, в areContentsTheSame содержимое, поэтому модели должны быть неизменяемыми, удобнее всего data class с copy().
Для Compose хватит одной фразы: там перерисовкой управляет рекомпозиция, и при стабильных параметрах и заданном key в LazyColumn пересчитывается только изменившаяся часть.
Где спотыкаются
Переопределяют только двухаргументный onBindViewHolder. Тогда payload молча игнорируется, и ячейка всё равно биндится целиком.
Меняют поле прямо в объекте, который уже лежит в списке, и отдают в submitList тот же список или его копию с теми же объектами. DiffUtil сравнивает объект сам с собой, не видит разницы, и экран не обновляется.
Вызывают notifyDataSetChanged() на любое изменение. Это перепривязка всех видимых ячеек без анимаций, и lint не зря помечает такой вызов предупреждением.