RecyclerView で動的リストを作成する Android Jetpack の一部。

Compose を試す
Jetpack Compose は Android で推奨される UI ツールキットです。Compose でレイアウトを操作する方法を学習します。

RecyclerView を使用すると、大規模なデータセットを簡単かつ効率的に表示できます。データを指定し、各アイテムの表示方法を定義すると、必要に応じて、RecyclerView ライブラリが動的に要素を作成します。

RecyclerView は、その名のとおり、これらの個々の要素をリサイクルします。アイテムが画面外にスクロールされても、RecyclerView はビューを破棄せず、代わりに、RecyclerView は画面上でスクロールした新しいアイテムのビューを再利用します。RecyclerView を使用すると、パフォーマンスとアプリの応答性が向上し、消費電力が削減されます。

主なクラス

複数のクラスが連携して動的リストを作成します。

  • RecyclerView は、データに対応するビューを含む ViewGroup です。これはビュー自体であるため、他の UI 要素を追加するのと同じ方法で、RecyclerView をレイアウトに追加します。

  • リスト内の各要素はビューホルダー オブジェクトによって定義されます。ビューホルダーは、作成されるときにデータが関連付けられることはありません。ビューホルダーは、作成された後で RecyclerView によってそのデータにバインドされます。ビューホルダーを定義するには、RecyclerView.ViewHolder を拡張します。

  • RecyclerView はビューをリクエストし、アダプターのメソッドを呼び出してビューをデータにバインドします。アダプターを定義するには、RecyclerView.Adapter を拡張します。

  • レイアウト マネージャーによって、リスト内の個々の要素が配置されます。RecyclerView ライブラリによって提供されるレイアウト マネージャーを使用することも、独自のレイアウト マネージャーを定義することもできます。レイアウト マネージャーはいずれも、ライブラリの LayoutManager 抽象クラスに基づいています。

すべての要素がどのように組み合わされているかは、RecyclerView サンプルアプリ(Kotlin)または RecyclerView サンプルアプリ(Java)で確認できます。

RecyclerView を実装する手順

RecyclerView を使用する場合は、いくつか必要な作業があります。これについては、次のセクションで詳しく説明します。

  1. リストまたはグリッドの外観を指定します。通常は、RecyclerView ライブラリの標準レイアウト マネージャーのいずれかを使用できます。

  2. リスト内の各要素の外観と動作を設計します。この設計に基づいて、ViewHolder クラスを拡張します。ViewHolder のバージョンでは、リスト項目のすべての機能が提供されます。ビューホルダーは View のラッパーであり、そのビューは RecyclerView によって管理されます。

  3. データを ViewHolder ビューに関連付ける Adapter を定義します。

また、RecyclerView をニーズに合わせて的確に調整できる高度なカスタマイズ オプションも用意されています。

レイアウトの計画

RecyclerView のアイテムは LayoutManager クラスによって並べ替えられます。RecyclerView ライブラリには 3 つのレイアウト マネージャーがあり、レイアウトの標準的な状況を処理します。

  • LinearLayoutManager は 1 次元のリスト内にアイテムを配置します。
  • GridLayoutManager は、2 次元のグリッド内にアイテムを配置します。
    • グリッドが垂直方向に配置されている場合、GridLayoutManager は各行のすべての要素が同じ幅と高さになるようにしますが、行ごとに異なる高さを指定することもできます。
    • グリッドが水平方向に配置されている場合、GridLayoutManager は各列のすべての要素の幅と高さが同じになるようにしますが、列ごとに異なる幅を指定することもできます。
  • StaggeredGridLayoutManagerGridLayoutManager と似ていますが、行のアイテムの高さが同じである必要もなく(垂直グリッドの場合)、同じ列のアイテムの幅が同じである必要もありません(水平グリッドの場合)。結果として、行または列内のアイテムが互いにオフセットされることになります。

また、個々のアイテムのレイアウトを設計する必要もあります。次のセクションで説明するように、ビューホルダーを設計するときにこのレイアウトが必要になります。

アダプタとビューホルダーを実装する

