¡Atención! Este sitio utiliza cookies.
Si no cambias la configuración de tu navegador, aceptas su uso.

GridViews y Adapters en Android

Los GridViews son muy útiles cuando queremos presentar información —o controles— al usuario en forma de retícula o botonera. A diferencia de los ListView (que ya se trataron en otro artículo), donde la presentación es a modo de persiana, y por tanto cada elemento ocupa todo el ancho de la pantalla, las retículas nos permiten colocar una sucesión de elementos a modo de galería de fotos, tomando todo el ancho disponible con tantas columnas y tantos elementos como sea posible.

Es importante no confundir en Android un GridView con un GridLayout. Mientras que el primero es una única vista (generada en tiempo de ejecución), una suerte de galería de objetos en forma de rejilla, el segundo es simplemente un layout estático, simplemente como forma de disponer elementos, normalmente aquellos que necesitan una presencia y una posición fija.

Antes de nada…

Se dan –por tanto— por sabidos los conceptos de Activity, Fragment, GridView, y por supuesto, el lenguaje Java. Por cierto, este ejemplo sigue una arquitectura tipo MVA, pero no es imprescindible que sepas cómo funciona de momento. Lo comprenderás más adelante.

Android Adapters

Ingredientes para implementar un ‘Activity’ que contenga un ‘GridView’

  • Un Activity para poder contener el Grid.
  • Un Fragment donde realizar únicamente la implementación del grid.
  • Una clase ‘Item’ (el modelo) que encapsule la información de cada celda del grid, como el título, la imagen, etc.
  • Un Adapter para transformar nuestro modelo de datos a celdas visibles en el grid.

Aviso a navegantes. Normalmente desarrollo con Cocoa, así que los nombres de los métodos van a ser laaaargos. Cuestión de preferencia personal, pero sí, estoy saltándome a la torera la costumbre javera (métodos tipo f(x) = ((x – x) + y) / dblThing, etc ). Y las llaves van abajo.

Creación del Activity

Clase HomeActivity:

Layout de HomeActivity:

 Creación del Fragment

Clase de HomeMenuFragment:

Layout de FragmentHomeMenu:

Ya tenemos el Activity y el Fragment, es decir, la parte controladora y sus vistas. Todavía no casan todas las piezas… Quedan dos.

El ‘modelo’ lo compone un solo objeto de tipo Item, que necesitaremos conocer en el último componente: el Adapter; simplemente para poder trasladar los datos hacia el fragment. Esto es similar a una arquitectura MVA (Model-View-Adapter) de la que hablaba antes, solo que mal implementada (hemos instanciado los objetos del grid dentro del Fragment, ¡mal!, pero lo he creído conveniente para no alargar absurdamente el ejemplo).

Esta es la clase Item que representa cualquier elemento a representar en el grid.

Y este es un XML (menú_cell_layout.xml) que usaremos para representar sus datos:

Simple, ¿no?

Ahora viene la parte interesante: el adapter. Aquí es donde se cuece toda la magia. Hasta ahora solo hemos desarrollado los extremos. Este es el código que usamos para “gobernarlos a todos y atarlos en las tinieblas[insertar referencia innecesaria al Señor de los Anillos aquí].

Un Adapter para dom… vale, vale…

¡Ahí va el código!

 ¿Y se supone que tengo que entender ese churro así por las buenas?

Vamos a ir método por método, pero antes, vamos a repasar lo que tenemos hasta ahora:

  1. Un HomeActivity y su layout. La pantalla de arranque de nuestra app. Contiene un contenedor donde calzaremos un fragmento.
  2. Un HomeMenuFragment y su layout. El fragmento donde tenemos la vista del Grid. Será absorbido por el Activity.
  3. Una clase Item. El objeto que representa cada una de las celdas del grid.
  4. Un XML para representar el Item, es decir, el layout para cada celda.
  5. Un Adapter que puebla el Grid a partir de un ArrayList.

Mucho ‘palabro’, pero poca chicha en realidad hasta que llegamos al GridItemsAdapter..

Vamos a explicar cómo funciona este Adapter paso a paso. Este proceso te servirá para multitud de otros casos. Antes que nada, fíjate en que hemos extendido la clase de nuestro adapter desde una genérica y BaseAdapter, que sirve para estos propósitos (es una clase abstracta, así que de todas formas nunca sería posible crear objetos directamente con ella).

