La cuestión del ciclo de vida de una aplicación de Android o un fragmento de una aplicación de Android es extremadamente importante para un androide practicante (desarrollador de Android). Por qué Debido a que el orden de ejecución de las devoluciones de llamada de todos los métodos relacionados con el estado del ciclo de vida ( onCreate () , onStart () , etc.) está codificado y su aplicación incorrecta conducirá a la inoperancia de la aplicación. ¿Qué tiene que ver el ciclo de vida con él? - preguntará el atento habretchit. Después de todo, el título, al parecer, no se trata de él. Respondo: hay algo en común entre el ciclo de vida de la actividad y el trabajo de RecyclerView: hay un ORDEN DURO de ejecución de los métodos de devolución de llamada cuando se usa este widget y, por lo tanto, la necesidad de APLICARLO CORRECTAMENTE .
Si esto no se hace, las listas pueden comportarse de una manera muy misteriosa.
Adaptador mínimo para RecyclerView
Por ejemplo Existe un adaptador de lista con un relleno mínimo estándar:
Listado 1
public class RvCustomAdapter extends RecyclerView.Adapter<RvCustomAdapter.CustomViewHolder> { private final Frag1 frag; private final LayoutInflater lInflater; private ArrayList<JSONDataSet> dataSet; ... ... ... public RvCustomAdapter(final Frag1 fragment) { this.frag = fragment; this.lInflater = (LayoutInflater) fragment.getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.dataSet = new ArrayList<>(); } ... ... ... @Override public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
En el código del método onBindViewHolder () del adaptador de nuestra lista, cuyos elementos contienen una casilla de verificación ( CheckBox ), existe una apelación al método del titular ( titular 'a), en el que los datos se leen de la colección conectada al adaptador y en función de lo cual se establece - casilla de verificación - estado, así como los elementos necesarios ( Listener ) están conectados a varios elementos de la interfaz:
Listado 2
void showData(final int position) { cur = dataSet.get(position); cb_Data.setChecked(cur.isChecked()); ... ... ... cb_Data.setOnCheckedChangeListener(cb_DataOnCheckedChangeListener); ll_Data.setOnClickListener(ll_DataOnClickListener); } private OnClickListener ll_DataOnClickListener = new OnClickListener() { @Override public void onClick(View view) { cur.setChecked(!cur.isChecked()); cb_Data.setChecked(cur.isChecked()); } }; private OnCheckedChangeListener cb_DataOnCheckedChangeListener = new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { cur.setChecked(checked); compoundButton.setChecked(checked); setItemsColor(checked); if (checked) { if (...) { (frag).addSelectedItemsCounter(cur); } else { cur.setChecked(!checked); compoundButton.setChecked(!checked); setItemsColor(!checked); if (...) { createPrimaryDialog(); } else { createSecondaryDialog(); } } } else { (frag).remSelectedItemsCounter(cur); } } };
Cuando se establece el indicador y se cumple una determinada condición, los oyentes cambian los datos de la colección y, cuando no se ejecuta, muestran uno u otro cuadro de diálogo.
Resulta algo como esto:



En la Fig-1 - la lista generada. En la figura 2: el elemento de lista marcado. En la Fig-3: un diálogo que informa sobre una violación de la condición al marcar el siguiente elemento.
Para obtener el resultado con el administrador de diseño de lista de la Figura 1 ( LayoutManager ), se realiza el siguiente orden de invocación de las funciones necesarias:
Algoritmo 1
- Rv_Adapter.getItemCount () - verifica el número de elementos en la colección;
- Rv_Adapter.onAttachedToRecyclerView (): el adaptador se conecta al widget;
- Si bien el espacio de la lista no se llena con los elementos de la lista, se realizan los siguientes pasos del algoritmo 2 para la lista:
Algoritmo 2
- Rv_Adapter.onCreateViewHolder (): para cada elemento de la colección, se crea su propio controlador;
- CustomViewHolder (): se ejecuta el constructor del controlador;
- Rv_Adapter.onBindViewHolder (): para cada instancia, se inicia el generador de vistas ;
- Rv_Adapter.onViewAttachedToWindow (): la vista generada está conectada a la ventana;
¡Todo es genial! Si no fuera por "Pero". Más bien, pero!
El problema
Al desplazarse por una larga lista que contiene al menos un par de docenas de elementos, recibiremos un mensaje de la Figura 3 sin ninguna otra acción.
Solución de problemas
La razón es que al escribir el código del adaptador, NO TENÍMOS EN CUENTA EL ORDEN DE DESEMPEÑO DE LAS FUNCIONES DE LLAMADA que se enumeran aquí y aquí al desplazarse. Y él es así:
Algoritmo 3
- Al ocultar en el extranjero la visibilidad de cada elemento de la lista para la instancia asociada del controlador, se ejecuta el método Rv_Adapter.onViewDetachedFromWindow () , que desconecta la vista oculta de la ventana;
- Cuando cada nuevo elemento de la lista ( itemView ) aparece fuera del alcance de la visibilidad, el Algoritmo 2 se ejecuta para la instancia del controlador asociado;
Pero eso no es todo. Con la configuración "predeterminada" del administrador de marcado, cada elemento de la lista que está desconectado de la ventana no permanece mucho tiempo en la cola para un acceso rápido. Tan pronto como haya 2 de ellos allí, el administrador los mueve a la cola de instancias desechadas, que se marca llamando al método Rv_Adapter.onViewRecycled () para cada elemento de lista utilizado y viceversa.
Por lo tanto, el algoritmo 3 en realidad se ve así:
Algoritmo 3 '
Del algoritmo 3 'anterior se ve que si se desplaza por la lista más de un máximo, el número de posiciones de vista en él se creará nuevamente, para lo cual se utilizará el método Rv_Adapter.onBindViewHolder (titular, visiblePos) , que repetirá las acciones del usuario.
Conclusión y recomendación.
Para evitar repetir operaciones en el método onBindViewHolder (holder, visiblePos) al desplazar la lista por el número de posiciones mayor que max es necesario:
- Complemente los elementos de la colección con un campo con un signo de desplazar la vista asociada a la cola de manipuladores utilizados, por ejemplo, bool reciclado ;
- Inserte instrucciones sobre cómo configurar este indicador en el método onViewRecycled (titular) , por ejemplo .... setRecycled (true) ;
- Inserte en el método onBindViewHolder (soporte, visiblePos) una comprobación de este signo, por ejemplo si (! Handler.cur.isRecycled ()) ...;
- Inserte en el onViewAttachedToWindow (titular) las instrucciones del método para eliminar este síntoma, por ejemplo ... setRecycled (false) ;
Por ejemplo , así:
Listado 3
@Override public void onViewRecycled(@NonNull CustomViewHolder holder) { super.onViewRecycled(holder); holder.cur.setRecycled(true); } @Override public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) { if (!holder.cur.isRecycled()){ ... ... ... } } @Override public void onViewAttachedToWindow(@NonNull CustomViewHolder holder) { super.onViewAttachedToWindow(holder); holder.cur.setRecycled(false); }