ViewHolder – buona pratica

Una piccola domanda newbie. Perché inizializzare il ViewHolder in getView() ? Perché non possiamo inizializzarla nel constructor?

  • Animazione a button personalizzata
  • Distriggers l'indicatore di tabella ActionBar in modo programmato
  • Chiamare setActiveUser genera errore
  • Anteprima Android L non cerca librerie native nella cartella "armeabi" (UnsatisfiedLinkError)
  • È ansible avere una spinner e una vista di elenco sulla stessa pagina?
  • Android Sostituisci frammento all'interno di un frammento
  • Come mostrare la risposta di json in altre attività?
  • Sintassi per il command di shell nell'applicazione Android
  • Chiamare un'attività da un altro in Android
  • Aggiungere il button positivo a Dialog
  • Mapview getLatitudeSpan e getLongitudeSpan non funzionano
  • Utilizzo di simpleCursorAdapter personalizzato
  • 2 Solutions collect form web for “ViewHolder – buona pratica”

    Avrai più oggetti di ViewHolder esistenti.

    Un ListView per sua natura non crea nuove istanze di View per ciascuna delle sue righe. È così che se si dispone di un ListView di milioni di cose, non è necessario memorizzare informazioni di layout per un milione di cose. Allora, cosa hai bisogno di memorizzare? Solo le cose che sono sullo schermo. Puoi quindi riutilizzare queste visioni più e più volte. In questo modo, il tuo ListView di un milione di oggetti può avere solo 10 visite per bambini.

    getView() matrix personalizzato, avrai una function chiamata getView() che ha qualcosa di simile:

     public View getView(int position, View convertView, ViewGroup parent) { //Here, position is the index in the list, the convertView is the view to be //recycled (or created), and parent is the ListView itself. //Grab the convertView as our row of the ListView View row = convertView; //If the row is null, it means that we aren't recycling anything - so we have //to inflate the layout ourselves. if(row == null) { LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); row = inflater.inflate(R.layout.list_item, parent, false); } //Now either row is the recycled view, or one that we've inflated. All that's left //to do is set the data of the row. In this case, assume that the row is just a //simple TextView TextView textView = (TextView) row.findViewById(R.id.listItemTextView); //Grab the item to be rendered. In this case, I'm just using a string, but //you will use your underlying object type. final String item = getItem(position); textView.setText(item); //and return the row return row; } 

    Ciò funziona, ma prendi un attimo e vedi se riesci a vedere l'inefficienza qui. Pensate a quale codice precedente verrà chiamato in modo ridondante.

    Il problema è che stiamo chiamando row.findViewById più e più volte, anche se dopo la prima volta lo cerchiamo, non cambierà mai. Mentre se hai solo un semplice TextView nella tua list, probabilmente non è così male, se hai un layout complesso o se hai più viste che desideri impostare i dati, puoi perdere un po 'di tempo a trovare la tua vista e ancora una volta.

    Come possiamo quindi risolvere questo problema? Beh, avrebbe senso memorizzare quel TextView da qualche parte dopo che lo guardiamo. Quindi introduciamo una class chiamata ViewHolder , che "tiene" le viste. Quindi, all'interno dell'adattatore, introduce una class interna così:

     private static class ViewHolder { TextView textView; } 

    Questa class è privata, in quanto è solo un meccanismo di memorizzazione della cache per l'adattatore e è statico in modo che non abbiamo bisogno di un riferimento all'adattatore per utilizzarlo.

    Questo memorizzerà la visualizzazione in modo che non dobbiamo call row.findViewById più volte. Dove dovremmo impostare? Quando giriamo la visualizzazione per la prima volta. Dove lo immagazziniamo? Le viste hanno un field "tag" personalizzato, che può essere utilizzato per memorizzare informazioni meta sulla visualizzazione – esattamente quello che vogliamo! Poi, se abbiamo già visto questa vista, dobbiamo solo cercare il tag invece di cercare ciascuna delle viste all'interno della row.

    Quindi, se l'istruzione if all'interno di getView() diventa:

     //If the row is null, it means that we aren't recycling anything - so we have //to inflate the layout ourselves. ViewHolder holder = null; if(row == null) { LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); row = inflater.inflate(R.layout.list_item, parent, false); //Now create the ViewHolder holder = new ViewHolder(); //and set its textView field to the proper value holder.textView = (TextView) row.findViewById(R.id.listItemTextView); //and store it as the 'tag' of our view row.setTag(holder); } else { //We've already seen this one before! holder = (ViewHolder) row.getTag(); } 

    Ora, dobbiamo solo aggiornare il valore di text holder.textView visto che è già un riferimento alla vista riciclata! Quindi il codice dell'adattatore finale diventa:

     public View getView(int position, View convertView, ViewGroup parent) { //Here, position is the index in the list, the convertView is the view to be //recycled (or created), and parent is the ListView itself. //Grab the convertView as our row of the ListView View row = convertView; //If the row is null, it means that we aren't recycling anything - so we have //to inflate the layout ourselves. ViewHolder holder = null; if(row == null) { LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); row = inflater.inflate(R.layout.list_item, parent, false); //Now create the ViewHolder holder = new ViewHolder(); //and set its textView field to the proper value holder.textView = (TextView) row.findViewById(R.id.listItemTextView); //and store it as the 'tag' of our view row.setTag(holder); } else { //We've already seen this one before! holder = (ViewHolder) row.getTag(); } //Grab the item to be rendered. In this case, I'm just using a string, but //you will use your underlying object type. final String item = getItem(position); //And update the ViewHolder for this View's text to the correct text. holder.textView.setText(item); //and return the row return row; } 

    E siamo finiti!

    Alcune cose da pensare:

    1. Come cambia questa situazione se si dispone di più viste di una row che si desidera modificare? Come una sfida, fare un ListView in cui each row abbia due oggetti TextView e un ImageView
    2. Quando si esegue il debug del tuo ListView, controllare alcune cose in modo da poter vedere cosa sta succedendo:
      1. Quante volte è chiamato il constructor di ViewHolder.
      2. Quale valore di holder.textView.getText() è prima di aggiornarlo alla fine di getView()

    Mentre scorriamo l'elenco each volta che fila popolare e crea una nuova creazione di righe per each row, dobbiamo init il titolare della vista. Proprio come ho due TextView in fila allora,

      static class ViewHolder { protected TextView title; protected TextView type; } public View getView(int position, View convertView, ViewGroup parent) { View view = null; if (convertView == null) { LayoutInflater inflator = context.getLayoutInflater(); view = inflator.inflate(R.layout.feeds_rowview, null); final ViewHolder viewHolder = new ViewHolder(); view.setTag(viewHolder); viewHolder.title = (TextView) view.findViewById(R.id.Title); viewHolder.type = (TextView) view.findViewById(R.id.Type); } else { view = convertView; } ViewHolder holder = (ViewHolder) view.getTag(); holder.title.setText(list.get(position).getTitle()); holder.type.setText(list.get(position).getType()); return view; } 
    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.