次の方法で共有


基本的な RecyclerView の例

このトピックでは、一般的なアプリケーションにおける RecyclerView の動作を理解するために RecyclerView を使用して大量の写真のコレクションを表示する単純なコード例について説明します。

CardViews を使用して写真を表示する RecyclerView アプリの 2 つのスクリーンショット

RecyclerViewerCardView を使用して、各写真項目を RecyclerView レイアウトに実装します。 RecyclerView のパフォーマンス上の利点があるため、このサンプル アプリでは、大量の写真をすばやくスムーズにスクロールでき、目立った遅延はありません。

データ ソースの例

このサンプル アプリでは、"フォト アルバム" データ ソース (PhotoAlbum クラスによって表されます) が RecyclerView に項目のコンテンツを提供します。 PhotoAlbum は、キャプションを含む写真のコレクションです。インスタンス化すると、32 枚の写真の既製のコレクションが取得されます。

PhotoAlbum mPhotoAlbum = new PhotoAlbum ();

PhotoAlbum 内の各写真インスタンスは、画像リソース ID、PhotoID、キャプション文字列、Caption の読み取りを可能にするプロパティを公開します。 写真のコレクションは、各写真にインデクサーがアクセスできるように編成されています。 たとえば、次のコード行は、コレクション内の 10 番目の写真の画像リソース ID とキャプションにアクセスします。

int imageId = mPhotoAlbum[9].ImageId;
string caption = mPhotoAlbum[9].Caption;

また、PhotoAlbum は、コレクション内の最初の写真を、コレクション内の別の場所でランダムに選択された写真と交換するために呼び出すことができる RandomSwap メソッドも提供します。

mPhotoAlbum.RandomSwap ();

PhotoAlbum の実装の詳細は RecyclerView の理解には関係ないため、 PhotoAlbum ソース コードはここには示されていません。

レイアウトと初期化

レイアウト ファイル Main.axml は、LinearLayout 内の単一の RecyclerView で構成されます。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:scrollbars="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

RecyclerView はサポート ライブラリにパッケージ化されているため、完全修飾名 android.support.v7.widget.RecyclerView を使用する必要があることに注意してください。 MainActivityOnCreate メソッドは、このレイアウトを初期化し、アダプターをインスタンス化し、基になるデータ ソースを準備します。

public class MainActivity : Activity
{
    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    PhotoAlbumAdapter mAdapter;
    PhotoAlbum mPhotoAlbum;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Prepare the data source:
        mPhotoAlbum = new PhotoAlbum ();

        // Instantiate the adapter and pass in its data source:
        mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);

        // Set our view from the "main" layout resource:
        SetContentView (Resource.Layout.Main);

        // Get our RecyclerView layout:
        mRecyclerView = FindViewById<RecyclerView> (Resource.Id.recyclerView);

        // Plug the adapter into the RecyclerView:
        mRecyclerView.SetAdapter (mAdapter);

このコードでは、次のことが行われます。

  1. PhotoAlbum データ ソースをインスタンス化します。

  2. フォト アルバム データ ソースをアダプター PhotoAlbumAdapter のコンストラクターに渡します (このガイドの後半で定義)。 データ ソースをパラメーターとしてアダプターのコンストラクターに渡すことがベスト プラクティスとみなされることに注意してください。

  3. レイアウトから RecyclerView を取得します。

  4. 上記のように RecyclerViewSetAdapter メソッドを呼び出して、アダプターを RecyclerView インスタンスに接続します。

レイアウト マネージャー

RecyclerView 内の各項目は、写真画像と写真キャプションを含む CardView で構成されています (詳細については、以下の「ビュー ホルダー」セクションを参照)。 定義済みの LinearLayoutManager は、各 CardView を垂直スクロール配置でレイアウトするために使用されます。

mLayoutManager = new LinearLayoutManager (this);
mRecyclerView.SetLayoutManager (mLayoutManager);

このコードは、メイン アクティビティの OnCreate メソッドに存在します。 レイアウト マネージャーのコンストラクターには "コンテキスト" が必要であるため、MainActivity は、上記のように this を使用して渡されます。

