A questão do ciclo de vida de um aplicativo Android ou fragmento de um aplicativo Android é extremamente importante para um praticante de Android (desenvolvedor Android). Porque Como a ordem de execução dos retornos de chamada de todos os métodos relacionados ao estado do ciclo de vida ( onCreate () , onStart () etc.) é codificada e sua aplicação incorreta leva à inoperabilidade do aplicativo. O que o ciclo de vida tem a ver com isso? - o atendente habretchit perguntará. Afinal, o título, ao que parece, não é sobre ele? Eu respondo: há algo em comum entre o ciclo de vida da atividade e o trabalho do RecyclerView - há uma ORDEM RÁPIDA de execução de métodos de retorno de chamada ao usar esse widget e, portanto, a necessidade de APLICÁ-LO CORRETAMENTE .
Se isso não for feito, as listas podem se comportar de uma maneira muito misteriosa.
Adaptador mínimo para RecyclerView
Por exemplo. Existe um adaptador dessa lista com preenchimento mínimo padrão:
Listagem 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) {
No código do método onBindViewHolder () do adaptador da nossa lista, cujos elementos contêm uma caixa de seleção ( CheckBox ), há um apelo ao método do manipulador ( holder 'a), no qual os dados são lidos a partir da coleção conectada ao adaptador e, com base nele, são definidos - check-box - state, bem como os elementos necessários ( Listener ) estão conectados a vários elementos da interface:
Listagem 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); } } };
Quando o sinalizador é definido e uma determinada condição é atendida, os ouvintes alteram os dados na coleção e, quando não são executados, exibem uma ou outra caixa de diálogo.
Acontece algo como isto:



Na Fig-1 - a lista gerada. Na Fig-2 - O item da lista marcado. Na Fig-3 - um diálogo informando sobre uma violação da condição ao marcar o próximo elemento.
Para obter o resultado com o gerenciador de layout de lista Figura-1 ( LayoutManager ), é executada a seguinte ordem de chamada das funções necessárias:
Algoritmo 1
- Rv_Adapter.getItemCount () - verifica o número de elementos na coleção;
- Rv_Adapter.onAttachedToRecyclerView () - o adaptador se conecta ao widget;
- Enquanto o espaço da lista não estiver preenchido com os elementos da lista, as seguintes etapas do algoritmo 2 são executadas para a lista:
Algoritmo 2
- Rv_Adapter.onCreateViewHolder () - para cada elemento da coleção, seu próprio manipulador é criado;
- CustomViewHolder () - o construtor do manipulador é executado;
- Rv_Adapter.onBindViewHolder () - para cada instância, o construtor de visualizações é iniciado;
- Rv_Adapter.onViewAttachedToWindow () - a exibição gerada é conectada à janela;
Está tudo ótimo! Se não fosse por "Mas". Antes, MAS!
O problema
Ao rolar por uma longa lista contendo pelo menos uma dúzia de itens, receberemos uma mensagem da Figura 3 sem outras ações.
Solução de problemas
O motivo é que, ao escrever o código do adaptador, NÃO CONTAMOS A ORDEM DE DESEMPENHO DAS FUNÇÕES DO QUADRO listadas aqui e aqui ao rolar. E ele é assim:
Algoritmo 3
- Ao ocultar a visibilidade de cada item da lista para a instância associada do manipulador, o método Rv_Adapter.onViewDetachedFromWindow () é executado , o que desconecta a exibição oculta da janela;
- Quando cada novo elemento da lista ( itemView ) aparece fora do escopo de visibilidade, o Algoritmo 2 é executado para a instância do manipulador associado;
Mas isso não é tudo. Com as configurações "padrão" do gerenciador de marcações, cada item da lista desconectado da janela não fica muito tempo na fila para acesso rápido. Assim que houver duas delas, elas serão movidas pelo gerente para a fila de instâncias descartadas, marcada por uma chamada ao método Rv_Adapter.onViewRecycled () para cada item da lista utilizado e vice-versa.
Portanto, o algoritmo 3 se parece com isso:
Algoritmo 3 '
A partir do algoritmo 3 acima, é visto que, se você rolar a lista por mais de max, o número de posições de exibição nela será criado novamente, para o qual o método Rv_Adapter.onBindViewHolder (holder, visiblePos) será usado, o que repetirá as ações do usuário.
Conclusão e recomendação
Para evitar operações repetidas no método onBindViewHolder (holder, visiblePos) ao rolar a lista pelo número de posições maiores que max, é necessário:
- Complemente os elementos de coleção com um campo com um sinal de exclusão da visualização associada na fila de manipuladores utilizados, por exemplo, bool reciclado ;
- Insira instruções sobre como definir esse sinalizador no método onViewRecycled (holder) , por exemplo .... setRecycled (true) ;
- Insira no método onBindViewHolder (holder, visiblePos) uma verificação desse sinal, por exemplo, if (! Handler.cur.isRecycled ()) ...;
- Insira as instruções do método onViewAttachedToWindow (holder) para remover esse sintoma, por exemplo .... setRecycled (false) ;
Por exemplo , assim:
Listagem 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); }