OnBindViewHolder () nunca se llama en vista en posición aunque RecyclerView.findViewHolderForAdapterPosition () devuelve null en esa posición
Tengo una lista con 13 artículos (aunque los artículos se pueden agregar o quitar), posiciones 0-12. Cuando se muestra primero el fragmento que contiene el RecyclerView, sólo las posiciones 0 a 7 son visibles para el usuario (la posición 7 es sólo la mitad visible). En mi adaptador I Log
cada vez que un sostenedor de la visión es binded / bound (idk si la gramática se aplica aquí) y registra su posición.
- Diffutil en recycleview, lo que hace autoscroll si se agrega un nuevo elemento
- Ejemplo sencillo de Android RecyclerView
- Desplazamiento sincronizado de múltiples RecyclerViews
- Android: fija el elemento fijo en RecyclerView
- Android RecyclerView, el reciclaje no funciona correctamente
@Override public void onBindViewHolder(final ViewHolder holder, final int position) { Log.d(TAG, "onBindViewHolder() position: " + position); ... }
Desde mi Log
veo que las posiciones 0-7 están enlazadas:
Tengo un método selectAll()
que obtiene cada ViewHolder
por la posición del adaptador. Si el holder
devuelto NO es null
, utilizo el holder
devuelto para actualizar la vista para mostrar que está seleccionado. Si el titular devuelto es null
llamo a selectOnBind()
un método que selectOnBind()
la vista en esa posición para mostrar que está seleccionada cuando está enlazada en vez de en tiempo real, ya que no se muestra en la actualidad:
public void selectAll() { for (int i = 0; i < numberOfItemsInList; i++) { MyAdapter.ViewHolder holder = (MyAdapter.ViewHolder) mRecyclerView.findViewHolderForAdapterPosition(i); Log.d(TAG, "holder at position " + i + " is " + holder); if (holder != null) { select(holder); } else { selectOnBind(i); } } }
En este método I Log
el holder
junto con su posición:
Así que hasta este punto todo parece normal. Tenemos posiciones 0-7 mostrando, y de acuerdo con el Log
estas son las posiciones enlazadas. Cuando toco selectAll()
sin cambiar las vistas visibles (desplazamiento) veo que las posiciones 0-7 están definidas y 8-12 son null
. Hasta aquí todo bien.
Aquí es donde se pone interesante. Si después de llamar a selectAll()
me selectAll()
más abajo en la lista, las posiciones 8 y 9 no muestran que están seleccionadas.
Cuando compruebo el Log
veo que es porque nunca se atan aunque fueron divulgados para ser null
:
Aún más confuso es que esto no sucede cada vez. Si primero lanzo la aplicación y prueba esto puede funcionar. Pero parece que sucede sin falta después. Supongo que tiene algo que ver con las opiniones que se están reciclando, pero aún así no tendrían que estar obligados?
EDIT (6-29-16)
Después de una actualización de AndroidStudio no puedo reproducir el error. Funciona como esperaba, vinculando las vistas nulas. Si este problema vuelve a aparecer, volveré a este mensaje.
- Cómo agregar un administrador LinearLayout y GridLayout Manager en RecyclerView en Android
- No se pudo resolver: com.android.support:cardview-v7:26.0.0 android
- Posiciones de superposición / vistas en la vista de reciclaje
- Checkbox listener en RecyclerView con DataBinding
- No hay animación en el elemento RecyclerView seleccione Android
- RecyclerView, swiping de artículos, java.lang.IllegalStateException
- Configurar la propiedad scrollbars en RecyclerView bloquea mi aplicación
- Diseño de cuadrícula con vista de reciclador android
Esto está sucediendo porque:
- Las vistas no se agregan al recyclerview (
getChildAt
no funcionará y devolverá null para esa posición) - También se almacenan en caché (
onBind
no se llamará)
La llamada a recyclerView.setItemViewCacheSize(0)
solucionará este "problema".
Debido a que el valor predeterminado es 2 ( private static final int DEFAULT_CACHE_SIZE = 2;
en RecyclerView.Recycler
), siempre obtendrá 2 vistas que no llamarán onBind
pero que no se agregan al reciclador
En su caso, las vistas para las posiciones 8 y 9 no se están reciclando, se están separando de la ventana y se volverán a conectar. Y para estas vistas separadas onBindViewHolder
no se llama, sólo onViewAttachedToWindow
se llama. Si anula estas funciones en su adaptador, puede ver lo que estoy hablando.
@Override public void onViewRecycled(ViewHolder vh){ Log.wtf(TAG,"onViewRecycled "+vh); } @Override public void onViewDetachedFromWindow(ViewHolder viewHolder){ Log.wtf(TAG,"onViewDetachedFromWindow "+viewHolder); }
Ahora, con el fin de resolver su problema que necesita para realizar un seguimiento de las opiniones que se suponía que se recicla, pero se desprenden y luego hacer su proceso de sección en
@Override public void onViewAttachedToWindow(ViewHolder viewHolder){ Log.wtf(TAG,"onViewAttachedToWindow "+viewHolder); }
Creo que jugar con la vista no es una buena idea en recyclerview. El acercamiento que utilizo siempre para seguir apenas para introducir una bandera al modelo usando para RecyclerView. Supongamos que su modelo es como –
class MyModel{ String name; int age; }
Si está realizando un seguimiento, la vista está seleccionada o no, a continuación, introduzca un booleano en el modelo. Ahora se verá como –
class MyModel{ String name; int age; boolean isSelected; }
Ahora su casilla de verificación será seleccionada / no seleccionada sobre la base de la nueva bandera isSelected (en onBindViewHolder ()). En cada selección en vista cambiará el valor del valor seleccionado del modelo correspondiente a verdadero, y en no seleccionado cambiará a falso. En su caso, ejecute un bucle para cambiar el valor isSelected de todo el modelo a true y luego llame a notifyDataSetChanged()
.
Por ejemplo, supongamos que su lista es
ArrayList<MyModel> recyclerList; private void selectAll(){ for(MyModel myModel:recyclerList) myModel.isSelected = true; notifyDataSetChanged(); }
Mi sugerencia, al usar recyclerView o ListView para intentar menos jugar con vistas.
Así que en su caso –
@Override public void onBindViewHolder(final ViewHolder holder, final int position) { holder.clickableView.setTag(position); holder.selectableView.setTag(position); holder.checkedView.setChecked(recyclerList.get(position).isSelected); Log.d(TAG, "onBindViewHolder() position: " + position); ... } @Override public void onClick(View view){ int position = (int)view.getTag(); recyclerList.get(position).isSelected = !recyclerList.get(position).isSelected; } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { int position = (int)buttonView.getTag(); recyclerList.get(position).isSelected = isChecked; }
Espero que te ayude, por favor, hágamelo saber si necesita más explicación 🙂
Así que creo que la pregunta es contestada por @Pedro Oliveira. El principal sentido de RecycleView, que utiliza algoritmos especiales para almacenar en caché ViewHolder en cualquier momento. Así que next onBindViewHolder (…) puede no funcionar, por ejemplo. Si la vista es estática, o algo más.
Y sobre su pregunta que piensa utilizar RecycleView para las vistas cambiadas dinámicamente. NO LO HAGA! Porque RecycleView invalida vistas y tiene sistema de caché, por lo que tendrá muchos problemas.
Utilice LinkedListView para esta tarea!
- Barra de acción – opción ifRoom dejando demasiado espacio
- RxJava solo planificador de hilos de fondo