定義済みの LinearLayoutManager を使用する代わりに、2 つの CardView 項目を横に並べて表示するカスタム レイアウト マネージャーをプラグインして、写真のコレクションを走査するページめくりアニメーション効果を実装できます。 このガイドの後半では、別のレイアウト マネージャーでスワップしてレイアウトを変更する方法の例を示します。

ビュー ホルダー

ビュー ホルダー クラスは PhotoViewHolder と呼ばれます。 各 PhotoViewHolder インスタンスは、関連付けられている行項目の ImageView および TextView への参照を保持します。これは、次の図のように CardView にレイアウトされています。

ImageView と TextView を含む CardView の図

PhotoViewHolderRecyclerView.ViewHolder から派生し、上記のレイアウトに示される ImageView および TextView への参照を格納するためのプロパティを含みます。 PhotoViewHolder は、2 つのプロパティと 1 つのコンストラクターで構成されます。

public class PhotoViewHolder : RecyclerView.ViewHolder
{
    public ImageView Image { get; private set; }
    public TextView Caption { get; private set; }

    public PhotoViewHolder (View itemView) : base (itemView)
    {
        // Locate and cache view references:
        Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
        Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
    }
}

このコード例では、PhotoViewHolder コンストラクターには、PhotoViewHolder がラップする親項目ビュー (CardView) への参照が渡されます。 必ず、親項目ビューを基本コンストラクターに転送するように注意してください。 PhotoViewHolder コンストラクターは、親項目ビューで FindViewById を呼び出して、子ビュー参照 ImageView および TextView をそれぞれ検索し、結果をそれぞれ Image および Caption プロパティに格納します。 アダプターは後で、この CardView の子ビューを新しいデータで更新するときに、これらのプロパティからビュー参照を取得します。

RecyclerView.ViewHolder の詳細については、RecyclerView.ViewHolder クラスのリファレンスを参照してください。

アダプター

アダプターは、各 RecyclerView 行に特定の写真のデータを読み込みます。 たとえば、行位置 P の特定の写真の場合、アダプターは、データ ソース内の位置 P に関連付けられているデータを検索し、RecyclerView コレクション内の位置 P にある行項目にこのデータをコピーします。 アダプターは、ビュー ホルダーを使用してその位置にある ImageViewTextView の参照を検索するため、ユーザーが写真コレクションをスクロールしてビューを再利用するときに、これらのビューに対して FindViewById を繰り返し呼び出す必要はありません。

RecyclerViewer では、アダプター クラスは RecyclerView.Adapter から派生し、PhotoAlbumAdapter を作成します。

public class PhotoAlbumAdapter : RecyclerView.Adapter
{
    public PhotoAlbum mPhotoAlbum;

    public PhotoAlbumAdapter (PhotoAlbum photoAlbum)
    {
        mPhotoAlbum = photoAlbum;
    }
    ...
}

mPhotoAlbum メンバーには、コンストラクターに渡されるデータ ソース (フォト アルバム) が含まれています。コンストラクターはフォト アルバムをこのメンバー変数にコピーします。 次の必須 RecyclerView.Adapter メソッドが実装されています。

  • OnCreateViewHolder – 項目レイアウト ファイルとビュー ホルダーをインスタンス化します。

  • OnBindViewHolder – 指定した位置にあるデータを、指定されたビュー ホルダーに参照が格納されているビューに読み込みます。

  • ItemCount – データ ソース内の項目の数を返します。

レイアウト マネージャーは、RecyclerView 内に項目を配置するときにこれらのメソッドを呼び出します。 これらのメソッドの実装については、次のセクションで説明します。

OnCreateViewHolder

レイアウト マネージャーは、RecyclerView が項目を表すために新しいビュー ホルダーを必要とするときに、OnCreateViewHolder を呼び出します。 OnCreateViewHolder は、ビューのレイアウト ファイルから項目ビューを拡張し、ビューを新しい PhotoViewHolder インスタンスにラップします。 PhotoViewHolder コンストラクターは、「ビュー ホルダー」で説明したように、子ビューへの参照を検索して、レイアウトに格納します。

