基本的な RecyclerView の例
このトピックでは、一般的なアプリケーションにおける RecyclerView
の動作を理解するために RecyclerView
を使用して大量の写真のコレクションを表示する単純なコード例について説明します。
RecyclerViewer は CardView を使用して、各写真項目を 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 を使用する必要があることに注意してください。 MainActivity
の OnCreate
メソッドは、このレイアウトを初期化し、アダプターをインスタンス化し、基になるデータ ソースを準備します。
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);
このコードでは、次のことが行われます。
PhotoAlbum
データ ソースをインスタンス化します。フォト アルバム データ ソースをアダプター
PhotoAlbumAdapter
のコンストラクターに渡します (このガイドの後半で定義)。 データ ソースをパラメーターとしてアダプターのコンストラクターに渡すことがベスト プラクティスとみなされることに注意してください。レイアウトから
RecyclerView
を取得します。上記のように
RecyclerView
SetAdapter
メソッドを呼び出して、アダプターをRecyclerView
インスタンスに接続します。
レイアウト マネージャー
RecyclerView
内の各項目は、写真画像と写真キャプションを含む CardView
で構成されています (詳細については、以下の「ビュー ホルダー」セクションを参照)。 定義済みの LinearLayoutManager
は、各 CardView
を垂直スクロール配置でレイアウトするために使用されます。
mLayoutManager = new LinearLayoutManager (this);
mRecyclerView.SetLayoutManager (mLayoutManager);
このコードは、メイン アクティビティの OnCreate
メソッドに存在します。 レイアウト マネージャーのコンストラクターには "コンテキスト" が必要であるため、MainActivity
は、上記のように this
を使用して渡されます。
定義済みの LinearLayoutManager
を使用する代わりに、2 つの CardView
項目を横に並べて表示するカスタム レイアウト マネージャーをプラグインして、写真のコレクションを走査するページめくりアニメーション効果を実装できます。 このガイドの後半では、別のレイアウト マネージャーでスワップしてレイアウトを変更する方法の例を示します。
ビュー ホルダー
ビュー ホルダー クラスは PhotoViewHolder
と呼ばれます。 各 PhotoViewHolder
インスタンスは、関連付けられている行項目の ImageView
および TextView
への参照を保持します。これは、次の図のように CardView
にレイアウトされています。
PhotoViewHolder
は RecyclerView.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 にある行項目にこのデータをコピーします。 アダプターは、ビュー ホルダーを使用してその位置にある ImageView
と TextView
の参照を検索するため、ユーザーが写真コレクションをスクロールしてビューを再利用するときに、これらのビューに対して 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
メソッド (後述) は、データ ソースからこのレイアウトの ImageView
と TextView
にデータをコピーします。
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
コードで構成されます。 MainActivity
は mRecyclerView
インスタンスを作成し、データ ソースとアダプターをインスタンス化し、レイアウト マネージャーとアダプターをプラグインします。
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; }
}
}
このコードをコンパイルして実行すると、次のスクリーンショットに示すように、基本的な写真表示アプリが作成されます。
(上のスクリーンショットに示すように) 影が描画されていない場合、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
を更新するようにします。