Android: clic ritardati in ListView

Ho la seguente struttura nella mia app:

FragmentActivity con ViewPager contenente più frammenti gestiti da FragmentStatePagerAdapter utilizzando il pacchetto di compatibilità con Android 2.1

Ogni frammento contiene ListView . Ogni elemento in ListView ha un LinearLayout con due TextViews e un Button . LinearLayout e il pulsante hanno onClickListeners (separato). Cliccando su LinearLayout avvia un’altra Activity . Ho notato che il comportamento dei clic è molto incoerente: a volte l’azione viene eseguita immediatamente, ma molto spesso viene ritardata ea volte viene semplicemente ignorata, non importa quante volte tocchi. Diventa ancora più strano perché posso toccare e l’azione verrà eseguita solo quando inizierò a scorrere l’elenco. Ho provato varie combinazioni di setFocusable(false) e setSelectable(true) ma sembra non fare alcuna differenza. Qualche idea? Sarò felice di fornire ulteriori dettagli.

Ho avuto un problema simile e mi ci sono voluti 2 giorni per eseguire il debug e risolverlo. Ho un ListAdapter che crea diverse TextView in un LinearLayout per ogni elemento della lista. Ogni TextView ha il proprio OnClickListener, perché devo gestire i clic su ciascun elemento.

Quando ho cambiato l’implementazione in modo da riutilizzare le visualizzazioni, OnClickListener ha smesso di funzionare correttamente. Nella 4.4.2 la maggior parte dei clic ha funzionato, ma a volte non si è verificata alcuna reazione finché non ho fatto scorrere l’elenco. Alla 2.3 i primi clic non funzionavano e quindi tutti i clic venivano gestiti in una raffica.

Nel mio caso speciale ho creato tutto il codice View in Java e non gonfiando le risorse. E il punto critico è stato che ho impostato i LayoutParam del LinearLayout anche quando la vista è stata riutilizzata (questo sembra essere più sicuro, assumendo quindi che la vista riutilizzata abbia i parametri di layout corretti). Quando non imposto i LayoutParam durante il riutilizzo, tutto funziona correttamente! Ecco il codice critico:

 public View getView(int position, View convertView, ViewGroup parent) { LinearLayout tapeLine = null; if (convertView != null && convertView instanceof LinearLayout && ((LinearLayout)convertView).getChildCount() == 4) tapeLine = (LinearLayout) convertView; // Reuse view else tapeLine = new LinearLayout(activity); if (convertView == null) { // Don't set LayoutParams when reusing view ViewGroup.LayoutParams tapeLineLayoutParams = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); tapeLine.setLayoutParams(tapeLineLayoutParams); } ScrollingTape scrollingTape = calculatorHolder.getCalculator().getScrollingTape(); int tapeWidthPx = parent.getWidth(); TapeLineTextSizeInfo tapeLineTextSizeInfo = calculatorHolder.getTapeLineTextSizeHelper().createTapeLineTextSizeInfo(tapeWidthPx); ScrollingTapeLine line = scrollingTape.getLine(position); tapeLine.setOrientation(LinearLayout.HORIZONTAL); int tapeBackgroundColor = getBackgroundColor(line); tapeLine.setBackgroundColor(tapeBackgroundColor); addColumnViews(tapeLine, line, tapeLineTextSizeInfo); tapeLine.setTag(R.id.scrollingtapeadapter_viewtag_position, position); tapeLine.setOnLongClickListener(longClickListener); tapeLine.setOnClickListener(remainClickListener); return tapeLine; } 

Qual è lo sfondo per questo strano comportamento della visualizzazione elenco? Ho fatto un po ‘di debugging e di ricerca nelle fonti Android. Quando Android aggiorna una vista, ci sono due passaggi importanti su Misura e onLayout. Il metodo getView di ListAdapter non è solo chiamato per disegnare la vista, ma anche prima durante onMeasure. In questo caso successivo la vista viene creata ma non è ancora registrata nella catena di eventi per gestire gli eventi di clic.

Quando una vista che è stata creata per onMeasure viene riutilizzata in seguito per essere attratta in modo attivo sullo schermo, deve essere registrata dal sistema Android per gestire gli eventi di clic. Per questo caso speciale i develepers di Android avevano fatto qualcosa, che poteva essere considerato un trucco sporco. Un flag speciale in LayoutParams viene utilizzato per decidere che la vista deve essere registrata nella modifica dell’evento.

Ora il mio problema: resettando il LayoutParam anche quando una vista viene riutilizzata, questo flag è sempre stato resettato. Pertanto il sistema Android non registrerebbe la vista e gli eventi non sarebbero arrivati.

riepilogare: quando riutilizzare una vista in getView di un ListAdapter non sovrascrive i LayoutParam perché mantengono le informazioni interne del sistema Android.

Ho incontrato lo stesso problema, ma nel mio caso la soluzione non era quella di mantenere i riferimenti alle viste, il che ha causato problemi con la cache della vista di ListView. Dopo aver implementato correttamente il metodo getView() con l’uso di converView, tutto il comportamento strano con le chiamate di clic perse / inattese era sparito.

Nel caso qualcuno si chiedesse come ho risolto questo. Fondamentalmente ho dovuto semplificare i miei layout. Sembra che quando si hanno strutture nidificate complesse, gli eventi possono richiedere troppo tempo per generare bolle e se si avvia lo scorrimento elenco allo stesso tempo, l’evento può triggersre un’azione errata. Ho rifilato i layout passando a RelativeLayout il più ansible e questo mi è sembrato di grande aiuto

Ciò che ha funzionato per me è stato assegnare un OnItemClickListener a ListView tramite setOnItemClickListener , piuttosto che un OnClickListener ai singoli elementi dell’elenco. Ovviamente il pulsante ha ancora bisogno del proprio OnClickListener , ma non ho provato quello scenario.

Non sono sicuro se questo aiuti qualcuno, ma ho avuto un problema simile invece con un TableLayout. Le soluzioni di cui sopra non hanno risolto il mio problema.

Per me il problema era: android:animateLayoutChanges="true"

La rimozione di questo ha permesso ai miei clic sui pulsanti all’interno delle righe di TableLayout di funzionare correttamente. Ho dovuto quindi animare manualmente le mie viste invece di fare affidamento sulla proprietà di cui sopra.

Sembra che tu stia eseguendo un processo di blocco (come invocare servizi web o aprire file) nel thread di eventi, quindi il thread Event è bloccato. Se questo è il tuo caso, ti preghiamo di elaborare il tuo codice di blocco in un thread diverso da Event THread.