RecyclerView.ViewHolder-getLayoutPositionとgetAdapterPosition


87

新しいサポートライブラリバージョン(22.x)以降、このトピックで説明されているメソッドの代わりに、クラスのgetPosition()メソッドはRecyclerView.ViewHolder非推奨になりました。ドキュメントを読んでも違いはありません。誰かが素人の用語の違いを説明できますか?

次のユースケースがあります-アダプタにを与え、List各リストアイテムに追加情報を関連付けられるようにしたいです。私は位置から追加へのマッピングを持っています、そして、彼らが彼らの位置のために余分をフェッチしてそれで何かをすることができるように、マッピングは所有者のために利用可能です。ホルダーでは、どの方法を使用すればよいですか?

インデックス0と1のリストアイテムが入れ替わった場合、ホルダーの位置はどうなりますか?メソッドは何を返しますか?

回答:


114

これはトリッキーな状況です。ドキュメントが十分でないことを残念に思います。

アダプタの内容が変更されると(そしてあなたが呼び出すとnotify***())、RecyclerViewは新しいレイアウトを要求します。その瞬間から、レイアウトシステムが新しいレイアウトの計算を決定するまで(<16 ms)、レイアウトはまだアダプターの変更を反映していないため、レイアウトの位置とアダプターの位置が一致しない場合があります。

ユースケースでは、データはアダプターの内容に関連しているため(アダプターの変更と同時にデータが変更されると想定しています)、を使用する必要がありますadapterPosition

ただし、を呼び出す場合はnotifyDataSetChanged()、すべてが無効になるため、次のレイアウトが計算されるまで、RecyclerViewはViewHolderのアダプター位置を認識しないことに注意してください。その場合、getAdapterPosition()RecyclerView#NO_POSITION-1)を返します。

ただしnotifyItemInserted(0)getAdapterPosition()を呼び出した場合、以前はその位置にあったViewHolderのがすぐに0戻り始め1ます。きめ細かい通知イベントをディスパッチしている限り、常に良好な状態です(新しいレイアウトがまだ計算されていなくても、アダプターの位置はわかっています)。

別の例として、ユーザーのクリックで何かをしている場合、getAdapterPosition()returnsの場合NO_POSITION、どのユーザーがクリックしたかわからないため、そのクリックを無視するのが最善です(アイテムを検索するための安定したIDなど、他のメカニズムがない場合)。

レイアウト位置が適切な場合の編集

LinearLayoutManager現在クリックされているアイテムの上にあるViewHolderを使用していて、アクセスしたいとします。その場合、上記のアイテムを取得するには、レイアウト位置を使用する必要があります。

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

レイアウト位置は、ユーザーが現在画面に表示しているものと一致するため、使用する必要があります。


1
少し遊んだところ、getAdapterPosition()メソッドが常に-1を返すことがわかりました。私はそれをデバッグしましたが、その理由は、メソッド(final ViewParent parent = itemView.getParent(); if(!(parent instanceof RecyclerView)){return -1;}のコードが常にifブロック、つまりリサイクラービューに入るからです。はセルビューの親ではありません。どうすればよいですか?ホルダーを作成するコードは次のとおりです。returnMyViewHolder(LayoutInflater.from(viewGroup.getContext())。inflate(R.layout.test_list_item、viewGroup、false)); (別のコメントに続く。)
wujek 2015

inflate(R.layout.test_list_item、viewGroup、true);を呼び出すようにコードを変更すると (ルートにアタッチする場合はtrueに注意してください)、Androidは次をスローします:java.lang.IllegalStateException:指定された子にはすでに親があります。最初に子の親でremoveView()を呼び出す必要があります。では、リサイクラービューに正しくアタッチされたビューでビューホルダーを作成する正しい方法は何ですか?不思議なことに、親がnullであるためにgetAdapterPosition()が-1を返したとしても、それ以外はすべて正常に機能します。
wujek 2015

1
レイアウトインフレータのブールパラメータは「addToParent」です。追加するのはLayoutManagerの責任であるため、falseである必要があります。すでにポジションを渡されているonBindでgetAdapterPositionを呼び出していると思います。技術的には、ビューホルダーは、onBindが戻った後のその位置を表します。ところで、getAdapterの位置を更新して、切り離されていても有効な位置を返すようにしました(可能な場合)。まもなくリリースされます。
yigit 2015

そうです、私はonBindでgetAdapterPositionを呼び出しています。代わりに、この場合はどうすればよいですか。一部のビューの状態に影響を与える情報を取得するためのポジションが必要です。この場合、getLayoutPositionを呼び出しても問題ありませんか?
wujek 2015

5
onBindメソッドに渡されるpositionパラメーターを使用する必要があります。
yigit 2015

2


差異(複数可)のを主張するためにgetAdapterPosition()getLayoutPosition()およびまたposition、以下のケースに気付くでしょう:

1.positionメソッドの引数onBindViewHolder()

を使用しpositionてデータをビューにバインドできます。position引数を使用してこれを行うことはできpositionますが、引数を使用してユーザーのクリックを処理することはできません。使用した場合は、「positionとして扱わないでください」という警告が表示されます。修正してholder.getAdapterPosition()代わりに使用してください。」

2 . getAdapterPosition()

このメソッドは、常に更新されたアダプターの位置で構成されholderます。これは、アイテムをクリックするたびに、アダプターにそのアイテムについて尋ねることを意味しますposition。したがって、アダプタのロジックの観点から、このアイテムの最新の位置を取得します。

3 . getLayoutPosition()

場合によってpositionは、更新されたレイアウト(ユーザーが現在表示している最後に渡されたレイアウト)の観点からを見つける必要があります。たとえば、ユーザーpositionが表示できる3番目のレイアウトを要求した場合、アイテムにswipe/dismissを使用するか、アニメーションを適用します。または、アイテムの装飾のgetLayoutPosition()代わりに使用する方がよいgetAdapterPosition()でしょう。これは、最新の渡されたレイアウトの観点からアイテムの位置を処理していることを常に確認できるためです。


詳細については、ここを参照してください。。。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.