Ejemplo básico de RecyclerView
Para comprender cómo funciona RecyclerView
en una aplicación típica, en este tema se explora un ejemplo de código sencillo que usa RecyclerView
para mostrar una gran colección de fotos:
RecyclerViewer usa CardView para implementar cada elemento de fotografía en el diseño RecyclerView
. Debido a las ventajas de rendimiento de RecyclerView
, esta aplicación de ejemplo puede desplazarse rápidamente a través de una gran colección de fotos sin problemas y sin retrasos notables.
Un origen de datos de ejemplo
En esta aplicación de ejemplo, un origen de datos de "álbum de fotos" (representado por la clase PhotoAlbum
) proporciona a RecyclerView
el contenido del elemento.
PhotoAlbum
es una colección de fotos con subtítulos; cuando se crea una instancia de ella, se obtiene una colección preparada de 32 fotos:
PhotoAlbum mPhotoAlbum = new PhotoAlbum ();
Cada instancia de foto de PhotoAlbum
expone propiedades que permiten leer su id. de recurso de imagen, PhotoID
, y su cadena de título, Caption
. La colección de fotos se organiza de forma que un indexador pueda acceder a cada foto. Por ejemplo, las siguientes líneas de código acceden al id. y al título del recurso de imagen para la décima foto de la colección:
int imageId = mPhotoAlbum[9].ImageId;
string caption = mPhotoAlbum[9].Caption;
PhotoAlbum
también proporciona un método RandomSwap
que se puede llamar para intercambiar la primera foto de la colección con una foto elegida aleatoriamente en otra parte de la colección:
mPhotoAlbum.RandomSwap ();
Dado que los detalles de implementación de PhotoAlbum
no son relevantes para comprender RecyclerView
, el código fuente de PhotoAlbum
no se presenta aquí.
Diseño e inicialización
El archivo de diseño, Main.axml, consta de un único RecyclerView
dentro de un LinearLayout
:
<?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>
Tenga en cuenta que debe usar el nombre completo android.support.v7.widget.RecyclerView porque RecyclerView
está empaquetado en una biblioteca de soporte técnico. El método OnCreate
de MainActivity
inicializa este diseño, crea una instancia del adaptador y prepara el origen de datos subyacente:
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);
Este código hace lo siguiente:
Crea una instancia del origen de datos
PhotoAlbum
.Pasa el origen de datos del álbum de fotos al constructor del adaptador,
PhotoAlbumAdapter
(que se define más adelante en esta guía). Tenga en cuenta que se considera un procedimiento recomendado pasar el origen de datos como parámetro al constructor del adaptador.Obtiene el
RecyclerView
del diseño.Conecta el adaptador a la
RecyclerView
instancia llamando alRecyclerView
SetAdapter
método como se muestra anteriormente.
Administrador de diseño
Cada elemento de RecyclerView
se compone de un elemento CardView
que contiene una imagen de foto y un título de foto (los detalles se tratan en la sección Titular de la vista a continuación). El LinearLayoutManager
predefinido se usa para diseñar cada CardView
en una disposición de desplazamiento vertical:
mLayoutManager = new LinearLayoutManager (this);
mRecyclerView.SetLayoutManager (mLayoutManager);
Este código reside en el método OnCreate
de la actividad principal. El constructor para el administrador de diseño requiere un contexto, por lo que MainActivity
se pasa usando this
como se ha visto anteriormente.
En lugar de usar el LinearLayoutManager
predefinido, puede conectar un administrador de diseño personalizado que muestre dos elementos CardView
en paralelo, implementando un efecto de animación de giro de página para recorrer la colección de fotos. Más adelante en esta guía, verá un ejemplo de cómo modificar el diseño intercambiándolo por otro administrador de diseños.
Titular de la vista
La clase del titular de vista se denomina PhotoViewHolder
. Cada instancia de PhotoViewHolder
contiene referencias a ImageView
y TextView
de un elemento de fila asociado, que se establece en un CardView
como muestra este diagrama:
PhotoViewHolder
deriva de RecyclerView.ViewHolder
y contiene propiedades para almacenar referencias a ImageView
y TextView
que se muestran en el diseño anterior.
PhotoViewHolder
consta de dos propiedades y un constructor:
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);
}
}
En este ejemplo de código, al constructor PhotoViewHolder
se le pasa una referencia a la vista de elementos primarios (el CardView
) que PhotoViewHolder
encapsula. Tenga en cuenta que siempre reenvía la vista de elemento primario al constructor base. El constructor PhotoViewHolder
llama a FindViewById
en la vista de elementos primarios para buscar cada una de sus referencias de vista secundarias, ImageView
y TextView
, almacenando los resultados en las propiedades Image
y Caption
, respectivamente. Más adelante, el adaptador recupera las referencias de vista de estas propiedades cuando actualiza las vistas secundarias de CardView
con nuevos datos.
Para obtener más información sobre RecyclerView.ViewHolder
, vea la referencia de la clase RecyclerView.ViewHolder.
Adaptador
El adaptador carga cada fila de RecyclerView
con datos para un elemento de fila determinado. Para una fotografía determinada en la posición de fila P, por ejemplo, el adaptador localiza los datos asociados en la posición P dentro del origen de datos y copia estos datos en el elemento de fila en la posición P en la colección RecyclerView
. El adaptador usa el titular de vista para buscar las referencias de ImageView
y TextView
en esa posición para que no tenga que llamar a FindViewById
repetidamente a esas vistas a medida que el usuario se desplaza por la colección y reutiliza las vistas.
En RecyclerViewer, se deriva una clase de adaptador de RecyclerView.Adapter
para crear PhotoAlbumAdapter
:
public class PhotoAlbumAdapter : RecyclerView.Adapter
{
public PhotoAlbum mPhotoAlbum;
public PhotoAlbumAdapter (PhotoAlbum photoAlbum)
{
mPhotoAlbum = photoAlbum;
}
...
}
El miembro mPhotoAlbum
contiene el origen de datos (el álbum de fotos) que se pasa al constructor; el constructor copia el álbum de fotos en esta variable miembro. Se implementan los siguientes métodos RecyclerView.Adapter
necesarios:
OnCreateViewHolder
: crea una instancia del archivo de diseño de elemento y el soporte de vista.OnBindViewHolder
: carga los datos en la posición especificada en las vistas cuyas referencias se almacenan en el titular de la vista especificado.ItemCount
: devuelve el número de elementos de la tabla.
El administrador de diseño llama a estos métodos mientras coloca elementos dentro de RecyclerView
. La implementación de estos métodos se examina en las secciones siguientes.
OnCreateViewHolder
El administrador de diseño llama a OnCreateViewHolder
cuando RecyclerView
necesita un nuevo titular de vista para representar un elemento. OnCreateViewHolder
infla la vista de elemento desde el archivo de diseño de la vista y ajusta la vista en una nueva instancia PhotoViewHolder
. El constructor PhotoViewHolder
localiza y almacena referencias a vistas secundarias en el diseño, tal como se ha descrito anteriormente en Titular de la vista.
Cada elemento de fila se representa mediante un CardView
que contiene un ImageView
(para la foto) y un TextView
(para el título). Este diseño reside en el archivo 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>
Este diseño representa un único elemento de fila en RecyclerView
. El método OnBindViewHolder
(descrito a continuación) copia los datos del origen de datos en ImageView
y TextView
de este diseño.
OnCreateViewHolder
infla este diseño para una ubicación de foto determinada en RecyclerView
y crea una instancia de una nueva instancia PhotoViewHolder
(que localiza y almacena en caché las referencias a las vistas secundarias ImageView
y TextView
en el diseño asociado CardView
):
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;
}
La instancia resultante del titular de la vista, vh
, se devuelve al autor de la llamada (el administrador de diseño).
OnBindViewHolder
Cuando el administrador de diseño está listo para mostrar una vista determinada en el área de pantalla visible de RecyclerView
, llama al método OnBindViewHolder
del adaptador para rellenar el elemento en la posición de fila especificada con contenido del origen de datos. OnBindViewHolder
obtiene la información de la foto para la posición de fila especificada (el recurso de imagen de la foto y la cadena del título de la foto) y copia estos datos en las vistas asociadas. Las vistas se encuentran a través de referencias almacenadas en el objeto del titular de la vista (que se pasa a través del parámetro 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;
}
El objeto de soporte de vista pasado primero debe convertirse en el tipo de titular de vista derivado (en este caso, PhotoViewHolder
) antes de usarlo.
El adaptador carga el recurso de imagen en la vista a la que hace referencia la propiedad Image
del titular de la vista y copia el texto del título en la vista a la que hace referencia la propiedad Caption
del titular de la vista. Esto enlaza la vista asociada con sus datos.
Observe que OnBindViewHolder
es el código que se ocupa directamente de la estructura de los datos. En este caso, OnBindViewHolder
comprende cómo asignar la posición del elemento RecyclerView
a su elemento de datos asociado en el origen de datos. La asignación es sencilla en este caso porque la posición se puede usar como índice de matriz en el álbum de fotos. Sin embargo, los orígenes de datos más complejos pueden requerir código adicional para establecer dicha asignación.
ItemCount
El método ItemCount
devuelve el número de elementos de la colección de datos. En la aplicación de visor de fotos de ejemplo, el recuento de elementos es el número de fotos del álbum de fotos:
public override int ItemCount
{
get { return mPhotoAlbum.NumPhotos; }
}
Para obtener más información sobre RecyclerView.Adapter
, vea la referencia de la clase RecyclerView.ViewHolder.
En resumen
La implementación RecyclerView
resultante de la aplicación de fotos de ejemplo consta de código MainActivity
que crea el origen de datos, el administrador de diseño y el adaptador. MainActivity
crea la instancia de mRecyclerView
, crea instancias del origen de datos y el adaptador, y conecta el administrador de diseño y el adaptador:
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
busca y almacena en caché las referencias de vista:
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
implementa las tres invalidaciones de método necesarias:
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; }
}
}
Cuando se compila y ejecuta este código, se crea la aplicación básica de visualización de fotos, como se muestra en las capturas de pantalla siguientes:
Si no se dibujan sombras (como se muestra en la captura de pantalla anterior), edite Properties/AndroidManifest.xml y agregue el siguiente valor de atributo al elemento <application>
:
android:hardwareAccelerated="true"
Esta aplicación básica solo admite la exploración del álbum de fotos. No responde a eventos de toque de elemento ni controla los cambios en los datos subyacentes. Esta funcionalidad se agrega en Extensión del ejemplo de RecyclerView.
Cambio de LayoutManager
Debido a la flexibilidad de RecyclerView
, es fácil modificar la aplicación para usar un administrador de diseño diferente. En el ejemplo siguiente, se modifica para mostrar el álbum de fotos con un diseño de cuadrícula que se desplaza horizontalmente en lugar de con un diseño lineal vertical. Para ello, se modifica la creación de instancias del administrador de diseño para usar GridLayoutManager
de la manera siguiente:
mLayoutManager = new GridLayoutManager(this, 2, GridLayoutManager.Horizontal, false);
Este cambio de código reemplaza la LinearLayoutManager
vertical por una GridLayoutManager
que presenta una cuadrícula formada por dos filas que se desplazan en la dirección horizontal. Al volver a compilar y ejecutar la aplicación, verá que las fotografías se muestran en una cuadrícula y que el desplazamiento es horizontal en lugar de vertical:
Al cambiar solo una línea de código, es posible modificar la aplicación de visualización de fotos para usar un diseño diferente con un comportamiento diferente. Tenga en cuenta que ni el código del adaptador ni el XML de diseño deben modificarse para cambiar el estilo de diseño.
En el tema siguiente, Extensión del ejemplo de RecyclerView, esta aplicación de ejemplo básica se extiende para controlar eventos de clic de elemento y actualizar RecyclerView
cuando cambia el origen de datos subyacente.