Naps Tecnología y educación

Optimizar un ListView usando ViewHolder

Veremos cómo optimizar un ListView usando ViewHolder para trabajar con listas grandes sin perder rendimiento en nuestra aplicación.

En el artículo anterior vimos cómo usar un ListView en Android para mostrar una lista de datos.  Ahora bien, ¿qué pasaría si esa lista de datos fuera muy grande? Por ejemplo, en una aplicación que contenga contactos, ésta lista fácilmente puede alcanzar los 100 elementos o más aún. 

Veamos el código propuesto para mostrar un ListView:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;

public class MyAdapter extends BaseAdapter {
    private Context context;
    private int layout;
    private ArrayList<String> names;
    public MyAdapter(Context context, int layout, ArrayList<String> names){
        this.context = context;
        this.layout = layout;
        this.names = names;
    }

    @Override
    public int getCount() {
        return this.names.size();
    }

    @Override
    public Object getItem(int position) {
        return this.names.get(position);
    }

    @Override
    public long getItemId(int id) {
        return id;
    }

    @Override

    public View getView(int position, View convertView, ViewGroup viewGroup) {
        // Copiamos la vista
        View v = convertView;

        //Inflamos la vista con nuestro propio layout
        LayoutInflater layoutInflater = LayoutInflater.from(this.context);

        v= layoutInflater.inflate(R.layout.list_item, null);
        // Valor actual según la posición

        String currentName  = names.get(position);
       
        // Referenciamos el elemento a modificar y lo rellenamos
        TextView textView = (TextView) v.findViewById(R.id.textView);
        textView.setText(currentName);
        //Devolvemos la vista inflada
        return v;
    }

Pues bien, éste código hace que, cada vez que el usuario hace un desplazamiento hacia arriba o hacia abajo en la lista, se ejecuta ésta línea:

TextView textView = (TextView) v.findViewById(R.id.textView);

Como puedes notar, contiene una función findViewById, que es una función bastante pesada dentro de Android.

Una forma de optimizar la aplicación es haciendo los siguiente:

  1. Si es la primera vez que el elemento va a ser mostrado en pantalla, mandar a llamar a findViewById.
  2. Si el elemento ya ha sido mostrado en pantalla, no mandar a llamar a findViewById, sino mostrar el dato ya guardado.

Para realizar esto, utilizamos el patrón ViewHolder, que mejorará el rendimiento de la aplicación y en consecuencia la experiencia de uso. 

ListView usando ViewHolder

Realizaremos los cambios necesarios al código de modo que realice las siguientes instrucciones:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;

public class MyAdapter extends BaseAdapter {

    private Context context;
    private int layout;
    private ArrayList<String> names;

    public MyAdapter(Context context, int layout, ArrayList<String> names){
        this.context = context;
        this.layout = layout;
        this.names = names;
    }

    @Override
    public int getCount() {
        return this.names.size();
    }

    @Override
    public Object getItem(int position) {
        return this.names.get(position);
    }

    @Override
    public long getItemId(int id) {
        return id;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup viewGroup) {
        ViewHolder holder;
        if (convertView == null){
            //Inflamos la vista con nuestro propio layout
            LayoutInflater layoutInflater = LayoutInflater.from(this.context);
            convertView = layoutInflater.inflate(R.layout.list_item, null);
            holder = new ViewHolder();
            // Referenciamos el elemento a modificar y lo rellenamos
            holder.nameTextView = (TextView) convertView.findViewById(R.id.textView);
            convertView.setTag(holder);
        }
        else{
            holder = (ViewHolder) convertView.getTag();
        }

        // Valor actual según la posición
        String currentName  = names.get(position);
        holder.nameTextView.setText(currentName);

        //Devolvemos la vista inflada
        return convertView;
    }
    
    static class ViewHolder{
        private TextView nameTextView; // Solo tenemos un textview
    }
}

Notamos que:

Se determina si es la primera vez que se cargan los datos al analizar si convertView (la vista) viene nulo.

if (convertView == null)

Si es así, realizamos el procedimiento normal, que incluye la llamada (o llamadas, si son varios elementos) hacia findViewById.

Se guarda el dato dentro del atributo nameTextView que pertenece a la clase ViewHolder.

holder.nameTextView = (TextView) convertView.findViewById(R.id.textView);

Cuando el convertView no es nulo, quiere decir que el elemento ya había sido renderizado (ya fue mostrado en pantalla anteriormente), por lo que no se manda a llamar a la función findViewById, sino que se recupera del dato guardado en el holder (o portador).

ListView usando ViewHolder

Referencias

  1. De la Torre (2015). El patrón ViewHolder, qué es y cómo utilizarlo. Disponible en [http://enekodelatorre.com/el-patron-viewholder/] consultado el [17-octubre-2018].
¿Qué te pareció este artículo?
  • Poco informativo ()
  • No era lo que buscaba ()
  • Regular ()
  • Interesante ()
  • Excelente ()
(Visto 1.552 veces)

Tu comentario

opiniones