次の方法で共有


RecyclerView の例を拡張する

基本的な RecyclerView の例で説明されている基本的なアプリは、実際には大したことは行いません。単にスクロールして、閲覧しやすくするために写真項目の固定リストを表示します。 実際のアプリケーションでは、ユーザーはディスプレイ内の項目をタップしてアプリを操作できることを期待しています。 また、基になるデータ ソースが変更する (またはアプリによって変更される) 可能性があり、表示の内容はこれらの変更と一貫性を維持する必要があります。 次のセクションでは、項目クリック イベントを処理し、基になるデータ ソースが変更されたときに RecyclerView を更新する方法について説明します。

項目クリック イベントの処理

ユーザーが RecyclerView 内の項目にタッチすると、項目クリック イベントが生成され、どの項目にタッチしたかをアプリに通知します。 このイベントは、項目ビュー (ビュー ホルダーにラップされている) RecyclerView によって生成されるのではなく、タッチを検出し、これらのタッチをクリック イベントとして報告します。

項目クリック イベントを処理する方法を説明するために、次の手順では、基本的な写真表示アプリを変更して、ユーザーがタッチした写真を報告する方法について説明します。 サンプル アプリで項目クリック イベントが発生すると、次のシーケンスが実行されます。

  1. 写真の CardView で項目クリック イベントを検出し、アダプターに通知します。

  2. アダプターは、イベント (項目の位置情報を含む) をアクティビティの項目クリック ハンドラーに転送します。

  3. アクティビティの項目クリック ハンドラーは、項目クリック イベントに応答します。

まず、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 内に ImageViewTextView が必要です。ここでは、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 を呼び出すよりもかなり効率的です。