Come funziona il meccanismo di riciclaggio di ListView


146

Quindi ho questo problema che avevo prima e naturalmente ho chiesto aiuto qui . La risposta di Luksprog è stata fantastica perché non avevo idea di come ListView e GridView si ottimizzassero con il riciclaggio delle viste. Quindi con il suo consiglio sono stato in grado di cambiare il modo in cui ho aggiunto Views a GridView. Il problema è che ora ho qualcosa che non ha senso. Questo è il mio getViewdal mio BaseAdapter:


public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        }
        Log.d("DayViewActivity", "Position is: "+position);
        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);

        //layout.addView(new EventFrame(parent.getContext()));

        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test"); 
        //the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
        //new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)   
        if(position == 0) {
            Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
            layout.addView(create);
        }

        return convertView;
    }

}

Il problema è quando scorro, questo succede, e non sulla posizione 0 ... Sembra che la posizione 6 e la posizione 8, in più ne metta due in posizione 8. Ora sto ancora cercando di capire come usare ListView e GridView, quindi lo faccio non capisco perché questo stia accadendo. Uno dei motivi principali per cui sto ponendo questa domanda è di aiutare gli altri che probabilmente non conoscono la vista sul riciclaggio di ListView e GridView o il modo in cui articolo lo inserisce, il meccanismo di ScrapView.

inserisci qui la descrizione dell'immagine

Modifica successiva

Aggiunta di un collegamento a una discussione su Google IO che è praticamente tutto ciò che serve per capire come funziona ListView. Link era morto sui commenti. Quindi user3427079 è stato abbastanza carino da aggiornare quel link. Qui è per un facile accesso.


Haha, grazie per quel link. Ho iniziato a guardarlo ora :)
Andy,

Non hai implementato completamente l'esempio di codice della domanda precedente. L'idea è che devi sempre avere un pezzo di codice per ripristinare le modifiche apportate per altre righe (nel tuo caso la posizione 0).
Luksprog,

Bene, capisco che @Luksprog Ma questo spiega perché sta anche mettendo il View nelle altre posizioni? Semmai, mi aspettavo che solo la posizione 0 avesse viste duplicate. Idk, ho ancora problemi a capire come ListView sta vincolando questa roba: /
Andy

1
Riciclaggio significa che la visualizzazione della riga che è appena scomparsa (ad esempio la posizione 0, dopo aver fatto scorrere una riga verso il basso) dallo schermo può essere utilizzata in seguito quando è ListViewnecessario mostrare una nuova riga (come continuare a scorrere verso il basso / verso l'alto). Il problema è che la vista che è appena scomparsa e che verrà utilizzata nelle posizioni necessarie in futuro ha TextViewgià aggiunto ad essa, questo è il problema. Per risolverlo è necessario rimuoverlo nel getViewmetodo se il getViewmetodo è chiamato per qualsiasi posizione diversa da 0.
Luksprog,

2
Il link del post principale è morto, penso che sia? youtube.com/watch?v=N6YdwzAvwOA
G_V

Risposte:


330

Inizialmente, non ero nemmeno a conoscenza del riciclaggio di listview e del meccanismo di utilizzo di convertview, ma dopo un'intera giornata di ricerche ho praticamente capito i meccanismi della visualizzazione di elenco facendo riferimento a un'immagine di android.amberfog inserisci qui la descrizione dell'immagine

Ogni volta che la visualizzazione elenco viene riempita da un adattatore, in pratica mostra il numero di righe che la visualizzazione elenco può mostrare sullo schermo e il numero di righe non aumenta anche quando si scorre l'elenco. Questo è il trucco usato da Android in modo che listview funzioni in modo più efficiente e veloce. Ora la storia interna di listview che si riferisce all'immagine, come puoi vedere, inizialmente la listview ha 7 elementi visibili, quindi, se scorri verso l'alto fino a quando l'elemento 1 non è più visibile, getView () passa questa vista (cioè item1) al riciclatore e puoi usare

System.out.println("getview:"+position+" "+convertView);

dentro il tuo

public View getView(final int position, View convertView, ViewGroup parent)
{
    System.out.println("getview:"+position+" "+convertView);
    ViewHolder holder;
    View row=convertView;
    if(row==null)
    {
        LayoutInflater inflater=((Activity)context).getLayoutInflater();
        row=inflater.inflate(layoutResourceId, parent,false);
        
        holder=new PakistaniDrama();
        holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
        holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);
        
        row.setTag(holder);
        
    }
    else
    {
        holder=(PakistaniDrama)row.getTag();
    }
            holder.tvDramaName.setText(dramaList.get(position).getDramaName());
    holder.cbCheck.setChecked(checks.get(position));
            return row;
    }

Noterai nel logcat, inizialmente, convertview è nullo per tutte le righe visibili, perché inizialmente non c'erano viste (cioè oggetti) nel riciclatore, quindi getView () crea una nuova vista per ciascuno degli oggetti visibili, ma il nel momento in cui scorri verso l'alto e l'elemento 1 esce dallo schermo, verrà inviato al riciclatore con il suo stato attuale (ad esempio il "testo" di TextView o nel mio caso, se la casella di controllo è selezionata, verrà associata alla vista e conservato in riciclatore).

