RecyclerView.ViewHolder - getLayoutPosition проти getAdapterPosition


89

Починаючи з нової версії бібліотеки підтримки (22.x), getPosition()метод RecyclerView.ViewHolderкласу застарів замість методів, згаданих у темі. Я насправді не отримую різниці від читання документів. Хтось може пояснити різницю в термінах неспеціалістів?

У мене є такий варіант використання - я даю своєму адаптеру a List, а також хочу мати можливість пов’язати додаткову інформацію для кожного елемента списку. У мене є картографування позиції до додаткової, і картографування доступне для власників, щоб вони могли отримати зайве для своєї позиції та робити з нею речі. Який метод у власнику слід використовувати?

Що відбувається з позиціями власника, коли елементи списку за індексами 0 та 1 замінюються місцями? Що повертають методи?

Відповіді:


116

Це складна ситуація, вибачте, що документів недостатньо.

Коли вміст адаптера змінюється (і ви телефонуєте 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)

Ви повинні використовувати положення макета, оскільки воно відповідає тому, що користувач зараз бачить на екрані.


1
Я трохи погрався, і виявляється, метод getAdapterPosition () завжди повертає мені -1. Я налагодив його, і причина полягає в тому, що код у методі (остаточний ViewParent батьківський = itemView.getParent (); if (! (Батьківський екземпляр RecyclerView)) {return -1;} завжди потрапляє в блок if, тобто подання переробника не є батьком мого перегляду комірки. Як це може бути? Моїм кодом для створення власника є: return MyViewHolder (LayoutInflater.from (viewGroup.getContext ()). inflate (R.layout.test_list_item, viewGroup, false)); (Продовження в іншому коментарі.)
wujek

Коли я міняю код для виклику надування (R.layout.test_list_item, viewGroup, true); (зверніть увагу на 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()методі:

Ми можемо використовувати positiona для прив'язки даних до подання, і це нормально використовувати positionаргумент для цього, але це не нормально використовувати positionаргумент для обробки кліків користувачів, і якщо ви використовували його, ви побачите попередження, яке говорить вам "не розглядати positionяк виправлено і використовувати holder.getAdapterPosition()замість цього ".

2 getAdapterPosition().:

Цей метод завжди складається з оновленої позиції адаптера holder. Це означає, що кожного разу, коли ви натискаєте на елемент, ви запитуєте адаптер про нього position. тож ви отримаєте останню позицію цього пункту з точки зору логіки адаптера.

3 getLayoutPosition().:

Іноді потрібно знайти positionтерміни оновленого макета (останнього пройденого макета, який користувач бачить зараз), наприклад: Якщо користувач запитує третю, яку positionвін може бачити, а ви використовуєте swipe/ dismissдля елементів або застосовуєте будь-яку анімацію або прикраси для предметів, які краще використовувати getLayoutPosition()замість getAdapterPosition(), тому що ви завжди будете впевнені, що маєте справу з позицією предметів з точки зору останнього пройденого макета.


Для отримання додаткової інформації щодо цього; дивіться тут . . .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.