[Android] RecyclerViewのposition検出について
positionについて
RecyclerViewのpositionに関連するメソッドは以下の2つのタイプがあります。
layout positon : LayoutManager視点からのposition adapter position : Adapter視点からのアイテムのposition
これら二つのpositionは、adapter.notify*イベントを送り、更新されたレイアウトを計算するまでの間以外は同じらしいです。
layout positonの使用例としては
RecyclerView.ViewHolder.getLayoutPosition()、 RecyclerView.findViewHolderForLayoutPosition(int)
これらのpositionはレイアウトが最後まで計算された変更が含まれます。ユーザーが画面上に見えているものとアダプターなりで保有しているリストのpositionが一致すると思われる時に、これらのメソッドを使います。
@Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { ViewHolder viewHolder = (ViewHolder)holder; SampleData data = mDataList.get(holder.getLayoutPosition()); //以下処理 }
onBindViewHolderの引数のpositionはバインドするpositionです。
adapter positionの使用例としては
RecyclerView.ViewHolder.getAdapterPosition()、 RecyclerView.findViewHolderForAdapterPosition(int)
最新のアダプターの状態で何らかの処理を行いたい時は、レイアウトがまだ反映されていなくても、これらのメソッドを使う必要があります。例えば、ViewHolderのクリックでnotifyItemInsertedやnotifyRemovedでリストの要素を追加したり削除を行ってすぐに最新のリストでのpositionが取りたい時とかですかね。
わかりずらいので実際の画面で、getLayoutPosition と getAdapterPosition を比べてみます。
getLayoutPositionの場合
@Override public int getItemCount() { return mDataList.size(); } public void addItem(SampleData data, int index) { mDataList.add(data); notifyItemInserted(index); } public void deleteItem(int index) { mDataList.remove(index); notifyItemRemoved(index); } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { final ViewHolder viewHolder = (ViewHolder)holder; viewHolder.text.setText("position " + viewHolder.getLayoutPosition()); viewHolder.addButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addItem(new SampleData(),viewHolder.getLayoutPosition()); viewHolder.text.setText("position " + viewHolder.getLayoutPosition()); } }); viewHolder.removeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { deleteItem(viewHolder.getLayoutPosition()); } }); }
position 1の追加ボタンを押すと、一段上に新しい要素が追加されて、ボタンを押した行のテキストを最新のpositionで更新しようとした場合、getLayoutPositonではレイアウトが反映される前のpositionを取得しています。
getAdapterPositonの場合
@Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { final ViewHolder viewHolder = (ViewHolder)holder; viewHolder.text.setText("position " + viewHolder.getLayoutPosition()); viewHolder.addButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addItem(new SampleData(),viewHolder.getLayoutPosition()); viewHolder.text.setText("position " + viewHolder.getAdapterPosition()); } }); }
getAdapterPostionでは要素追加後の最新のリストのpositionを取得できています。
以上、RecyclerViewでのpositionの簡単な使い分け例でした。
おまけ
RecyclerView.ViewHolderのソースの中身の違いは以下になっています。
public abstract static class ViewHolder { 省略 public final int getLayoutPosition() { return this.mPreLayoutPosition == -1?this.mPosition:this.mPreLayoutPosition; } public final int getAdapterPosition() { return this.mOwnerRecyclerView == null?-1:this.mOwnerRecyclerView.getAdapterPositionFor(this); }