Custom cells en Xamarin.Forms
En un post anterior les había hablado sobre sobre cómo crear un control personalizado en Xamarin.Forms, pero, ¿sabías que puedes hacer lo mismo con las celdas de un ListView
o TableView
? en este post voy a explicar cómo hacerlo. Esta es una solución ideal si tienes conocimiento de creación de interfaces en iOS o Android.
En Forms
Como siempre, cuando vamos a implementar funcionalidades específicas para cada plataforma en Xamarin.Forms, iniciamos con una clase dentro del proyecto de Forms, esta debe ser una abstracción de lo que vamos a implementar. En este caso debe ser una clase que derive de ViewCell
:
Además, dentro de la clase le agregamos una propiedad llamada ContactName
y su respectiva declaración de BindableProperty
, tu deberías agregarle las que necesites:
Por último, sobrescribe el método OnBindingContextChanged
, este método es llamado cada vez que el objeto enlazado con la celda cambia. Cuando este objeto cambie, nosotros vamos a cambiar el valor de la propiedad. Esto cobrará relevancia más adelante:
En Android
Diseño
En Android hay que implementar la celda, en esta plataforma la forma por excelencia para crear interfaces gráficas es a través de archivos XML. En este caso, los archivos de interfaz gráfica deben ir en la carpeta Resources/layout
, yo llamé a la celda ContactCell.axml
. El contenido es un simple RelativeLayout
con un par de TextViews
dentro (asegúrate de establecerle un ID a cada control que vayas a usar):
Inclusive puedes utilizar el editor gráfico de tu preferencia:
Wrapper
Lo siguiente es crear una clase wrapper que envuelva a nuestro archivo XML, esto con la finalidad de poder acceder más fácil a los controles y permitir que nuestra celda funcione con las CachingStrategy
dentro de las listas de Forms. La clase debe heredar de un contenedor de Android y implementar la interfaz INativeElementView
de Xamarin.Forms:
Dentro de esta clase agrega un par de propiedades, la primera para hacer referencia al control de Forms, y la segunda implementando la interfaz INativeElementView
:
Un par de TextView
para hacer referencia a lo declarado en el layout xml:
En el constructor hay que tomar el Context
y una instancia de la clase de la celda en Forms. Tomando el Context
vamos a crear una instancia de la celda, empleando el LayoutInflater
, luego, con esta instancia a la mano, recuperamos los TextView
.
Por último, agregamos un método que nos permitirá actualizar los valores de esta celda:
### Renderer
Luego, lo que hay que hacer es implementar el custom renderer para enlazar la celda de Forms con la nativa que acabamos de desarrollar. El renderer debe derivar de ViewCellRenderer
:
Dentro del renderer agrega una propiedad para contener una instancia de la celda nativa:
Después toca sobrescribir un par de métodos, GetCellCore
y OnCellPropertyChanged
, el primero que se ejecutará cada vez que el sistema necesite crear una nueva celda. Dentro de éste trataremos de convertir el argumento convertView
a una instancia de la celda nativa (aquí es donde se realiza el reciclado de celdas) y si no es posible, creamos una nueva. Antes de terminar la ejecución del método, llamamos a UpdateCell
para establecer los valores correctos:
Dentro de OnCellPropertyChanged
revisamos qué propiedad se modificó y si se modificó la que nos interesa, actualizamos los valores de la celda nuevamente.
En iOS
En iOS también tenemos la opción de crear celdas usando código, o como en este caso, archivos de interfaz gráfica .xib
, para crear uno en Visual Studio para Mac:
Diseño
Esto generará un par de archivos ContactCell.cs
y ContactCell.xib
, es el segundo archivo en el que se edita la interfaz. Yo inclusive lo edité con el editor de Xcode:
Wrapper
Una vez que está creada la celda, vamos a echarle un ojo a su clase asociada ContactCell.cs
, esta clase es la que actuará como el wrapper de la interfaz. Entonces, lo que hay que hacer es que implemente la interfaz INativeElementView
:
Luego, verás unas partes de código que se generaron automáticamente, estas nos serán de gran ayuda cuando sea la hora de crear las celdas.
Ya por último, añade una propiedad desde la cual puedas hacer referencia a un tipo ContactCell
del proyecto de Forms, liego debes agregar una propiedad pública de tipo Element
para cumplir con la interfaz INativeElementView
y por último, agrega una forma de actualizar el valor de los campos dentro de la celda:
Renderer
Ya sabes, hay que implementar un custom renderer, este igual heredará de ViewCellRenderer
:
Al igual que en el de Android, debes agregar un campo para mantener una referencia a la celda nativa, en este caso se llama iOSContactCell
porque usé un alias:
Ya para terminar, hay que sobrescribir el método GetCell
que es llamado cada vez que una celda necesita ser creada. Lo primero que hay que hacer es tratar de reutilizar la celda que viene en el argumento reusableCell
. Si no se puede (si es nula), instanciamos una nueva celda a partir del Nib que se creó junto con nuestro archivo de interfaz gráfica. Si, por otro lado, se puede reutilizar la celda, tenemos que eliminarle el evento PropertyChanged
a la celda de Forms, más de esto en corto.
Después, y ya una vez que tenemos una celda nativa que podemos usar, establecemos la propiedad FormsContactCell
para que haga referencia a la celda de Forms que está vinculada con la celda nativa. A esta misma le agregamos un manejador de evento a PropertyChanged
, esto con la finalidad de estar atentos a cuando alguna propiedad de nuestra celda de Forms cambia. Por último, actualizamos la celda nativa para que refleje los valores más recientes.
El manejador de evento es muy simple. Primero se comprueba que se haya modificado la propiedad que nos interesa, y después actuamos en consecuencia.
Eso es todo lo que se requiere hacer para que podamos crear y utilizar celdas personalizadas en Xamarin.Forms, así es como se ve el resultado final:
No olvides que puedes descargar el código fuente directamente de GitHub para que lo pruebes y modifiques a tu gusto.