Como optimizar una ListView con imagenes de internet | Tutoriales

Como optimizar una ListView con imagenes de internet

¿Cómo optimizar una listview en Android?

Nuestro equipo especializado en apps ha preparado este post para explicar a la hora de personalizar una ListView en Android con más componentes que un simple texto o imágenes almacenadas en el dispositivo, entran en juego otros factores que en su mayoría afectan a la experiencia de usuario y a la gestión de memoria del dispositivo.

Partiremos de una sencilla ListView cuyos items serán una ImageView y una TextView. A continuación detallo el layout para los elementos de la lista:

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<ImageView
android:id="@+id/image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp" />

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium" />

</LinearLayout>

Una primera optimización nos permite descargar imágenes de internet de manera que el hilo de ejecución principal (en el que se toma en cuenta la interacción del usuario) no se vea afectado. Así, el scroll de la ListView es suave y no espera a que las imágenes se hayan descargado para permitir los eventos de usuario. Siguiendo los pasos que comenta Guilles Debunne en el Blog de Android Developers, dispondremos de una clase utilitaria para descargar imágenes y asignarlas a una ImageView por medio de una ASyncTask. Además podemos añadir una sencilla caché para las imágenes descargadas recientemente de manera que no tengamos que descargarlas siempre

Esta primera optimización presenta un problema a partir de un número considerable de elementos en la lista, puesto que genera mucho tráfico de datos y en scrolls rápidos lanza demasiadas AsyncTask, por lo que el rendimiento decae considerablemente e incluso provoca excepciones por el número de tareas lanzadas. Nuestra segunda optimización va dirigida al adaptador de la lista, más concretamente a su método getView(int position, View convertView, ViewGroup parent). Crearemos un adaptador eficiente que reutilice las vistas de sus elementos y así se reduzca su número sin colapsar la memoria a base de objetos. Esto lo haremos reasignando la vista que nos viene como parámetro en el método getView(). Además, utilizamos una clase estática auxiliar que contendrá nuestras ImageView y TextView, lo que reduce significativamente el número de llamadas al método findViewById(int id),  costoso en tiempo.

Así quedaría nuestro adaptador, junto con la clase auxiliar:

public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder;
if (convertView == null) {

convertView = inflater.inflate(R.layout.list_item, parent, false);
holder = new ViewHolder();
holder.image = (ImageView)convertView.findViewById(R.id.image);
holder.text = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(holder);

else holder = (ViewHolder) convertView.getTag();

imagesDownloader.download("La url de la imagen", holder.image);
holder.text.setText("Nuestro texto");

return convertView;

}

static class ViewHolder {

public ImageView image;
public TextView text;

}

Con esta optimización salvamos problemas de memoria importantes, así como evitamos que se lancen AsyncTask por cada elemento de la lista al hacer un scroll muy rápido, ya que los elementos de la lista se van reciclando conforme entran/salen de la porción de lista que vemos en pantalla.

Por último, aprovecharemos el ScrollListener que se puede asignar a toda ListView para evitar poner a descargar imágenes mientras hacemos scroll, por tanto sólo cuando la lista esté detenida descargará las imágenes de sus elementos de internet. De esta manera ganamos en eficiencia, lo que tiene una repercusión directa en la interacción del usuario con la lista. Es interesante aprovechar que nuestra clase de utilidad para descargar imágenes en segundo plano nos permite asignar una imagen por defecto, que se mostrará mientras dure el proceso de carga de la imagen real, y que utilizaremos mientras la lista no esté detenida. La Activity que aloje nuestra lista deberá implementar por tanto OnScrollListener, cuyos métodos son:

public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {

}

public void onScrollStateChanged(AbsListView view, int scrollState) {

switch (scrollState) {

case OnScrollListener.SCROLL_STATE_IDLE:
mBusy = false;
mAdapter.notifyDataSetChanged();
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
mBusy = true;
break;
case OnScrollListener.SCROLL_STATE_FLING:
mBusy = true;
break;

}

}

Con el flag mBusy controlamos la imagen que se va a mostrar en nuestra lista a través del método getView() de la siguiente forma:

if (!mBusy) {

imagesDownloader.download("La url de la imagen", holder.image);

} else holder.image.setImageResource("La imagen por defecto");

Con esta serie de optimizaciones tendremos listas de objetos que pueden extenderse tanto como quieran sin introducir problemas en la interacción del usuario y manteniendo el uso de memoria muy ajustado, lo que favorecerá la impresión que causen nuestras aplicaciones Android.

Síguenos en Facebook, Google+, Linkedin y Twitter.

Facebook Google+ Linkedin Twitter