¿Cuál es el beneficio de ViewHolder?

Cuando estás desarrollando un progtwig de Android; y desea tener un ArrayAdapter , puede simplemente tener una clase (la mayoría de veces con el sufijo ViewHolder ) o inflar directamente su convertView y encontrar su vista por identificación.
Entonces, ¿cuál es el beneficio de usar ViewHolder?
El ejemplo de ambos aquí:

if(convertView==null) { convertView = ((Activity)_context).getLayoutInflater().inflate(R.layout.row_phrase, null); } ((TextView)convertView.findViewById(R.id.txtPhrase)).setText("Phrase 01"); 

O bien:

 static class ViewHolder { ImageView leftIcon; TextView upperLabel; TextView lowerLabel; } 

y finalmente en el getView:

 ViewHolder holder = null; if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.row_layout, null, false); holder = new ViewHolder(); holder.leftIcon = (ImageView) view.findViewById(R.id.leftIcon); 

Comprender cómo funciona el reciclaje de listview

Cómo funciona el mecanismo de reciclaje de ListView

No puedes reciclar una fila que esté actualmente en uso. El enlace de arriba explica cómo funciona el mecanismo de reciclaje listview

Entonces, ¿cuál es el beneficio de usar ViewHolder?

Citando documentos

Su código podría llamar a findViewById() frecuencia durante el desplazamiento de ListView, lo que puede ralentizar el rendimiento. Incluso cuando el Adaptador devuelve una vista inflada para reciclar, aún necesita buscar los elementos y actualizarlos. Una forma de evitar el uso repetido de findViewById() es usar el patrón de diseño “titular 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

ViewHolder patrón de diseño ViewHolder se usa para acelerar el renderizado de ListView ; en realidad, para que funcione sin problemas, findViewById es bastante costoso (realiza el análisis DOM) cuando se usa cada vez que se muestra un elemento de lista, debe atravesar la jerarquía de diseño y crear instancias de objetos . Como 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:

http://www.youtube.com/watch?v=wDBM6wVEO70&feature=youtu.be&t=7m

a partir del minuto 10, ha explicado el patrón de diseño de ViewHolder por expertos de google.

[editar]

findViewById no está instanciando 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

A medida que revisa su ListView, solo se muestran un puñado de vistas en un momento dado. Esto significa que no tiene que instanciar una vista para cada elemento en su adaptador; cuando una vista se desplaza fuera de la pantalla, puede reutilizarse o reciclarse .

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) que realiza. El patrón ViewHolder solo funciona cuando aprovecha el reciclaje de vistas.

En getView(int position, View convertView, ViewGroup parent) , el parámetro convertView es nulo o es una vista que se ha reciclado: aún tendrá los datos de un elemento de lista diferente vinculado a él.

Sin el patrón ViewHolder, aún puede aprovechar el reciclaje de vistas (es decir, no crear ciegamente las vistas):

 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 inflemos una nueva Vista para cada fila; solo inflemos una vista si no tenemos una para reutilizar. Evitar tener que inflar una vista es la parte que definitivamente ayudará con el rendimiento cuando se desplaza por la lista: aproveche el reciclaje de vistas.

Entonces, ¿cuál es el ViewHolder para entonces? Actualmente estamos haciendo 4x findViewById(int) para cada elemento, independientemente de si la fila ya existe. Como findViewById(int) repite iterativamente un ViewGroup hasta que encuentra un descendiente con el ID dado, esto es un poco inútil para nuestras vistas recicladas: estamos volviendo a encontrar puntos de vista a los que ya tenemos referencias.

Evite esto utilizando un objeto ViewHolder para mantener referencias a las sub-vistas 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) permite decirle a la Vista que contenga un objeto arbitrario. Si lo usamos para mantener una instancia de nuestro ViewHolder después de hacer nuestras findViewById(int) , entonces podemos usar View.getTag() en vistas recicladas para evitar tener que realizar 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 .

En primer lugar :

En ListView cuando se desplaza por ListView , necesita crear un nuevo elemento y vincular sus datos, de modo que si tiene muchos elementos en ListView puede causar pérdida de memoria porque hay más objetos creados para los elementos, pero Android usa el concepto de reciclar la mayor parte de su API , lo que significa que creas un objeto y lo usas en lugar de destruirlo y declarar uno nuevo, así que cuando te desplazas por la API de ListView usando los elementos invisibles que recorraste y lo pasas en el método getView , es convertView así que aquí convertView con más elementos ListView

En segundo lugar:

si tiene un elemento personalizado en ListView , debe adjuntar su diseño personalizado en cada elemento de ListView modo que cada vez que ListView enlace un nuevo elemento utilizando findViewById obtenga una referencia de los elementos de diseño. Este método irá a la búsqueda de su elemento de forma recursiva para que ViewHolder lo ayude a hacer el recursivo por una sola vez y luego mantendrá la referencia del elemento de diseño hasta que pueda adjuntarlo a ListView

Espero que esto te ayude y retroalimenta en cualquier cosa no obvia