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:
- Si es la primera vez que el elemento va a ser mostrado en pantalla, mandar a llamar a findViewById.
- 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).
Referencias
- 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].
Tu comentario
opiniones