Ответ 1
После некоторого случайного толчка я нашел решение. Вызовите notifyItemChanged
на вашем адаптере. Это заставит выведенный вид вернуться в исходное положение.
Есть ли способ вернуть действие салфетки и восстановить держатель вида в исходное положение после, а салфетка завершена, а onSwiped
вызывается в экземпляре ItemTouchHelper.Callback
? Я получил экземпляры RecyclerView
, ItemTouchHelper
и ItemTouchHelper.Callback
, чтобы работать вместе отлично, мне просто нужно отменить действие салфетки и не удалить поврежденный элемент в некоторых случаях.
После некоторого случайного толчка я нашел решение. Вызовите notifyItemChanged
на вашем адаптере. Это заставит выведенный вид вернуться в исходное положение.
Реализация Google ItemTouchHelper
предполагает, что элемент each, который будет удаляться, в конечном итоге удаляется из представления recycler, в некоторых случаях это может быть не так.
RecoverAnimation
является вложенным классом в ItemTouchHelper
, который управляет анимацией касания элементов подталкивания/перетаскивания. Хотя название подразумевает, что только восстанавливает позицию элементов, это фактически единственный класс, который используется для восстановления (отмена салфетки/перетаскивания) и заменить (вывести на экран или заменить на перетаскивание). Странное имя.
Здесь существует логическое свойство с именем mIsPendingCleanup
в RecoverAnimation
, которое ItemTouchHelper
использует, чтобы выяснить, находится ли элемент в ожидании удаления. Итак, ItemTouchHelper
, после прикрепления элемента RecoverAnimation
к элементу, устанавливает это свойство после успешного удаления, и анимация не удаляется из списка анимаций восстановления, пока это свойство установлено. Проблема заключается в том, что mIsPendingCleanup
всегда будет устанавливаться для выбитого элемента, в результате чего элемент RecoverAnimation
для элемента никогда не будет удален из списка анимаций. Таким образом, даже если вы восстановите позицию позиции после успешной салфетки, она будет отправлена обратно в выведенную из строя позицию, как только вы ее коснетесь, потому что RecoverAnimation приведет к началу анимации с последней позиции.
Решение, к сожалению, скопировать исходный код класса ItemTouchHelper
в тот же пакет, что и в библиотеке поддержки, и удалить свойство mIsPendingCleanup
из класса RecoverAnimation
. Я не уверен, что это приемлемо для Google, и я еще не опубликовал обновление в Play Store, чтобы узнать, приведет ли оно к отказу, но вы можете найти исходный код класса из библиотеки поддержки v22.2.1 с приведенным выше упомянутое исправление в https://gist.github.com/kukabi/f46e1c0503d2806acbe2.
Вы должны переопределить метод onSwiped
в ItemTouchHelper.Callback
и обновить этот конкретный элемент.
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder,
int direction) {
adapter.notifyItemChanged(viewHolder.getAdapterPosition());
}
грязный обходной путь для решения этой проблемы состоит в том, чтобы повторно присоединить ItemTouchHelper, дважды вызвав ItemTouchHelper::attachToRecyclerView(RecyclerView)
, что затем вызовет закрытый метод ItemTouchHelper::destroyCallbacks()
. destroyCallbacks()
удаляет украшение элемента и всех слушателей, но также очищает все RecoverAnimations.
Обратите внимание, что сначала нам нужно вызвать itemTouchHelper.attachToRecyclerView(null)
, чтобы обмануть ItemTouchHelper
и подумать, что второй вызов itemTouchHelper.attachToRecyclerView(recyclerView)
- это новое представление переработчика.
Для получения дополнительной информации ознакомьтесь с исходным кодом ItemTouchHelper
здесь.
Пример обходного пути:
RecyclerView recyclerView = findViewById(R.id.recycler_view);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
...
// Workaround to reset swiped out views
itemTouchHelper.attachToRecyclerView(null);
itemTouchHelper.attachToRecyclerView(recyclerView);
Рассматривайте это как грязный обходной путь, потому что этот метод использует внутреннюю недокументированную деталь реализации ItemTouchHelper
.
Обновление:
Из документации ItemTouchHelper::attachToRecyclerView(RecyclerView)
:
Если TouchHelper уже подключен к RecyclerView, он сначала отсоединится от предыдущего. Вы можете вызвать этот метод с нулевым значением, чтобы отсоединить его от текущего RecyclerView.
и в документации параметров:
Экземпляр RecyclerView, к которому вы хотите добавить этот помощник или ноль, если вы хотите удалить ItemTouchHelper из текущего RecyclerView.
Так что, по крайней мере, это частично документировано.
Вызовите notifyDataSetChanged на своем адаптере, чтобы сделать прокрутку обратно согласованной.
В случае использования LiveData
для предоставления списка ListAdapter
, вызов notifyItemChanged
не работает. Тем не менее, я нашел ошибочный обходной путь, который включает в себя повторное присоединение ItemTouchHelper
к представлению переработчика в обратном вызове onSwiped
как таковое
val recyclerView = someRecyclerViewInYourCode
var itemTouchHelper: ItemTouchHelper? = null
val itemTouchCallback = object : ItemTouchHelper.Callback {
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction:Int) {
itemTouchHelper?.attachToRecyclerView(null)
itemTouchHelper?.attachToRecyclerView(recyclerView)
}
}
itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)