レイアウトを決定したら、AdapterViewHolder を実装する必要があります。これら 2 つのクラスが連携して、データの表示方法を定義します。ViewHolder は、リスト内の各アイテムのレイアウトを含む View のラッパーです。Adapter は、必要に応じて ViewHolder オブジェクトを作成し、それらのビューのデータも設定します。ビューをこれらのデータに関連付けるプロセスは、バインディングと呼ばれます。

アダプタを定義する際は、次の 3 つの主要なメソッドをオーバーライドします。

  • onCreateViewHolder(): RecyclerView は、新しい ViewHolder を作成する必要があるたびにこのメソッドを呼び出します。このメソッドは、ViewHolder とそれに関連する View を作成して初期化しますが、ビューのコンテンツは埋めませんViewHolder はこの時点で特定のデータにバインドされていません)。

  • onBindViewHolder(): RecyclerView はこのメソッドを呼び出して、ViewHolder をデータに関連付けます。このメソッドは適切なデータを取得し、そのデータを使用してビューホルダーのレイアウトを埋めます。たとえば、RecyclerView が名前のリストを表示する場合、メソッドはリストから適切な名前を見つけ、ビューホルダーの TextView ウィジェットに入力できます。

  • getItemCount(): RecyclerView がこのメソッドを呼び出して、データセットのサイズを取得します。たとえば、アドレス帳アプリでは住所の合計数です。RecyclerView はこれを使用して、表示できるアイテムがなくなったかどうかを判断します。

次に、ネストされ、データのリストを表示する ViewHolder を持つ簡単なアダプターの典型的な例を示します。この例では、RecyclerView はテキスト要素の簡単なリストを表示しています。アダプターには、ViewHolder 要素のテキストを含む文字列の配列が渡されます。

Kotlin


class CustomAdapter(private val dataSet: Array<String>) :
        RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder)
     */
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView

        init {
            // Define click listener for the ViewHolder's View
            textView = view.findViewById(R.id.textView)
        }
    }

    // Create new views (invoked by the layout manager)
    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        // Create a new view, which defines the UI of the list item
        val view = LayoutInflater.from(viewGroup.context)
                .inflate(R.layout.text_row_item, viewGroup, false)

        return ViewHolder(view)
    }

    // Replace the contents of a view (invoked by the layout manager)
    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {

        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        viewHolder.textView.text = dataSet[position]
    }

    // Return the size of your dataset (invoked by the layout manager)
    override fun getItemCount() = dataSet.size

}

Java


public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {

    private String[] localDataSet;

    /**
     * Provide a reference to the type of views that you are using
     * (custom ViewHolder)
     */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private final TextView textView;

        public ViewHolder(View view) {
            super(view);
            // Define click listener for the ViewHolder's View

            textView = (TextView) view.findViewById(R.id.textView);
        }

        public TextView getTextView() {
            return textView;
        }
    }

    /**
     * Initialize the dataset of the Adapter
     *
     * @param dataSet String[] containing the data to populate views to be used
     * by RecyclerView
     */
    public CustomAdapter(String[] dataSet) {
        localDataSet = dataSet;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        // Create a new view, which defines the UI of the list item
        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.text_row_item, viewGroup, false);

        return new ViewHolder(view);
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {

        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        viewHolder.getTextView().setText(localDataSet[position]);
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return localDataSet.length;
    }
}

各ビューアイテムのレイアウトは、通常どおり XML レイアウト ファイルで定義されます。この場合、アプリの text_row_item.xml ファイルは次のようになります。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/list_item_height"
    android:layout_marginLeft="@dimen/margin_medium"
    android:layout_marginRight="@dimen/margin_medium"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/element_text"/>
</FrameLayout>

次のステップ

次のコード スニペットは、RecyclerView の使用方法を示しています。

Kotlin


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val dataset = arrayOf("January", "February", "March")
        val customAdapter = CustomAdapter(dataset)

        val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
        recyclerView.adapter = customAdapter

    }

}

Java


RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setAdapter(customAdapter);

このライブラリには、実装をカスタマイズするさまざまな方法も用意されています。詳しくは、RecyclerView の高度なカスタマイズをご覧ください。

参考情報

Android でのテストの詳細については、次のリソースをご覧ください。

サンプルアプリ