Ora quando scorri verso l'alto / il basso, la visualizzazione elenco non creerà una nuova visualizzazione, utilizzerà la vista presente nel tuo riciclatore. Nel tuo Logcat noterai che 'convertView' non è nullo, è perché il tuo nuovo oggetto 8 verrà disegnato usando convertview, cioè fondamentalmente prende la vista oggetto 1 dal riciclatore e gonfia l'articolo 8 al suo posto, e puoi osservare quello nel mio codice. Se avevi una casella di controllo e la spunta in posizione 0 (supponiamo che l'elemento 1 avesse una casella di controllo e l'hai selezionata), quindi quando scorri verso il basso vedrai la casella di controllo elemento 8 già selezionata, ecco perché listview utilizza la stessa vista, non crearne uno nuovo a causa dell'ottimizzazione delle prestazioni.

Cose importanti

1 . Non impostare mai il layout_heighte layout_widthdel listview a wrap_contentcome getView()costringerà l'adattatore per ottenere qualche bambino per misurare l'altezza dei punti di vista da trarre in visualizzazione elenco e può causare alcuni comportamenti imprevisti come tornare convertview anche l'elenco non è scrolled.always utilizzano match_parento fissi larghezza altezza.

2 . Se si desidera utilizzare alcuni layout o vista dopo la vostra visualizzazione elenco e la domanda di forza è venuto nella vostra mente se ho impostato il layout_heightper fill_parentla vista dopo visualizzazione elenco non verranno visualizzati come va giù per lo schermo, in modo da suo meglio per mettere il vostro ListView all'interno di un layout.Ad esempio Layout lineare e imposta l'altezza e la larghezza di quel layout secondo le tue esigenze e imposta l' attributo altezza e larghezza della tua visualizzazione elenco come dal tuo layout (come se la larghezza del layout è 320 e l'altezza è 280) quindi la visualizzazione elenco dovrebbe avere la stessa altezza e larghezza. Questo dirà a getView () l'esatta altezza e larghezza delle viste da rendere, e getView () non chiamerà più e più volte alcune righe casuali, e altri problemi come restituire la vista convertita anche prima che lo scorrimento non accada, ho un test anche io, a meno che la mia lista non fosse all'interno della lineaLayout, avessero anche problemi come ripetere la chiamata di vista e convertire la vista come, mettere Listview in LinearLayout ha funzionato come per magia per me (non sapevo perché)

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.636: I/System.out(13871): getview 1 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 2 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 3 android.widget.RelativeLayout@406082c0
01-01 14:49:36.656: I/System.out(13871): getview 4 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 5 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.696: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

Ma ora è risolto, lo so, non sono così bravo a spiegare, ma mentre metto tutto il mio giorno a capire, quindi ho pensato che altri principianti come me possano ottenere aiuto dalla mia esperienza e spero che ora la gente avrà un po 'di comprensione del framework ListView come funziona, in quanto è davvero disordinato e complicato, quindi i principianti hanno trovato troppi problemi a capirlo


36
"So che non sono così bravo a spiegare" >>> I +37 voti su questa risposta mi dicono che hai fatto un ottimo lavoro nello spiegare. Continua a condividere le tue conoscenze! ;)
2Dee

1
Ottima risposta raja ji +1 da parte mia .. :)
Ehsan Sajjad

2
Grazie per l'immagine. Dopo aver avuto molti problemi con ListView quando ho iniziato con Android, conoscevo già il principio del riciclaggio dopo aver lottato molto con esso. Tuttavia, ci sono voluti questa foto e la frase: if you had a checkbox and if you check it at position 0(let say item1 has also a checkbox and you checked it) so when you scroll down you will see item 8 checkbox is already checked,this is why listview is re using the same view not creating a new for you due to performance optimization.per me realizzare il mio errore. Mi sono imbattuto nel miglior post sul riciclaggio di ListView per Android!
Kevin Cruijssen,

1
@MuhammadBabar Sì, ho postato Domanda: stackoverflow.com/q/30122027/1318946
Pratik Butani

1
@MuhammadBabar fantastico spiegazione uomo! Mi fa risparmiare tempo. Voglio solo sapere, funziona RecyclerViewanche sullo stesso meccanismo? Nella mia domanda stackoverflow.com/questions/47897770/… , so che il compito è assolutamente impossibile con listView. Dovrei provare con recyclerView?
Shambhu,

8

Fai attenzione, nel modello Supporto, se imposti la posizione nell'oggetto Supporto, dovresti impostarla ogni volta, ad esempio:

@Override
public final View getView(int position, View view, ViewGroup parent) {
    Holder holder = null;
    if (view == null) {
        LayoutInflater inflater = (LayoutInflater) App.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(getContainerView(), parent, false);
        holder = getHolder(position, view, parent);
        holder.setTag(tag);
        view.setTag(holder);
    } else {
        holder = (Holder) view.getTag();
    }
    holder.position = position;
    draw(holder);
    return holder.getView();
}

questo è un esempio di una classe astratta, dove

getHolder(position, view, parent);

esegue tutte le operazioni di impostazione per

ImageViews, TextViews, etc..

1
Non me ne sono accorto per tutto questo tempo. parent.setTag(holder);invece del modo corretto che è,view.setTag(holder);
ArtiomLK

2

Utilizzando il modello Holder puoi ottenere ciò che desideri:

Puoi trovare la descrizione di questo modello qui:

Il riciclo della visualizzazione elenco avviene quando si scorre lo schermo verso il basso e gli elementi della visualizzazione elenco sopra sono nascosti. Vengono riutilizzati per mostrare nuovi elementi di visualizzazione elenco.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.