[Android] RecyclerViewのposition検出について

2015.06.30

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のクリックでnotifyItemInsertednotifyRemovedでリストの要素を追加したり削除を行ってすぐに最新のリストでのpositionが取りたい時とかですかね。

わかりずらいので実際の画面で、getLayoutPositiongetAdapterPosition を比べてみます。

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());
            }
        });
    }

getlayout

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());
            }
        });
      }

getadapter

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);
}