Come già detto qui,
ArrayAdapter
assegna di default l'item di una sorgente di dati a una TextView
. Questo, ovviamente, non sempre è ciò che vogliamo fare nella nostra applicazione. Soprattutto se l'informazione da dare all'utente è strutturata su più campi dati. Il comportamento di default previsto dalla classe ArrayAdapter
nel metodo public View getView(int position, View convertView, ViewGroup parent)
può essere allora modificato sovrascrivendo proprio quest'ultimo! I parametri usati dal metodo prevedono: la posizione (variabile position
) dell'item all'interno dell'oggetto ArrayAdapter
; il riferimento a una vecchia View
(variabile convertView
) non più visibile, ad esempio a causa dello scorrimento della lista, che è preferibile riciclare anziché istanziare una nuova View
; il riferimento a l'oggetto ViewGroup
(variabile parent
) che contiene gli oggetti View
.Il nostro obiettivo, dunque, è descrivere la view dell'item all'interno del metodo
getItem()
. Dovendo modificare il processo di inflating (che, ricordo, istanzia gli oggetti a partire dalla risorsa xml che li definisce, il layout dell'item in questo caso) occorre in primo luogo ottenere un riferimento all'oggetto che svolge questo lavoro. Subito dopo, quindi, passiamo a tale oggetto un riferimento al layout da usare per le view degli item (che personalizzeremo a nostro piacimento). Le operazioni dette sopra avvengono attraverso le seguenti righe di codice:LayoutInflater inflater=(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemView=inflater.inflate(R.layout.record,null);
Nell'ultimo metodo possiamo infatti notare il riferimento al layout da applicare ai record della lista lista di oggetti! Non ci resta che caratterizzare il contenuto dei widget con le informazioni dell'item, il cui riferimento può essere ottenuto con il metodo
getItem(position)
. Il codice del metodo dovrà eventualmente collegare gli eventi dei bottoni ai rispettivi metodi di callback (nel nostro esempio occorre descrivere l'azione da intraprendere alla pressione del bottone Delete, vedere l'immagine dell'applicazione GList).Possiamo migliorare ulteriormente il codice dell'applicazione del metodo
getView()
! Non abbiamo infatti usato il parametro convertView
che, come detto prima, è un riferimento a una View
da riciclare. E' importante, infatti, evitare l'istanza di nuovi oggetti View
per non appesantire l'esecuzione dell'applicazione. Meglio quindi verificare la presenza di tale variabile e regolarsi di conseguenza, come viene mostrato nel codice che segue:...
if(convertView==null) {
LayoutInflater inflater=(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView=inflater.inflate(R.layout.record,null);
}
...
Ho modificato l'applicazione vista qui, personalizzando il layout degli item e aggiungendo nuove funzionalità, come il salvataggio e il ripristino dello stato dell'applicazione! Trovate qui il codice sorgente dell'applicazione. Attraverso il codice QR che segue potete invece installare l'applicazione sul vostro dispositivo Android:
Alcune osservazioni sull'applicazione:
Per salvare e leggere lo stato dell'applicazione, ovvero il contenuto della lista, ho usato un file di appoggio. L'oggetto da salvare va dunque serializzato e scritto attraverso uno stream sul file, a tale proposito la classe che descrive l'oggetto deve necessariamente implementare l'interfaccia Serializable. La lettura o la scrittura richiede uno stream verso il file di appoggio detto sopra che Android colloca, per ogni applicazione, nel path
/data/data/package_name/file_name.dat
. Per l'input da file usate lo stream della classe FileInputStream
, per l'output su file usate lo stream della classe FileOutputStream
. Dagli stream ottenuti istanziamo, a seconda dei casi, uno stream per la lettura o la scrittura degli oggetti. In tal caso usate lo stream della classe ObjectOutputStream
e il metodo writeObject(oggetto_da_salvare)
per la scrittura dell'oggetto, oppure lo stream ObjectInputStream
e il metodo readObject()
per la lettura dell'oggetto (che va trattato con un casting per ripristinare la classe dell'oggetto). Non dimenticate, infine, di chiudere gli stream aperti con il metodo close()
! Vi riporto il metodo usato per salvare lo stato della lista:public boolean save() {
Log.i("INFO","Method save()!");
if(!save) return true;
try {
Toast.makeText(this,"Saving...",Toast.LENGTH_LONG).show();
FileOutputStream file=new FileOutputStream(fileName);
ObjectOutputStream data=new ObjectOutputStream(file);
data.writeObject(arrayOfItems);
data.close();
file.close();
return true;
}
catch(Exception e) {
Log.i("INFO",e.toString());
return false;
}
}
e quello per caricarlo dal file:
public boolean read() {
Log.i("INFO","Method read()!");
try {
FileInputStream file=new FileInputStream(fileName);
Toast.makeText(this,"Loading...",Toast.LENGTH_LONG).show();
ObjectInputStream data=new ObjectInputStream(file);
arrayOfItems=(ArrayList<MyItem>)data.readObject();
arrayOfItemsAdapter=this.configArrayAdapter();
ListView listView=(ListView)findViewById(R.id.viewOfItems);
listView.setAdapter(arrayOfItemsAdapter);
data.close();
file.close();
return true;
}
catch(Exception e) {
Log.i("INFO",e.toString());
return false;
}
}
Gli eventi legati ai clic sui bottoni dell'interfaccia sono legati, all'interno dei file xml che descrivono i vari layout, ai metodi definiti nella classe dell'activity attraverso l'attributo
android:onClick="nomeMetodo"
. Non dimenticate di implementarli all'interno della classe!Quando salvare la lista di oggetti? La lista va evidentemente salvata solo se l'utente la modifica. A tale proposito ho inserito all'interno della classe una variabile booleana (
save
) il cui stato influenzerà le operazioni di lettura e scrittura. I metodi in grado di modificare la variabile save sono: addItem()
, clearAll()
e remove()
. Se ad esempio l'utente avvia l'applicazione per una consultazione, senza aggiungere o rimuovere oggetti nella lista, all'uscita dell'applicazione lo stato della lista non va conservato poiché immutato!In che punto dell'applicazione devo salvare o ripristinare lo stato dell'applicazione? In questa applicazione ho deciso di salvare la lista di oggetti non appena l'utente preme il tasto back o il tasto home. Il metodo
save()
viene quindi invocato all'interno del metodo onPause()
. Il metodo read()
va invece invocato all'interno dei metodi onCreate()
, onRestart()
e reloadItems()
.L'applicazione vista in questo esempio si presta alla memorizzazione di cose da fare, da comprare, etc... GList sta per generic list, lista generica. Nell'esempio ho memorizzato nella lista l'occorrente per cucinare i fusilli alla sorrentina!
Grazie Luca dell'articolo.
RispondiElimina