Rozšíření příkladu RecyclerView

Základní aplikace popsaná v příkladu Basic RecyclerView ve skutečnosti nedělá moc – jednoduše se posune a zobrazí pevný seznam položek fotografie, které usnadňují procházení. V reálných aplikacích uživatelé očekávají, že budou moct s aplikací pracovat klepnutím na položky na displeji. Podkladový zdroj dat může také změnit (nebo ho změnit aplikace) a obsah zobrazení musí zůstat konzistentní s těmito změnami. V následujících částech se dozvíte, jak zpracovat události kliknutí na položku a aktualizovat RecyclerView , když se podkladový zdroj dat změní.

Zpracování událostí kliknutí na položku

Když se uživatel dotkne položky v RecyclerViewpoložce, vygeneruje se událost kliknutí na položku, která aplikaci oznámí, na kterou položku se dotknul. Tato událost není generována RecyclerView – místo toho zobrazení položek (které je zabaleno v držiteli zobrazení) rozpozná dotyky a oznámí tyto dotyky jako události kliknutí.

Abyste si ukázali, jak zpracovat události kliknutí na položku, následující kroky vysvětlují, jak se základní aplikace pro prohlížení fotek změnila tak, aby hlásila, na kterou fotografii uživatel kontaktoval. Když v ukázkové aplikaci dojde k události kliknutí na položku, provede se následující posloupnost:

  1. Fotografie CardView zjistí událost kliknutí na položku a oznámí adaptéru.

  2. Adaptér předá událost (s informacemi o poloze položky) obslužné rutině kliknutí na položku aktivity.

  3. Obslužná rutina kliknutí na položku aktivity reaguje na událost kliknutí na položku.

Nejprve se do definice třídy přidá PhotoAlbumAdapter člen ItemClick obslužné rutiny události:

public event EventHandler<int> ItemClick;

Dále je přidána metoda obslužné rutiny události po kliknutí na položku .MainActivity Tato obslužná rutina stručně zobrazí informační zpráva, která indikuje, která položka fotografie byla dotkována:

void OnItemClick (object sender, int position)
{
    int photoNum = position + 1;
    Toast.MakeText(this, "This is photo number " + photoNum, ToastLength.Short).Show();
}

Dále je potřeba řádek kódu k registraci obslužné rutiny OnItemClick v PhotoAlbumAdapter. Dobré místo, kde to udělat, je hned po PhotoAlbumAdapter vytvoření:

mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);
mAdapter.ItemClick += OnItemClick;

V tomto základním příkladu probíhá registrace obslužné rutiny v metodě hlavní aktivity OnCreate , ale produkční aplikace může obslužnou rutinu OnResume zaregistrovat a zrušit její registraci – další informace najdete v OnPause části Životní cyklus aktivity.

PhotoAlbumAdapter bude nyní volat OnItemClick , když obdrží událost po kliknutí na položku. Dalším krokem je vytvoření obslužné rutiny v adaptéru, který vyvolá tuto ItemClick událost. Následující metoda je OnClickpřidána ihned za metodu adaptéru ItemCount :

void OnClick (int position)
{
    if (ItemClick != null)
        ItemClick (this, position);
}

Tato OnClick metoda je naslouchací proces adaptéru pro události kliknutí na položku ze zobrazení položek. Před registrací tohoto naslouchacího procesu v zobrazení položky (prostřednictvím držitele zobrazení položky) PhotoViewHolder musí být konstruktor upraven tak, aby tuto metodu přijal jako další argument, a zaregistrovat OnClick se v události zobrazení Click položky. Tady je upravený PhotoViewHolder konstruktor:

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);
}

Tento itemView parametr obsahuje odkaz na CardView uživatele, na který se uživatel dotkl. Všimněte si, že základní třída držáku zobrazení zná pozici rozložení položky (CardView), která představuje (prostřednictvím LayoutPosition vlastnosti), a tato pozice se předá metodě adaptéru OnClick , když dojde k události kliknutí na položku. Metoda adaptéru OnCreateViewHolder je upravena tak, aby předala metodu adaptéru OnClick konstruktoru view-holder:

PhotoViewHolder vh = new PhotoViewHolder (itemView, OnClick);

Když teď sestavíte a spustíte ukázkovou aplikaci pro prohlížení fotek, klepnutí na fotku na displeji způsobí, že se zobrazí informační zpráva, na kterou fotografii jste se dotkli:

Příklad informačního lístku, který se zobrazí při klepnutí na kartu fotky

Tento příklad ukazuje pouze jeden přístup pro implementaci obslužných rutin událostí s RecyclerView. Dalším přístupem, který by zde mohl být použit, je umístit události na držitele zobrazení a mít adaptér přihlásit k odběru těchto událostí. Pokud ukázková aplikace fotek poskytla funkci úprav fotek, budou pro ImageView jednotlivé události potřeba samostatné události a TextView v každém z nich CardViewse dotkne TextViewEditView dialogového okna, které uživateli umožní upravit popis a dotykové ovládání ImageView by spustilo nástroj pro dotykové ovládání fotky, který uživateli umožní oříznout nebo otočit fotku. V závislosti na potřebách aplikace musíte navrhnout nejlepší přístup pro zpracování a reakci na dotykové události.

Abychom si ukázali, jak RecyclerView se dá aktualizovat při změně sady dat, můžete ukázkovou aplikaci pro prohlížení fotek upravit tak, aby náhodně vybrala fotku ve zdroji dat a prohodí ji s první fotkou. Nejprve se do rozložení Main.axml ukázkové aplikace fotek přidá tlačítko Náhodný výběr:

<?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>

Dále se na konec metody hlavní aktivity OnCreate přidá kód, který vyhledá Random Pick tlačítko v rozložení a připojí k němu obslužnou rutinu:

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();
    }
};

Tato obslužná rutina volá metodu fotoalba RandomSwap při klepnutí na tlačítko Náhodný výběr . Metoda RandomSwap náhodně prohodí fotku s první fotkou ve zdroji dat a pak vrátí index náhodně prohozené fotky. Když zkompilujete a spustíte ukázkovou aplikaci s tímto kódem, klepnutí na tlačítko Náhodný výběr nezpůsobí změnu zobrazení, protože RecyclerView o změně zdroje dat neví.

Chcete-li zachovat RecyclerView aktualizaci po změně zdroje dat, obslužná rutina náhodného kliknutí na výběr musí být změněna tak, aby volala metodu adaptéru NotifyItemChanged pro každou položku v kolekci, která se změnila (v tomto případě se změnily dvě položky: první fotka a prohozená fotografie). To způsobí RecyclerView aktualizaci zobrazení tak, aby bylo konzistentní s novým stavem zdroje dat:

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);
    }
};

Po klepnutí na tlačítko Náhodný výběr se teď aktualizuje zobrazení, RecyclerView aby se zobrazilo, že fotka v kolekci byla prohozena první fotkou v kolekci:

První snímek obrazovky před prohozením, druhý snímek obrazovky po prohození

Samozřejmě bylo NotifyDataSetChanged možné volat místo volání dvou volání NotifyItemChanged, ale to by vynutilo RecyclerView aktualizaci celé kolekce, i když se změnily pouze dvě položky v kolekci. Volání NotifyItemChanged je výrazně efektivnější než volání NotifyDataSetChanged.