Autore Topic: Dubbio sull'uso di tag per ottimizzazione creazione View dentro un Adapter  (Letto 548 volte)

Offline AndreaNobili

  • Utente junior
  • **
  • Post: 75
  • Respect: 0
    • Mostra profilo
Ciao,
stò studiando sull'ottimo libro di Carli ed in questi giorni mi stò decisamente chiarendo le idee sull'argomento Adapter

Avrei però qualche dubbietto sull'uso di tag per ottimizzare la creazione delle View dentro un Adapter

Faccio l'esempio concreto del libro così è più facile capire cosa intendo dire:

Ho la seguente Activity:

Codice: [Seleziona]
package it.apogeo.android.cap05.customarrayadaptertest;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class CustomArrayAdapterTestActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                // Impostiamo il Layout:
                setContentView(R.layout.main);
               
                // Otteniamo il riferimento alla ListView:
                ListView listView = (ListView) findViewById(R.id.arrayList);
               
                // Creiamo uno specifico ArrayAdapter ridefinendo il metodo getView tramite Override:
                ArrayAdapter<CustomItem> arrayAdapter = new ArrayAdapter<CustomItem>(this, R.layout.custom_row, R.id.firstnameLabel, createItems()) {
                       
                        /* Restituisce una View che mostra i dati nella posizione corrente dell'array:
                         * @param position è la posizione dell'elemento all'interno dell'insieme di dati dell'adapter che vogliamo visualizzare nella view
                         * @param convertView è la vecchia View da riusare (se possibile). Bisogna controllare che non sia null e che sia di un tipo appropriato. Se
                         *                   non è possibile convertire la View per mostrare i dati correttamente se ne può creare un'altra
                         * @param parent è la View padre a cui eventualmente questa View deve essere assegnata */
                        @Override
                        public View getView(int position, View convertView, ViewGroup parent){
                                return getViewHolder(position,convertView,parent);                                                // Caso ulteriormente ottimizzato usante un Holder
                        }
/* Metodo ulteriormente ottimizzato: evita di invocare i metodi findViewById associando a ciascuna View un oggetto che mantenga i riferimenti alle
                         * TextView in esse contenute */
                        public View getViewHolder(int position, View convertView, ViewGroup parent) {
                               
                                // Creiamo il riferimento all'holder:
                                ViewHolder viewHolder = null;
                               
                                // Se non presente una istanza della View la creiamo attraverso inflating. Se già presente la riutilizziamo
                                if(convertView==null){
                                       
                                        /* Otteniamo il riferimento alla View da parserizzare: creo l'oggetto inflater e poi da questo ottengo la View a partire dal documento
                                         * custom_row.xml istanziandolo nella View convertView:  */
                                        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                                        convertView = inflater.inflate(R.layout.custom_row, null);
                                       
                                        // Creiamo un Holder associato a tale View:
                                        viewHolder = new ViewHolder();
                                       
                                        // Assegnamo i riferimenti alle textView:
                                        viewHolder.firstnameView = (TextView)convertView.findViewById(R.id.firstname);
                                        viewHolder.lastnameView = (TextView)convertView.findViewById(R.id.lastname);
                                       
                                        // Lo assegnamo come Tag della View:
                                        convertView.setTag(viewHolder);
                                }else{
                                        // La View esiste già per cui possiede già l'holder (ottengo il riferimento all'holder)
                                        viewHolder = (ViewHolder)convertView.getTag();
                                }
                               
                                // Otteniamo l'elemento i-esimo:
                                CustomItem item = getItem(position);
                               
                                // Assegnamo i valori (senza dover richiamare ogni volta il metodo findViewById()):
                                viewHolder.firstnameView.setText(item.firstname);
                                viewHolder.lastnameView.setText(item.lastname);
                               
                                // Ritorniamo la View:
                                return convertView;
                        }       
                               
                       
                };
               
                // Lo impostiamo come adapter della listView:
                listView.setAdapter(arrayAdapter);
        }
       
                       

        // Classe statica interna che rappresenta un generico oggetto da rappresentare che contiene solo i campi firstname e lastname:
        private static class CustomItem {
                public String firstname;
                public String lastname;
        }

        // Metodo di utilità che permette la creazione di un certo numero di istanze di test da visualizzare:
        private CustomItem[] createItems() {
               
                CustomItem[] items = new CustomItem[20];        // Creo un array di 20 oggetti di tipo CustomItem
               
                // Popolo l'array con valori del tipo: (Firstname_0 Lastname_0), (Firstname_1 Lastname_1), etc
                for (int i = 0; i < items.length; i++) {       
                        items[i] = new CustomItem();
                        items[i].firstname = "Firstname_" + i;
                        items[i].lastname = "Lastname_" + i;
                }
               
                return items;
        }
       
        /* Classe interna che mantiene i riferimenti alle TextView per evitare la continua invocazione dei metodi findViewById */
        private static class ViewHolder{
                public TextView firstnameView;
                public TextView lastnameView;
               
        }
               
}       

