RecyclerView の例を拡張する
基本的な RecyclerView の例で説明されている基本的なアプリは、実際には大したことは行いません。単にスクロールして、閲覧しやすくするために写真項目の固定リストを表示します。 実際のアプリケーションでは、ユーザーはディスプレイ内の項目をタップしてアプリを操作できることを期待しています。 また、基になるデータ ソースが変更する (またはアプリによって変更される) 可能性があり、表示の内容はこれらの変更と一貫性を維持する必要があります。 次のセクションでは、項目クリック イベントを処理し、基になるデータ ソースが変更されたときに RecyclerView
を更新する方法について説明します。
項目クリック イベントの処理
ユーザーが RecyclerView
内の項目にタッチすると、項目クリック イベントが生成され、どの項目にタッチしたかをアプリに通知します。 このイベントは、項目ビュー (ビュー ホルダーにラップされている) RecyclerView
によって生成されるのではなく、タッチを検出し、これらのタッチをクリック イベントとして報告します。
項目クリック イベントを処理する方法を説明するために、次の手順では、基本的な写真表示アプリを変更して、ユーザーがタッチした写真を報告する方法について説明します。 サンプル アプリで項目クリック イベントが発生すると、次のシーケンスが実行されます。
写真の
CardView
で項目クリック イベントを検出し、アダプターに通知します。アダプターは、イベント (項目の位置情報を含む) をアクティビティの項目クリック ハンドラーに転送します。
アクティビティの項目クリック ハンドラーは、項目クリック イベントに応答します。
まず、ItemClick
というイベント ハンドラー メンバーが PhotoAlbumAdapter
クラス定義に追加されます。
public event EventHandler<int> ItemClick;
次に、項目クリック イベント ハンドラー メソッドが MainActivity
に追加されます。
このハンドラーは、タッチされた写真項目を示すトーストを簡単に表示します。
void OnItemClick (object sender, int position)
{
int photoNum = position + 1;
Toast.MakeText(this, "This is photo number " + photoNum, ToastLength.Short).Show();
}
次に、OnItemClick
ハンドラーを PhotoAlbumAdapter
に登録するためにコード行が必要です。 これを実行するのは、次の PhotoAlbumAdapter
の作成直後が良いでしょう。
mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);
mAdapter.ItemClick += OnItemClick;
この基本的な例では、ハンドラーの登録はメイン アクティビティの OnCreate
メソッドで行われますが、運用アプリで OnResume
ハンドラーを登録し、OnPause
で登録解除する場合があります。詳細については、「アクティビティのライフサイクル」を参照してください。
PhotoAlbumAdapter
は、項目クリック イベントを受信したときに OnItemClick
を呼び出すようになりました。 次の手順では、この ItemClick
イベントを発生させるアダプターでハンドラーを作成します。 次のメソッド (OnClick
) は、アダプターの ItemCount
メソッドの直後に追加されます。
void OnClick (int position)
{
if (ItemClick != null)
ItemClick (this, position);
}
この OnClick
メソッドは、項目ビューからの項目クリック イベントのアダプターのリスナーです。 (アイテム ビューのビュー ホルダーを介して) このリスナーを項目 ビューに登録するには、PhotoViewHolder
コンストラクターを変更してこのメソッドを追加の引数として受け入れ、OnClick
を項目ビュー Click
イベントに登録する必要があります。
変更された PhotoViewHolder
コンストラクターを次に示します。
public PhotoViewHolder (View itemView, Action<int> listener)
: base (itemView)
{
Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
itemView.Click += (sender, e) => listener (base.LayoutPosition);
}
itemView
パラメーターには、ユーザーが操作した CardView
への参照が含まれています。 ビュー ホルダーの基本クラスは、(LayoutPosition
プロパティを介して) 表す項目 (CardView
) のレイアウト位置を把握し、項目クリック イベントが発生したときにこの位置がアダプターの OnClick
メソッドに渡されることに注意してください。 アダプターの OnCreateViewHolder
メソッドを変更して、ビュー ホルダーのコンストラクターにアダプターの OnClick
メソッドを渡します。
PhotoViewHolder vh = new PhotoViewHolder (itemView, OnClick);
これで、サンプルの写真表示アプリをビルドして実行すると、ディスプレイ内の写真をタップすると、タッチされた写真を報告するトーストが表示されるようになりました。
この例では、RecyclerView
を使用してイベント ハンドラーを実装するための 1 つの方法のみを示します。 ここで使用できるもう 1 つの方法は、ビュー ホルダーにイベントを配置し、アダプターにこれらのイベントをサブスクライブさせる方法です。 サンプルの写真アプリで写真編集機能が提供されている場合は、それぞれの CardView
内に ImageView
と TextView
が必要です。ここでは、TextView
をタッチすると、ユーザーがキャプションを編集するための EditView
ダイアログが起動し、ImageView
をタッチすると、ユーザーが写真のトリミングや回転を行うことができる写真タッチアップ ツールが起動します。 アプリのニーズに応じて、タッチ イベントの処理と応答に最適なアプローチを設計する必要があります。
データセットが変更されたときにどのように RecyclerView
を更新できるかを示すために、サンプルの写真表示アプリを変更して、データ ソース内の写真をランダムに選択し、最初の写真と交換することができます。 まず、例の写真アプリの Main.axml レイアウトに Random Pick ボタンが追加されます。
<?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">
<Button
android:id="@+id/randPickButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Random Pick" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:scrollbars="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
次に、メイン アクティビティの OnCreate
メソッドの末尾に、レイアウトで Random Pick
ボタンを見つけてハンドラーをアタッチするコードを追加します。
Button randomPickBtn = FindViewById<Button>(Resource.Id.randPickButton);
randomPickBtn.Click += delegate
{
if (mPhotoAlbum != null)
{
// Randomly swap a photo with the first photo:
int idx = mPhotoAlbum.RandomSwap();
}
};
このハンドラーは、[ランダム選択] ボタンがタップされたときにフォト アルバムの RandomSwap
メソッドを呼び出します。 RandomSwap
メソッドは、データ ソース内の最初の写真を含む写真をランダムに入れ替え、ランダムに入れ替えた写真のインデックスを返します。 このコードを使用してサンプル アプリをコンパイルして実行すると、[ランダム選択] ボタンをタップしても、RecyclerView
でデータ ソースへの変更が認識されないため、表示が変更されることはありません。
データ ソースの変更後も RecyclerView
を最新の状態に維持するには、[ランダム選択] クリック ハンドラーを変更して、変更されたコレクション内の各項目に対してアダプターの NotifyItemChanged
メソッドを呼び出す必要があります (この場合、最初の写真と入れ替わった写真の 2 つの項目が変更されました)。 これにより、RecyclerView
は、データ ソースの新しい状態と一致するようにその表示を更新します。
Button randomPickBtn = FindViewById<Button>(Resource.Id.randPickButton);
randomPickBtn.Click += delegate
{
if (mPhotoAlbum != null)
{
int idx = mPhotoAlbum.RandomSwap();
// First photo has changed:
mAdapter.NotifyItemChanged(0);
// Swapped photo has changed:
mAdapter.NotifyItemChanged(idx);
}
};
[ランダム選択] ボタンをタップすると、コレクション内のさらに下にある写真がコレクション内の最初の写真と入れ替わったことを示す表示が RecyclerView
で更新されます。
もちろん、NotifyItemChanged
を 2 回呼び出す代わりに NotifyDataSetChanged
を呼び出すこともできますが、そうすると、コレクション内の 2 つの項目だけが変更されたにもかかわらず、RecyclerView
がコレクション全体を強制的に更新します。 NotifyItemChanged
を呼び出す方が NotifyDataSetChanged
を呼び出すよりもかなり効率的です。