RecyclerView

2017年05月01日(編集2017年05月01日)
このエントリーをはてなブックマークに追加

Android Lint

version 2.3.1
lint_id RecyclerView
severity Error
category Correctness
priority 8

環境

  • OS X El Capitan
  • android sdk 25
  • Oracle jdk version 1.8.0_72
  • Android Studio 2.3.1
  • Android 7.1 (API level 25)

メッセージ

Do not treat position as fixed; only use immediately and call holder.getAdapterPosition() to look it up later.

説明

RecyclerView will not call onBindViewHolder again when the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined. For this reason, you should only use the position parameter while acquiring the related data item inside this method, and should not keep a copy of it. If you need the position of an item later on (e.g. in a click listener), use getAdapterPosition() which will have the updated adapter position.

エラー原因

RecyclerView.AdapterのonBindViewHolderメソッドの引数positionを変数に保持すると、このエラーが発生します。

エラーコード

以下にエラーが発生するJavaコードを記載します。

{project_folder}/app/src/main/RecyclerViewGooglePlusFragmentAdapter.java
public class RecyclerViewGooglePlusFragmentAdapter extends RecyclerView.Adapter<RecyclerViewGooglePlusFragmentAdapter.ViewHolder> {
    private static final String TAG = "Adapter";

    private final Context context;
    private int lastPosition;

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.note = noteList.get(position);
        holder.title.setText(holder.note.title);
        holder.summary.setText(holder.note.summary);

        holder.view.setOnClickListener(v -> onClickView(holder));

        holder.plus.setOnClickListener(v -> onClickPlus(holder));

        if (lastPosition < position) {
            startAnimation(holder.view, position);
            lastPosition = position;
        }
    }
}
        

Don't.

上記のコードはLintエラーになります。severityがErrorなので、ビルドに失敗します。

fig1. Terminal Build Error

修正方法

項目の位置を保持する必要がある場合は、更新されたアダプタ位置を持つgetAdapterPosition()を使用します

正常コード

{project_folder}/app/src/main/RecyclerViewGooglePlusFragmentAdapter.java
public class RecyclerViewGooglePlusFragmentAdapter extends RecyclerView.Adapter<RecyclerViewGooglePlusFragmentAdapter.ViewHolder> {
    private static final String TAG = "Adapter";

    private final Context context;
    private int lastPosition;

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.note = noteList.get(position);
        holder.title.setText(holder.note.title);
        holder.summary.setText(holder.note.summary);

        holder.view.setOnClickListener(v -> onClickView(holder));

        holder.plus.setOnClickListener(v -> onClickPlus(holder));

        if (lastPosition < position) {
            startAnimation(holder.view, position);
           +lastPosition = holder.getAdapterPosition();
        }
    }
}
        

Do.

アイテムの位置がデータセット内で変更されたとき、RecyclerViewはonBindViewHolderを呼び出すとは限りません。なので、 positionパラメーターのコピーを保持してはいけません。

リスナーをクリックする等で、項目の位置が必要な場合は、更新されたアダプタ位置を持つgetAdapterPosition()を使用します。

ビルド

上記の修正後、Lintを確認してみましょう。

fig2. Terminal Build Success

Lintエラーが消えてビルドに成功しました。

結論

RecyclerViewは便利であると同時に、アプリ作成の要です。バグを極力少なくするプログラミングを心がけてください。

関連記事

タグ検索で調べてみよう

Android7.1 Error