Sostanzialmente in questo esempio usa 2 forme di ottimizzazione:

1) La prima ottimizzazione evita di creare una nuova View ogni volta controllando se la view referenziata dal parametro convertView è nullo...se è nullo significa che è la prima volta che il metodo viene chiamato e che deve crearla mediante inflating ma tutte le altre volte riutilizza tale view invece di crearne di nuove perchè l'operazione di inflating è abbastanza onerosa...

Ho capito bene il concetto? Ora però già quì mi sorge un dubbio...ogni volta che entra nel metodo lui controlla se la view convertView è nulla...ma praticamente è una cosa automatica di Android che chiamando il metodo getView gli passa questi parametri?

2) La seconda ottimizzazione consiste nell'evitare di dover chiamare i metodi findViewById ogni volta che si vuole ottenere la View corrente...in pratica usa una classe interna ViewHolder che conterrà i riferimenti alle 2 TextView di interesse dove andrò a mettere i dati da visualizzare in output (dal momento che si usa anche l'ottimizzazione 1 e che la View viene riutilizzata di volta in volta tale oggetto sarà unico).
Quindi si crea un oggetto ViewHolder ed assegna ai suoi 2 campi i valori delle 2 TextView di interesse usando il metodo findViewById() solo nel caso in cui sia la prima View ad essere costruita...tutte le altre volte userà i riferimenti salvati nell'oggetto di tipo VieHolder appena costruito...

La cosa che non capisco a pieno è cosa faccia esattamente il metodo convertView.setTag(viewHolder);
In pratica stà dicendo che alla View appena creata devo associare l'oggetto viewHolder di tipo ViewHolder che contiene i riferimenti alle TextView in cui inserire i valori della View da visualizzare?

E con la riga:
Codice: [Seleziona]
viewHolder = (ViewHolder)convertView.getTag(); mi viene da pensare che ottenga il riferimento all'oggetto viewHolder che contiene i riferimenti da usare così da non dover richiamare di volta in volta i metodi findViewById()

Però non sono proprio certo di aver capito bene...qualcuno che illumina un povero autodidatta?

Tnx
Andrea

Offline Verandi

  • Moderatore
  • Utente normale
  • *****
  • Post: 378
  • Respect: +75
    • Mostra profilo
  • Sistema operativo:
    Windows 7
Re:Dubbio sull'uso di tag per ottimizzazione creazione View dentro un Adapter
« Risposta #1 il: 10 Settembre 2011, 19:56:22 CEST »
+1

Sostanzialmente in questo esempio usa 2 forme di ottimizzazione:

1) La prima ottimizzazione evita di creare una nuova View (...)

Ho capito bene il concetto? Ora però già quì mi sorge un dubbio...ogni volta che entra nel metodo lui controlla se la view convertView è nulla...ma praticamente è una cosa automatica di Android che chiamando il metodo getView gli passa questi parametri?

Sì, hai capito perfettamente.  :-) E sì, il metodo getView() passa sempre la convertView, position, parent, utili rispettivamente per: riciclo della view, differenziazione in base alla posizione, utilizzo dei parametri di layout della parent nella view che si sta creando.

La cosa che non capisco a pieno è cosa faccia esattamente il metodo convertView.setTag(viewHolder);
In pratica stà dicendo che alla View appena creata devo associare l'oggetto viewHolder di tipo ViewHolder che contiene i riferimenti alle TextView in cui inserire i valori della View da visualizzare?

E con la riga:
Codice: [Seleziona]
viewHolder = (ViewHolder)convertView.getTag(); mi viene da pensare che ottenga il riferimento all'oggetto viewHolder che contiene i riferimenti da usare così da non dover richiamare di volta in volta i metodi findViewById()

Sì, perfetto.   :-P Il setTag() associa o, meglio come scrivi tu, "riferisce" un oggetto qualsiasi alla view, il getTag() lo riprende.

Offline AndreaNobili

  • Utente junior
  • **
  • Post: 75
  • Respect: 0
    • Mostra profilo
Re:Dubbio sull'uso di tag per ottimizzazione creazione View dentro un Adapter
« Risposta #2 il: 10 Settembre 2011, 20:17:29 CEST »
0
Ok,
perfetto...allora non credo ci sia nulla da aggiungere in merito...ti ringrazio molto :-)

Al prossimo dubbio tornerò a chiedere a voi ;-)