Compartir vía


Extensión del ejemplo RecyclerView

La aplicación básica descrita en un ejemplo básico de RecyclerView realmente no hace mucho: simplemente se desplaza y muestra una lista fija de elementos de fotografía para facilitar la exploración. En las aplicaciones del mundo real, los usuarios esperan poder interactuar con la aplicación pulsando elementos en la pantalla. Además, el origen de datos subyacente puede cambiar (o cambiarlo la aplicación) y el contenido de la pantalla debe ser coherente con estos cambios. En las secciones siguientes, aprenderá a controlar eventos de clic de elemento y a actualizar RecyclerView cuando cambia el origen de datos subyacente.

Controlar eventos de clic de elemento

Cuando un usuario toca un elemento en RecyclerView, se genera un evento de clic de elemento para notificar a la aplicación qué elemento se tocó. Este evento no se genera mediante RecyclerView: en su lugar, la vista de elementos (que está ajustada en el soporte de la vista) detecta toques e informa de estos toques como eventos de clic.

Para ilustrar cómo controlar los eventos de clic de elementos, los pasos siguientes explican cómo se modifica la aplicación de visualización de fotos básica para informar de qué fotografía ha sido tocada por el usuario. Cuando se produce un evento de clic de elemento en la aplicación de ejemplo, se realiza la siguiente secuencia:

  1. La CardView de la fotografía detecta el evento de clic de elementos y notifica al adaptador.

  2. El adaptador reenvía el evento (con información de posición del elemento) al controlador de clic de elemento de la actividad.

  3. El controlador de clic de elemento de la actividad responde al evento de clic de elementos.

En primer lugar, se agrega un miembro del controlador de eventos llamado ItemClick a la definición de clase PhotoAlbumAdapter:

public event EventHandler<int> ItemClick;

A continuación, se agrega un método de controlador de eventos de clic de elementos a MainActivity. Este controlador muestra brevemente una notificación del sistema que indica qué elemento de fotografía se tocó:

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

A continuación, se necesita una línea de código para registrar el controlador OnItemClick con PhotoAlbumAdapter. Un buen lugar para hacerlo es inmediatamente después de crearse PhotoAlbumAdapter:

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

En este ejemplo básico, el registro del controlador tiene lugar en el método de OnCreate la actividad principal, pero una aplicación de producción podría registrar el controlador en OnResume y anular el registro en OnPause: vea Ciclo de vida de actividad para obtener más información.

PhotoAlbumAdapter ahora llamará a OnItemClick cuando reciba un evento de clic de elemento. El siguiente paso consiste en crear un controlador en el adaptador que genera este evento ItemClick. El método siguiente, OnClick, se agrega inmediatamente después del método del adaptador ItemCount:

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

Este método OnClick es el agente de escucha del adaptador para los eventos de clic de elemento de las vistas de elementos. Para que este agente de escucha se pueda registrar con una vista de elemento (a través del titular de la vista de elemento), el constructor PhotoViewHolder debe modificarse para aceptar este método como argumento adicional y registrar OnClick con el evento Click de vista de elementos. Este es el constructor modificado 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);
}

El parámetro itemView contiene una referencia al objeto CardView que el usuario tocó. Tenga en cuenta que la clase base del titular de vista conoce la posición de diseño del elemento (CardView) que representa (a través de la propiedad LayoutPosition) y esta posición se pasa al método OnClick del adaptador cuando se produce un evento de clic de elemento. El método OnCreateViewHolder del adaptador se modifica para pasar el método OnClick del adaptador al constructor del titular de la vista:

PhotoViewHolder vh = new PhotoViewHolder (itemView, OnClick);

Ahora, al compilar y ejecutar la aplicación de visualización de fotos de ejemplo, pulsar una foto en la pantalla hará que aparezca una notificación del sistema que informa de qué fotografía se tocó:

Notificación del sistema de ejemplo que aparece cuando se pulsa una tarjeta fotográfica

En este ejemplo se muestra solo un enfoque para implementar controladores de eventos con RecyclerView. Otro enfoque que se podría usar aquí es colocar eventos en el titular de la vista y hacer que el adaptador se suscriba a estos eventos. Si la aplicación de fotos de ejemplo proporcionaba una funcionalidad de edición de fotos, se requerirían eventos independientes para ImageView y TextView dentro de cada CardView: al tocar en el TextView se iniciaría un cuadro de diálogo EditView que permite al usuario editar el título y tocar en el ImageView iniciaría una herramienta de interacción de fotos que permite al usuario recortar o girar la foto. En función de las necesidades de la aplicación, debe diseñar el mejor enfoque para controlar y responder a eventos táctiles.

Para demostrar cómo RecyclerView se puede actualizar cuando cambia el conjunto de datos, la aplicación de visualización de fotos de ejemplo se puede modificar para seleccionar aleatoriamente una foto en el origen de datos y intercambiarla con la primera foto. En primer lugar, se agrega un botón Selección aleatoria al diseño Main.axml de la aplicación de fotos de ejemplo:

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

A continuación, el código se agrega al final del método OnCreate de la actividad principal para buscar el botón Random Pick en el diseño y adjuntar un controlador a él:

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

Este controlador llama al método RandomSwap del álbum de fotos cuando se pulsa el botón Selección aleatoria. El método RandomSwap intercambia aleatoriamente una foto con la primera foto del origen de datos y, a continuación, devuelve el índice de la foto intercambiada aleatoriamente. Al compilar y ejecutar la aplicación de ejemplo con este código, al pulsar el botón Selección aleatoria no se produce un cambio en la pantalla porque RecyclerView no es consciente del cambio en el origen de datos.

Para mantener RecyclerView actualizado después de que el origen de datos cambie, el controlador de clic de selección aleatoria debe modificarse para llamar al método NotifyItemChanged del adaptador para cada elemento de la colección que ha cambiado (en este caso, dos elementos han cambiado: la primera foto y la foto intercambiada). Esto hace que RecyclerView actualice su presentación para que sea coherente con el nuevo estado del origen de datos:

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

Ahora, cuando se pulsa el botón Selección aleatoria, RecyclerView actualiza la pantalla para mostrar que se ha intercambiado una foto más abajo en la colección con la primera foto de la colección:

Primera captura de pantalla antes del intercambio, segunda captura de pantalla después del intercambio

Por supuesto, se podría haber llamado a NotifyDataSetChanged en lugar de realizar las dos llamadas a NotifyItemChanged, pero si lo hace, forzaría a RecyclerView a actualizar toda la colección aunque solo se hubieran cambiado dos elementos de la colección. Llamar a NotifyItemChanged es significativamente más eficaz que llamar a NotifyDataSetChanged.