Buscar (Don't Edit)

sábado, 12 de octubre de 2013

Patrones de Diseño para Android: El patrón ViewHolder

Aquí en MoztroDev siempre hemos tratado de cuidar cada tutorial que hacemos, y es que, además de enseñarles cómo realizar cada una de las técnicas, procesos, etc. que ven en cada uno de los tutoriales, siempre tratamos de inculcar que lleven a cabo buenas prácticas de programación, tales como patrones de diseño, etc., y precisamente hoy les traigo un tutorial sobre el patrón ViewHolder para android, que si bien podemos aplicar muchos de los patrones que usamos en Java, éste patrón es usado en un caso muy particular en el desarrollo para dispositivos android: los ListView. Veamos que tal...
¿En que nos beneficia?
El patrón ViewHolder tiene un único propósito en su implementación: mejorar el performance. Y es que cuando hablamos de aplicaciones móviles, el rendimiento es un tema de gran importancia. 

¿Cómo funciona?
Lo que hacemos con el ViewHolder es mantener una referencia a los elementos de nuestro ListView mientras el usuario realiza scrolling en nuestra aplicación. Así que cada vez que obtenemos la vista de un item, evitamos las frecuentes llamadas a findViewById, la cuál se realizaría únicamente la primera vez y el resto llamaríamos a la referencia en el ViewHolder, ahorrándonos procesamiento. 

Código, código, código!!!
Ok, empecemos.
Lo primero será crear un nuevo proyecto Android en Eclipse, lo nombraré ViewHolderTutorial. Y lo que haremos será una lista de post, como un lector de feeds pero sin funcionalidad, eso lo dejaremos para el siguiente tutorial.
Una vez creado el proyecto nos vamos a la carpeta res->layout, y añadiremos 2 nuevos xml:
list_activity.xml
Esta será la vista de nuestra MainActivity.
item.xml
Ésta será la vista que tendrá cada uno de los elementos de nuestra lista. Lo primero será crearla como un RelativeLayout.
Ahora le añadiremos una imagen y dos TextView para el nombre y la fecha del post.

Nuestro xml debería entonces verse de la siguiente manera.

Ya que tenemos nuestras vistas, vayamos a definir el modelo. Para esto crearemos una nueva clase llamada Post que usaremos para representar cada uno de los elementos de nuestra lista y la crearemos de la siguiente manera.

Como pueden ver es una clase simple, con 3 atributos y sus correspondientes getters y setters, y dos constructores. Ahora vayamos a nuestra MainActivity.
Haremos que nuestra MainActivity herede de ListActivity y que tome la vista que creamos como list_activity.xml.
Muy bien, recordemos entonces como realizar una ListView con elementos a mostrar. Toda ListView obtiene sus elementos de un Adapter, el cual puede ser un ListAdapter o un ArrayAdapter el cual toma un xml como vista y un conjunto de valores que son los elementos ¿correcto? Pues bien, lo que haremos nosotros será crear nuestro propio Adapter que herede de ArrayAdapter y al cual parametrizaremos con nuestro objeto Post. De ésta manera tendremos un Adapter al cual llenaremos con nuestros objetos y el cual utilizará el patrón ViewHolder para incrementar el rendimiento.
Entonces una vez explicado lo anterior, crearemos una nueva clase llamada PostAdapter que extenderá de ArrayAdapter y como dije anteriormente la parametrizaremos para que reciba nuestro objeto Post.

Lo primero que nos indicará Eclipse al extender de ArrayAdapter será que implementemos su constructor, entonces lo implementamos.
A continuación añadiremos los siguientes atributos:

mContext. Es el contexto de nuestra aplicación, el cuál le pasaremos como argumentos al instanciar nuestro PostAdapter.
postList. Es nuestra lista de objetos Post.
imagen. Es el widget que mostrará la imagen del post.
titulo y fecha. Son TextView donde se mostrará la información del Titulo y Fecha del post.

Ahora en el constructor añadiremos lo siguiente:
Aquí simplemente seteamos nuestros atributos mContext y postList a los que recibe nuestro objeto PostAdapter al ser instanciado.

Lo que sigue es crear el método getView, que es el que obtiene los elementos del ListView y los muestra en pantalla mientras se realiza el scrolling. Primero codificaré éste método sin utilizar ViewHolder para que noten la diferencia una vez implementado el patrón.
Explicaré brevemente el método, lo primero que hace es obtener el inflater del contexto e inflar la vista (convertView) del item con el item.xml que creamos anteriormente, en seguida realizamos los findViewById para castear los widgets a nuestros atributos titulo, fecha e imagen. Lo siguiente creamos un nuevo objeto post y obtenemos el elemento actual de la vista mediante la posición. Dicho objeto nos servirá para setearle la información a nuestros atributos y mostrarlos en pantalla, para finalizar retornamos la vista del item. Si se fijan en el método podrán darse cuenta que cada vez que queramos mostrar el item en pantalla tendremos que "buscarlos" nuevamente a traves de findViewById ya que no guardamos ninguna referencia a ellos y es como si estuvieramos creando un nuevo item cada vez que se realiza el método getView. Por mientras lo dejaremos así como está y regresaremos a nuestra MainActivity.
Crearemos los siguientes atributos:
Un Array de objetos Post el cual hidrataremos con nuestros objetos y un PostAdapter que nos servirá para cargar nuestro ListView con elementos.
Crearé un método privado que me servirá para crear datos y añadirlos a nuestra lista de objetos.
A continuación en el método onCreate llamaremos al método llenarLista para cargar nuestra lista de objetos e instanciaremos nuestro PostAdapter pasandole la lista de objetos hidratada, y para finalizar seteamos nuestro postAdapter a nuestra actividad con setListAdapter.
Si corremos nuestra aplicación, deberíamos poder ver los siguiente.
Ok, todo correcto, ya tenemos nuestro lista cargada con objetos, pero, el objetivo del tutorial es como hacerlo mas óptimo y mejorar el rendimiento de la aplicación a través del patrón ViewHolder, así que regresemos a nuestra clase PostAdapter.
Crearemos una clase estática dentro de nuestra clase PostAdapter y la llamaremos ViewHolder.
Ahora realizare algunos cambios en el método getView para mostrarles como implementar el patrón. El método quedará de la siguiente manera.
Muy bien, ahora entendamos un poco lo que cambio, de entrada declaramos un objeto ViewHolder, a continuación preguntamos primero si la vista es nula, de ser así, la inflamos a través del inflater e instanciamos nuestro objeto viewHolder y casteamos los widget a sus atributos a través del findViewById (aquí estamos guardando la referencia), una vez hecho ésto seteamos el viewHolder a la vista a través de setTag, éste proceso se realizará una sola vez para cada vista (la primera vez), ya que al momento de llamar nuevamente a getView, dicha vista ya no será nula y entonces lo único que hará será recoger la referencia del viewHolder a través de getTag,  lo demás queda de igual manera solamente que ahora seteamos la información del objeto a los atributos del viewHolder.
Cómo pueden observar el ViewHolder tiene un fin muy específico pero que mejora bastante el performance de nuestra aplicación, solo faltaría medir el rendimiento con alguna herramienta para verificar de cuánto es la mejora.

Bueno, eso sería todo por hoy. Saludos. ;)

No hay comentarios.:

Publicar un comentario