Какой метод DiffUtil подходит для перерисовки одного измененного элемента
Что проверяют
Знаете ли вы, как DiffUtil решает, что элемент изменился, и как обновить в ячейке только изменённую часть без полной перепривязки и мигания. Ждут ответ getChangePayload в паре с onBindViewHolder с payloads. Следом часто спрашивают, почему submitList иногда ничего не обновляет.
Как отвечать
Разложите по шагам, какой метод за что отвечает:
areItemsTheSame: тот же ли это элемент. Сравнивают стабильный идентификатор, обычноid, а не весь объект;areContentsTheSame: вызывается, только когда первый вернулtrue. Если здесьfalse, элемент считается изменённым и получаетnotifyItemChanged;getChangePayload: вызывается для изменённых элементов. По умолчанию возвращаетnull, и тогда ячейка перепривязывается целиком, аDefaultItemAnimatorпроигрывает кроссфейд. Если вернуть объект с описанием изменения, напримерLikeChanged(count), он приедет в адаптер.
В адаптере переопределяется перегрузка onBindViewHolder(holder, position, payloads). Если payloads пуст, делайте полную привязку. Если нет, обновите только нужное: счётчик, иконку, прогресс. Это список, потому что до следующего прохода раскладки изменений может накопиться несколько. Бонус: с непустым payload аниматор переиспользует тот же ViewHolder, и мигание пропадает.
В современном коде обычно берут не голый DiffUtil.Callback, а ListAdapter с DiffUtil.ItemCallback: те же три метода, но разница считается в фоновом потоке через AsyncListDiffer. Если изменение одно и позиция известна, можно обойтись без DiffUtil и вызвать notifyItemChanged(position, payload) напрямую.
Где спотыкаются
Мутируют существующий список и передают в submitList тот же экземпляр. ListAdapter видит ту же ссылку и ничего не сравнивает, экран не обновляется. Передавать нужно новый список.
В areItemsTheSame сравнивают объекты целиком. Любая правка превращается в удаление и вставку, прокрутка прыгает, анимация выглядит как замена элемента.
Возвращают payload, но не переопределяют трёхаргументный onBindViewHolder. Реализация по умолчанию вызывает обычный onBindViewHolder, payload молча теряется, и ячейка всё равно привязывается заново целиком.