各行項目は、ImageView (写真の場合) と TextView (キャプションの場合) を含む CardView によって表されます。 このレイアウトは、PhotoCardView.axml ファイルに存在します。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardElevation="4dp"
        card_view:cardUseCompatPadding="true"
        card_view:cardCornerRadius="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="8dp">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/imageView"
                android:scaleType="centerCrop" />
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="#333333"
                android:text="Caption"
                android:id="@+id/textView"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="4dp" />
        </LinearLayout>
    </android.support.v7.widget.CardView>
</FrameLayout>

このレイアウトは、RecyclerView の 1 つの行項目を表します。 この OnBindViewHolder メソッド (後述) は、データ ソースからこのレイアウトの ImageViewTextView にデータをコピーします。 OnCreateViewHolder はこのレイアウトを、RecyclerView の特定の写真の場所に対して拡張し、新しい PhotoViewHolder インスタンスをインスタンス化します (関連付けられた CardView レイアウト内の ImageView および TextView 子ビューへの参照を検索してキャッシュします)。

public override RecyclerView.ViewHolder
    OnCreateViewHolder (ViewGroup parent, int viewType)
{
    // Inflate the CardView for the photo:
    View itemView = LayoutInflater.From (parent.Context).
                Inflate (Resource.Layout.PhotoCardView, parent, false);

    // Create a ViewHolder to hold view references inside the CardView:
    PhotoViewHolder vh = new PhotoViewHolder (itemView);
    return vh;
}

その結果得られるビュー ホルダー インスタンス vhは、呼び出し元 (レイアウト マネージャー) に返されます。

OnBindViewHolder

レイアウト マネージャーは、RecyclerView の表示される画面領域に特定のビューを表示する準備ができたら、アダプターの OnBindViewHolder メソッドを呼び出して、指定した行の位置にある項目にデータ ソースのコンテンツを入力します。 OnBindViewHolder は、指定された行位置の写真情報 (写真の画像リソースと写真のキャプションの文字列) を取得し、関連付けられたビューにこのデータをコピーします。 ビューは、ビュー ホルダー オブジェクト (holder パラメーターを介して渡されます) に格納されている参照を介して配置されます。

public override void
    OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
{
    PhotoViewHolder vh = holder as PhotoViewHolder;

    // Load the photo image resource from the photo album:
    vh.Image.SetImageResource (mPhotoAlbum[position].PhotoID);

    // Load the photo caption from the photo album:
    vh.Caption.Text = mPhotoAlbum[position].Caption;
}

渡されたビュー ホルダー オブジェクトは、使用する前に、まず派生ビュー ホルダー型 (この場合 PhotoViewHolder) にキャストされる必要があります。 アダプターは、ビュー ホルダーの Image プロパティによって参照されるビューに画像リソースを読み込み、ビュー ホルダーの Caption プロパティによって参照されるビューにキャプション テキストをコピーします。 これにより、関連付けられているビューがデータに "バインドされます"。

OnBindViewHolder は、データの構造を直接処理するコードであることに注意してください。 この場合、OnBindViewHolder は、RecyclerView 項目の位置をデータ ソース内の関連するデータ項目にマップする方法を理解します。 この場合、位置をフォト アルバムの配列インデックスとして使用できるため、マッピングは簡単です。ただし、より複雑なデータ ソースでは、このようなマッピングを確立するために追加のコードが必要になる場合があります。

ItemCount

ItemCount メソッドは、データ コレクション内の項目の数を返します。 フォト ビューアー アプリの例では、項目数はフォト アルバム内の写真の数です。

public override int ItemCount
{
    get { return mPhotoAlbum.NumPhotos; }
}

RecyclerView.Adapter の詳細については、RecyclerView.Adapter クラスのリファレンスを参照してください。

まとめ

その結果行われる写真アプリの例の RecyclerView 実装は、データ ソース、レイアウト マネージャー、アダプターを作成する MainActivity コードで構成されます。 MainActivitymRecyclerView インスタンスを作成し、データ ソースとアダプターをインスタンス化し、レイアウト マネージャーとアダプターをプラグインします。

