Andrea, parte de este gran staff, nos hizo el favor de crear esta sección ya que conoce perfectamente este tema.
Solución:
Comprender cómo funciona el reciclaje de vista de lista
Cómo funciona el mecanismo de reciclaje de ListView
No puede reciclar una fila que está actualmente en uso. El enlace anterior explica cómo funciona el mecanismo de reciclaje de vista de lista
Entonces, ¿cuál es el beneficio de usar ViewHolder?
citando documentos
Su código podría llamar findViewById()
con frecuencia durante el desplazamiento de ListView, lo que puede ralentizar el rendimiento. Incluso cuando el adaptador devuelve una vista inflada para reciclar, aún debe buscar los elementos y actualizarlos. Una forma de evitar el uso repetido de findViewById()
es usar el patrón de diseño “portador de vista”.
public View getView(int position, View convertView, ViewGroup parent)
ViewHolder holder;
if (convertView == null) // if convertView is null
convertView = mInflater.inflate(R.layout.mylayout,
parent, false);
holder = new ViewHolder();
// initialize views
convertView.setTag(holder); // set tag on view
else
holder = (ViewHolder) convertView.getTag();
// if not null get tag
// no need to initialize
//update views here
return convertView;
Te perdiste la parte importante convertView.setTag(holder)
y holder = (ViewHolder) ConvertView.getTag()
http://developer.android.com/training/improving-layouts/smooth-scrolling.html
A medida que recorre su ListView, solo se muestran un puñado de vistas en un momento dado. Esto significa que no tiene que crear instancias de una vista para cada elemento en su adaptador; cuando una vista se desplaza fuera de la pantalla, se puede reutilizar, o reciclado.
El reciclaje de vistas y el patrón ViewHolder no son lo mismo. El patrón ViewHolder es únicamente para reducir el número de view.findViewById(int)
llamadas que haces. El patrón ViewHolder solo funciona cuando aprovecha el reciclaje de vistas.
En getView(int position, View convertView, ViewGroup parent)
los convertView
el parámetro es cualquiera null o es una vista que ha sido reciclada: aún tendrá los datos de un elemento de lista diferente vinculado a ella.
Sin el patrón ViewHolder, aún puede aprovechar el reciclaje de vistas (es decir, no crear instancias de vistas a ciegas):
public View getView(int position, View convertView, ViewGroup parent)
View view = convertView;
if (view == null)
view = // inflate new view
ImageView imageView = (ImageView) view.findViewById(R.id.listitem_image);
TextView textView = (TextView) view.findViewById(R.id.listitem_text);
TextView timestampView = (TextView) view.findViewById(R.id.listitem_timestamp);
ProgressBar progressSpinnerView = (ProgressBar) view.findViewById(R.id.progress_spinner);
// TODO: set correct data for this list item
// imageView.setImageDrawable(...)
// textView.setText(...)
// timestampView.setText(...)
// progressSpinnerView.setProgress(...)
return view;
Arriba hay un ejemplo de reciclaje de vistas: no inflamos una nueva vista para cada fila; solo inflamos una vista si no se nos da una para reutilizarla. Evitar tener que inflar una vista es la parte que definitivamente ayudará con el rendimiento al desplazarse por su lista: aproveche el reciclaje de vistas.
Entonces, ¿para qué sirve el ViewHolder? Actualmente estamos haciendo 4x findViewById(int)
por cada elemento, independientemente de si la fila en sí ya existía. Como findViewById(int)
itera recursivamente un ViewGroup hasta que encuentra un descendiente con la ID dada, esto es un poco inútil para nuestras vistas recicladas: estamos volviendo a encontrar vistas a las que ya tenemos referencias.
Evite esto usando un objeto ViewHolder para contener referencias a las subvistas después de “encontrarlas”:
private static class ViewHolder
final TextView text;
final TextView timestamp;
final ImageView icon;
final ProgressBar progress;
ViewHolder(TextView text, TextView timestamp, ImageView icon, ProgressBar progress)
this.text = text;
this.timestamp = timestamp;
this.icon = icon;
this.progress = progress;
View.setTag(Object)
le permite decirle a la Vista que contenga un objeto arbitrario. Si lo usamos para contener una instancia de nuestro ViewHolder después de hacer nuestro findViewById(int)
llamadas, entonces podemos usar View.getTag()
en vistas recicladas para evitar tener que hacer las llamadas una y otra vez.
public View getView(int position, View convertView, ViewGroup parent)
View view = convertView;
if (view == null)
view = // inflate new view
ViewHolder holder = createViewHolderFrom(view);
view.setTag(holder);
ViewHolder holder = view.getTag();
// TODO: set correct data for this list item
// holder.icon.setImageDrawable(...)
// holder.text.setText(...)
// holder.timestamp.setText(...)
// holder.progress.setProgress(...)
return view;
private ViewHolder createViewHolderFrom(View view)
ImageView icon = (ImageView) view.findViewById(R.id.listitem_image);
TextView text = (TextView) view.findViewById(R.id.listitem_text);
TextView timestamp = (TextView) view.findViewById(R.id.listitem_timestamp);
ProgressBar progress = (ProgressBar) view.findViewById(R.id.progress_spinner);
return new ViewHolder(text, timestamp, icon, progress);
Los beneficios de rendimiento de esta optimización son cuestionables, pero ese es el beneficio de ViewHolder.
ViewHolder
patrón de diseño se utiliza para acelerar la representación de su ListView
– en realidad, para que funcione sin problemas, findViewById es bastante costoso (analiza DOM) cuando se usa cada vez que se representa un elemento de la lista, debe atravesar su jerarquía de diseño y también instanciar objetos. Dado que las listas pueden volver a dibujar sus elementos con bastante frecuencia durante el desplazamiento, dicha sobrecarga puede ser sustancial.
Puedes encontrar una buena explicación de cómo funciona esto en:
a partir del minuto 10, ha explicado el patrón de diseño de ViewHolder por expertos de Google.
[edit]
findViewById no crea instancias de nuevos objetos, solo atraviesa la jerarquía: aquí hay una referencia http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/view/ViewGroup.java#3610