El Constructor.

En este punto es donde exigimos lo mínimo para que esta maquinaria funcione: pedimos un contexto (que es básicamente quien nos ha invocado), y un ArrayList de ítems. Los guardamos como variables de clase para usarlos más tarde, cuando hagan falta. El contexto es necesario, en cuanto al array… no tanto. No quiero entrar en el debate de si habría que crear una factoría, hacerlo mediante inyección de dependencia, etc… Pasamos el array de ítems así, directamente desde el Fragment, para ‘atar’ mejor los conceptos (y porque no me apetece hacerlo de la otra forma y ya está, no nos vamos a engañar).

 Métodos accesores.

Como he comentado, estamos heredando de la clase BaseAdapter, la cual es abstracta. Esto quiere decir que debemos cumplir una serie de reglas, ya que al serlo no es posible crear directamente objetos desde ella, sino de sus hijas. Estas reglas son cumplir con el protocolo de implementar estos métodos: “dime cuántos elementos tiene este adapter“, “dime qué ítem hay en esta posición”, y “dime el id que tendría el elemento en esta posición”.

 El método que devuelve las celdas.

Es importante conocer el funcionamiento de este tipo de adapters, y en estas líneas está una de sus claves.

Cuando cargamos un grid en pantalla es posible que haya un porrón de celdas, muchas veces, más de las que caben en la pantalla del móvil. ¿Sería lógico cargar 100 celdas en memoria si solo se van a ver 20? El funcionamiento en este tipo de casos es el siguiente: crear la celda una vez, y en el resto reutilizar la misma cambiando los valores. Así, si tenemos 20 celdas, crearemos una y clonaremos las 19 restantes. ¿Y el resto? Pues conforme vayan apareciendo por pantalla, así de simple. Efectivo, ¿no?

Este código hace justo eso: se nos pasa la vista en la variable “convertView”, que se supone que va a ser nuestra celda. ¿Que no existe?, píllamos el XML de la celda e inflamos la vista. ¿Que ya teníamos una de antes?, pues nada, nos saltamos la parte de creación y la llenamos con los datos del modelo (nuestro ArrayList de ítems, ¿recuerdas?), y listo. Por último, devolvemos la celda para que el adapter siga haciendo su cometido y vuelva a llamarnos para crear la siguiente si es caso.

El resto de métodos no merecen gran explicación:

  • fillViewWithItemAtIndex recibe la celda y una posición para ir al array del modelo y asignarle los valores a la etiqueta y a la imagen.
  • inflatedViewWithContextInParentView se desarrolla en el artículo sobre el LayoutInflater. Simplemente devuelve una instancia de vista a partir un layout en XML.

En definitiva

Dicho rápido y mal (muy mal, de hecho), crear una vista como retícula consiste meramente en colocar un GridView dentro de un Fragment (o Activity) y enlazarlo con un Adapter, el cual devuelve las celdas a su padre a partir de los datos del modelo. Como hemos visto, la magia está dentro de este Adapter. Al estar Android orientado al Model-View-Adapter, esta es la metodología a seguir. Cuando hay patrones de diseño muy estrictos en un framework, mejor olvidarse del traje de Batman y hacer lo que todo el resto de mortales.

No era realmente el objetivo de este artículo aprender a crear un GridView, sino entender que, al no existir los delegados, debemos crearnos nuestros propios Adapters personalizados. Entendido esto, el resto de lo anteriormente explicado queda como una mera excusa para aprender a desarrollarlo con un ejemplo que he creído el más práctico de todos (¿quién no necesita poblar un GridView con celdas propias alguna vez?). Así sabes dónde pescar, ¡y te llevas la caña de regalo!

Si quieres aportar algo, realizar alguna corrección —suelo cometer despistes, no te asustes si ves algo raro—, o simplemente te apetece que desarrolle mejor alguno de los apartados, no dudes en dejar constancia en los comentarios.

¡Muchas gracias!

Escrito por Miguel Hernández Jaso

Autor del blog. Desarrollador especializado en iOS.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Mi Gobierno me obliga a decirle que utilizo 'cookies' en mi página. Si continúa navegando, quiere decir que está conforme con ello.