RecyclerView の状態を管理する

RecyclerView では、最小限のグラフィック リソースを使用して大量のデータを表示できます。ユーザーが RecyclerView 内のアイテムをスクロールすると、画面外にスクロールしたアイテムの View インスタンスが再使用され、画面上でのスクロール時に新しいアイテムが作成されます。しかし、デバイスの回転などの構成変更により、RecyclerView の状態がリセットされ、ユーザーがアイテムのリストの以前の位置に再びスクロールしなければならない場合があります。

RecyclerView は、すべての構成変更において、状態(特にスクロール位置)とリスト要素の状態を維持する必要があります。

状態の維持

RecyclerView のスクロール位置を保存するように RecyclerView.Adapter の状態復元ポリシーを設定します。RecyclerView のリストアイテムの状態を保存します。RecyclerView アダプターにリストアイテムの状態を追加し、ViewHolder にバインドされたときにリストアイテムの状態を復元します。

1. Adapter の状態復元ポリシーを有効にする

RecyclerView アダプターの状態復元ポリシーを有効にして、構成変更後も RecyclerView のスクロール位置が維持されるようにします。ポリシー仕様をアダプター コンストラクタに追加します。

Kotlin

class MyAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    init {
        stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY
    }
    ...
}

Java

class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public Adapter() {
        setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY);
    }
    ...
}

2. ステートフルなリストアイテムの状態を保存する

EditText 要素を含むアイテムなど、複雑な RecyclerView リストアイテムの状態を保存します。たとえば、EditText の状態を保存するには、テキストの変更をキャプチャする onClick ハンドラと同様のコールバックを追加します。コールバック内で、保存するデータを定義します。

Kotlin

input.addTextChangedListener(
    afterTextChanged = { text ->
        text?.let {
            // Save state here.
        }
    }
)

Java

input.addTextChangedListener(new TextWatcher() {

    ...

    @Override
    public void afterTextChanged(Editable s) {
        // Save state here.
    }
});

Activity または Fragment でコールバックを宣言します。ViewModel を使用して状態を保存します。

3. Adapter にリストアイテムの状態を追加する

リストアイテムの状態を RecyclerView.Adapter に追加します。ホスト Activity または Fragment が作成されたときに、アイテムの状態をアダプター コンストラクタに渡します。

Kotlin

val adapter = MyAdapter(items, viewModel.retrieveState())

Java

MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());

4. アダプターの ViewHolder でリストアイテムの状態を復元する

RecyclerView.AdapterViewHolder をアイテムにバインドする際に、アイテムの状態を復元します。

Kotlin

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    ...
    val item = items[position]
    val state = states.firstOrNull { it.item == item }

    if (state != null) {
        holder.restore(state)
    }
}

Java

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    ...
    Item item = items[position];
    Arrays.stream(states).filter(state -> state.item == item)
        .findFirst()
        .ifPresent(state -> holder.restore(state));
}

要点

結果

RecyclerView は、スクロール位置と RecyclerView リスト内のすべてのアイテムの状態を復元できるようになりました。

このガイドを含むコレクション

このガイドは、Android 開発の幅広い目標を網羅する、厳選されたクイックガイド コレクションの一部です。

タブレット、折りたたみ式デバイス、ChromeOS デバイスで最適化されたユーザー エクスペリエンスをサポートするようにアプリを有効にします。

ご質問やフィードバックがある場合

よくある質問のページでクイックガイドをご覧になるか、お問い合わせフォームからご意見をお寄せください。