FindViewById vs Ver patrón de soporte en el adaptador ListView

Siempre uso LayoutInflater y findViewById para crear nuevo elemento en el método getView de Adapter.

Pero en muchos artículos los pueblos escriben que findViewById es muy muy lento y recomienda encarecidamente usar View Holder Pattern.

¿Puede alguien explicar porqué findViewById es tan lento? ¿Y por qué es más rápido el patrón del titular de la vista?

¿Y qué debo hacer si es necesario agregar elementos diferentes a ListView? ¿Debo crear clases para cada tipo?

static class ViewHolderItem1 { TextView textViewItem; } static class ViewHolderItem2 { Button btnViewItem; } static class ViewHolderItem3 { Button btnViewItem; ImageView imgViewItem; } 

¿Puede alguien explicar porqué findViewById es tan lento? ¿Y por qué es más rápido el patrón del titular de la vista?

Cuando no esté usando Holder, el método getView() llamará findViewById() tantas veces como la fila (s) estará fuera de View. Así que si usted tiene 1000 filas en la lista y 990 filas estará fuera de la vista, 990 veces se llamará findViewById() nuevo.

El patrón de diseño del soporte se utiliza para el almacenamiento en caché de la vista – El objeto Holder (arbitrario) mantiene widgets secundarios de cada fila y cuando la fila está fuera de View, entonces no se llamará findViewById () pero se reciclarán y se obtendrán widgets de Holder.

 if (convertView == null) { convertView = inflater.inflate(layout, null, false); holder = new Holder(convertView); convertView.setTag(holder); // setting Holder as arbitrary object for row } else { // view recycling // row already contains Holder object holder = (Holder) convertView.getTag(); } // set up row data from holder titleText.setText(holder.getTitle().getText().toString()); 

Donde puede aparecer la clase de titular:

 public class Holder { private View row; private TextView title; public Holder(View row) { this.row = row; } public TextView getTitle() { if (title == null) { title = (TextView) row.findViewById(R.id.title); } return title; } } 

Como @meredrica señaló su si desea obtener un mejor rendimiento, puede utilizar los campos públicos (pero destruye la encapsulación).

Actualizar:

Este es el segundo enfoque de cómo usar el patrón de ViewHolder :

 ViewHolder holder; // view is creating if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.row, parent, false); holder = new ViewHolder(); holder.title = (TextView) convertView.findViewById(R.id.title); holder.icon = (ImageView) convertView.findViewById(R.id.icon); convertView.setTag(holder); } // view is recycling else { holder = (ViewHolder) convertView.getTag(); } // set-up row final MyItem item = mItems.get(position); holder.title.setText(item.getTitle()); ... private static class ViewHolder { public TextView title; public ImageView icon; } 

Actualización # 2:

Como todo el mundo sabe, Google y AppCompat v7 como biblioteca de soporte liberado nuevo ViewGroup llamado RecyclerView que está diseñado para procesar cualquier punto de vista basado en el adaptador. Como dice @antonioleiva en post : "Se supone que es el sucesor de ListView y GridView" .

Para poder usar este elemento necesitas básicamente uno de lo más importante y es un tipo especial de Adaptador que está envuelto en el mencionado ViewGroup – RecyclerView.Adapter donde ViewHolder es esa cosa de la que estamos hablando aquí 🙂 Simplemente, este nuevo elemento ViewGroup tiene Su propio patrón de ViewHolder implementado. Todo lo que usted necesita hacer es crear la clase de ViewHolder de encargo que tiene que extender de RecyclerView.ViewHolder y usted no necesita preocuparse de comprobar si la fila actual en el adaptador es null o no.

El adaptador lo hará por usted y usted puede estar seguro que la fila será inflada solamente en el caso que debe ser inflada (yo diría). Aquí está la simple implementación:

 public static class ViewHolder extends RecyclerView.ViewHolder { private TextView title; public ViewHolder(View root) { super(root); title = root.findViewById(R.id.title); } } 

Dos cosas importantes aquí:

  • Tienes que llamar al constructor super () en el que necesitas pasar la vista de raíz de la fila
  • Puede obtener la posición específica de la fila directamente desde ViewHolder a través del método getPosition () . Esto es útil cuando desea realizar alguna acción después de tocar 1 en el widget de fila.

Y un uso de ViewHolder en el adaptador. El adaptador tiene tres métodos que tiene que implementar:

  • OnCreateViewHolder () – donde se crea ViewHolder
  • OnBindViewHolder () – donde está actualizando su fila. Podemos decir que es un pedazo de código donde se está reciclando la fila
  • GetItemCount () – diría que es igual que el método típico getCount () en BaseAdapter

Así que un pequeño ejemplo:

 @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View root = LayoutInflater.from(mContext).inflate(myLayout, parent, false); return new ViewHolder(root); } @Override public void onBindViewHolder(ViewHolder holder, int position) { Item item = mItems.get(position); holder.title.setText(item.getTitle()); } @Override public int getItemCount() { return mItems != null ? mItems.size() : 0; } 

1 Es bueno mencionar que RecyclerView no proporciona una interfaz directa para poder escuchar eventos de clic de artículo. Esto puede ser curioso para alguien, pero aquí es una buena explicación por qué no es tan curioso como lo que realmente se ve.

He resuelto esto mediante la creación de mi propia interfaz que se utiliza para manejar los eventos de clic en las filas (y cualquier tipo de widget que desea en la fila):

 public interface RecyclerViewCallback<T> { public void onItemClick(T item, int position); } 

Estoy vinculándolo en el adaptador a través de constructor y luego llamar a la devolución de llamada en ViewHolder:

 root.setOnClickListener(new View.OnClickListener { @Override public void onClick(View v) { int position = getPosition(); mCallback.onItemClick(mItems.get(position), position); } }); 

Este es un ejemplo básico, así que no lo tome como una sola manera posible. Las posibilidades son infinitas.

El patrón de ViewHolder creará una instancia estática de ViewHolder y lo adjuntará al elemento de vista la primera vez que se cargue y, a continuación, se recuperará de esa etiqueta de vista en las llamadas futuras. Ya que sabíamos que el método getView () se llama con mucha frecuencia, especialmente cuando hay muchos elementos en listview para desplazarse, de hecho se llama cada vez que un elemento de listview se hace visible en scroll.

ViewHolder Pattern evita que findViewById() sea ​​llamado muchas veces inútilmente, manteniendo las vistas en una referencia estática, es un buen patrón para guardar algunos recursos (especialmente cuando se necesita hacer referencia a muchas vistas en los elementos de listview).

Muy bien dicho por @RomainGuy

El ViewHolder puede y debe ser utilizado también para almacenar estructuras de datos temporales para evitar asignaciones de memoria en getView (). El ViewHolder contiene un buffer de char para evitar asignaciones al obtener datos del Cursor.

  • ListFragment con adaptador personalizado
  • ListView con horizontalScrollView OnItemClick no funciona
  • ¿Cómo hacer ListView personalizado con fondos de colores de los elementos?
  • Lotes de GC cuando se desplaza ListView (con el patrón de titular)
  • Deslizar para eliminar listitem
  • Cómo agregar tres niveles ListView en ExpandableListView en android
  • CursorAdapter respaldado ListView eliminar animación "parpadeos" en borrar
  • Cómo configurar la fuente personalizada para android listview?
  • Android setOnCheckedChangeListener vuelve a llamar cuando la vista anterior vuelve
  • Android - ¿cómo puedo saber cuando gridview ha llegado al fondo?
  • ¿Cómo desplazar ListView a la parte inferior?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.