RecyclerView.ViewHolder - getLayoutPosition против getAdapterPosition

88

Начиная с новой версии библиотеки поддержки (22.x), getPosition()метод RecyclerView.ViewHolderкласса устарел вместо методов, упомянутых в теме. Я не особо понимаю разницу от чтения документации. Может ли кто-нибудь объяснить разницу в терминах непрофессионала?

У меня есть следующий вариант использования - я даю своему адаптеру a List, а также хочу иметь возможность связывать дополнительную информацию для каждого элемента списка. У меня есть сопоставление позиции-к-дополнительному, и сопоставление доступно для держателей, так что они могут получить дополнительную информацию для своей позиции и что-то с ней делать. Какой метод использовать в держателе?

Что происходит с позициями держателя, когда элементы списка с индексами 0 и 1 меняются местами? Что возвращают методы?

Wujek
источник

Ответы:

115

Это непростая ситуация, извините, что документов недостаточно.

Когда содержимое адаптера изменяется (и вы вызываете notify***()), RecyclerView запрашивает новый макет. С этого момента, пока система макета не решит рассчитать новый макет (<16 мс), положение макета и положение адаптера могут не совпадать, поскольку макет еще не отразил изменения адаптера.

В вашем случае использования, поскольку ваши данные связаны с содержимым вашего адаптера (и я предполагаю, что данные изменяются одновременно с изменениями адаптера), вы должны использовать adapterPosition.

Однако будьте осторожны, если вы вызываете notifyDataSetChanged(), поскольку он делает недействительным все, RecyclerView не знает эту позицию адаптера ViewHolder до тех пор, пока не будет рассчитан следующий макет. В этом случае getAdapterPosition()вернется RecyclerView#NO_POSITION( -1).

Но допустим, если вы вызвали notifyItemInserted(0), getAdapterPosition()ViewHolder, который ранее был в позиции 0, 1немедленно начнет возвращаться . Итак, пока вы отправляете подробные события уведомления, вы всегда в хорошем состоянии (мы знаем положение адаптера, даже если новый макет еще не рассчитан).

Другой пример: если вы что-то делаете при щелчке пользователем, в случае getAdapterPosition()возврата NO_POSITIONлучше всего игнорировать этот щелчок, потому что вы не знаете, какой пользователь щелкнул (если у вас нет другого механизма, например, стабильных идентификаторов для поиска элемента).

Редактировать, когда расположение макета хорошее

Допустим, вы используете LinearLayoutManagerи хотите получить доступ к ViewHolder над текущим выбранным элементом. В этом случае вы должны использовать позицию макета, чтобы получить элемент выше.

mRecyclerView.findViewHolderForLayoutPosition(myViewHolder.getLayoutPosition() - 1)

Вы должны использовать положение макета, потому что оно соответствует тому, что пользователь в данный момент видит на экране.

yigit
источник
1
Я немного поигрался, и оказалось, что метод getAdapterPosition () всегда возвращает мне -1. Я отлаживал его, и причина в том, что код в методе (final ViewParent parent = itemView.getParent (); if (! (Parent instanceof RecyclerView)) {return -1;} всегда попадает в блок if, то есть в представление ресайклера не является родителем моего представления ячейки. Как это может быть? Мой код для создания держателя: return MyViewHolder (LayoutInflater.from (viewGroup.getContext ()). inflate (R.layout.test_list_item, viewGroup, false)); (Продолжение в другом комментарии.)
wujek
Когда я меняю код для вызова inflate (R.layout.test_list_item, viewGroup, true); (обратите внимание на истинное значение для «прикрепить к корню»), Android выдает: java.lang.IllegalStateException: у указанного потомка уже есть родитель. Вы должны сначала вызвать removeView () для родительского элемента ребенка. Итак, как правильно создать держатель представления с представлением, которое правильно прикреплено к представлению ресайклера? Как ни странно, даже если getAdapterPosition () возвращает -1, потому что родительский элемент равен нулю, все остальное работает нормально.
wujek
1
Логический параметр в инфляторе макета - «addToParent». Он должен быть ложным, потому что LayoutManager должен его добавить. Я думаю, вы вызываете getAdapterPosition в onBind, где вам уже передана позиция. Технически, держатель представления представляет эту позицию после возврата onBind. Кстати, мы обновили позицию getAdapter, чтобы вернуть действительную позицию (если возможно), даже если она отсоединена, она скоро будет выпущена.
yigit
Вы правы, я вызываю getAdapterPosition в onBind. Что мне делать в этом случае? Мне действительно нужна позиция, чтобы получить некоторую информацию, которая влияет на состояния некоторых представлений. Можно ли в этом случае вызвать getLayoutPosition?
wujek
5
Вы должны использовать параметр позиции, который передается методу onBind.
yigit
2


Для того , чтобы спорить разницу (ов) из getAdapterPosition(), getLayoutPosition(), а также position; мы бы отметили следующие случаи:

1. positionаргумент в onBindViewHolder()методе:

Мы можем использовать positionдля привязки данных к представлению, и для этого можно использовать positionаргумент, но нельзя использовать positionаргумент для обработки пользовательских щелчков, и если вы его использовали, вы увидите предупреждение, говорящее вам: «не рассматривать positionкак исправлено и используйте holder.getAdapterPosition()вместо этого ".

2 getAdapterPosition().:

Этот метод всегда состоит из обновленной позиции адаптера holder. Это означает, что всякий раз, когда вы нажимаете на элемент, вы спрашиваете об этом адаптер position. так что вы получите последнюю позицию этого элемента с точки зрения логики адаптера.

3 getLayoutPosition().:

Иногда необходимо найти positionобновленный макет (последний переданный макет, который сейчас видит пользователь), например: если пользователь запрашивает третий, positionон может видеть, а вы используете swipe/ dismissдля элементов или применяете любую анимацию или украшения для элементов, которые лучше использовать getLayoutPosition()вместо них getAdapterPosition(), потому что вы всегда будете уверены, что имеете дело с положением элементов с точки зрения последнего переданного макета.


Для получения дополнительной информации об этом; см. здесь . . .

Эльяр Абад
источник