public class MainActivity : Activity
{
    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    PhotoAlbumAdapter mAdapter;
    PhotoAlbum mPhotoAlbum;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);
        mPhotoAlbum = new PhotoAlbum();
        SetContentView (Resource.Layout.Main);
        mRecyclerView = FindViewById<RecyclerView> (Resource.Id.recyclerView);

        // Plug in the linear layout manager:
        mLayoutManager = new LinearLayoutManager (this);
        mRecyclerView.SetLayoutManager (mLayoutManager);

        // Plug in my adapter:
        mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);
        mRecyclerView.SetAdapter (mAdapter);
    }
}

PhotoViewHolder はビュー参照を検索してキャッシュします。

public class PhotoViewHolder : RecyclerView.ViewHolder
{
    public ImageView Image { get; private set; }
    public TextView Caption { get; private set; }

    public PhotoViewHolder (View itemView) : base (itemView)
    {
        // Locate and cache view references:
        Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
        Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
    }
}

PhotoAlbumAdapter では、3 つの必要なメソッド オーバーライドが実装されます。

public class PhotoAlbumAdapter : RecyclerView.Adapter
{
    public PhotoAlbum mPhotoAlbum;
    public PhotoAlbumAdapter (PhotoAlbum photoAlbum)
    {
        mPhotoAlbum = photoAlbum;
    }

    public override RecyclerView.ViewHolder
        OnCreateViewHolder (ViewGroup parent, int viewType)
    {
        View itemView = LayoutInflater.From (parent.Context).
                    Inflate (Resource.Layout.PhotoCardView, parent, false);
        PhotoViewHolder vh = new PhotoViewHolder (itemView);
        return vh;
    }

    public override void
        OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
    {
        PhotoViewHolder vh = holder as PhotoViewHolder;
        vh.Image.SetImageResource (mPhotoAlbum[position].PhotoID);
        vh.Caption.Text = mPhotoAlbum[position].Caption;
    }

    public override int ItemCount
    {
        get { return mPhotoAlbum.NumPhotos; }
    }
}

このコードをコンパイルして実行すると、次のスクリーンショットに示すように、基本的な写真表示アプリが作成されます。

縦方向にスクロールする写真カードを含む写真表示アプリの 2 つのスクリーンショット

(上のスクリーンショットに示すように) 影が描画されていない場合、Properties/AndroidManifest.xml を編集し、次の属性設定を <application> 要素に追加します。

android:hardwareAccelerated="true"

この基本アプリは、フォト アルバムの閲覧のみをサポートしています。 項目タッチ イベントには応答せず、基になるデータの変更も処理しません。 この機能は、「RecyclerView の例を拡張する」で追加されます。

LayoutManager の変更

RecyclerView には柔軟性があるため、別のレイアウト マネージャーを使用するようにアプリを簡単に変更できます。 次の例では、垂直方向の線形レイアウトではなく水平方向にスクロールするグリッド レイアウトでフォト アルバムを表示するように変更されています。 これを行うために、レイアウト マネージャーのインスタンス化は、次のように GridLayoutManager を使用する形に変更されます。

mLayoutManager = new GridLayoutManager(this, 2, GridLayoutManager.Horizontal, false);

このコード変更は、垂直 LinearLayoutManager を、水平方向にスクロールする 2 つの行で構成されるグリッドを表示する GridLayoutManager に置き換えます。 アプリをコンパイルして再度実行すると、写真がグリッドに表示され、スクロールが垂直ではなく水平になったことがわかります。

グリッド内で写真を横方向にスクロールするアプリのスクリーンショットの例

コードを 1 行だけ変更することで、動作が異なる別のレイアウトを使用するように写真表示アプリを変更できます。 レイアウト スタイルを変更するために、アダプター コードもレイアウト XML も変更する必要がなかったことに注目してください。

次のトピック「RecyclerView の例を拡張する」では、この基本的なサンプル アプリを拡張して項目クリック イベントを処理し、基になるデータ ソースが変更されたときに RecyclerView を更新するようにします。