venerdì 22 aprile 2011

Android, menu per le applicazioni (GList v.2)

Tutti i dispositivi per Android dispongono di un tasto per richiamare il menu dell'applicazione in esecuzione. I menu permettono all'utente di interagire con l'applicazione e di modificarne il comportamento. Non vi è alcun dubbio, quindi, che costituiscono una parte dell'applicazione che in molti casi è indispensabile! Non possiamo, infatti, appesantire l'interfaccia grafica di un'activity con bottoni utili al setup dell'applicazione. Meglio includere gli stessi all'interno di un menu. Tale menu, ovviamente, è disponibile all'utente solo se previsto dal programmatore, vediamo come crearne uno.
Android definisce per le applicazioni tre tipi di menu:
  • menu delle opzioni: menu accessibile premendo l'apposito tasto sul dispositivo;
  • menu per la scelta rapida: menu accessibile attraverso il tocco su una view (come ad esempio per l'elemento di una lista);
  • sottomenu: menu che raccoglie sotto un unico gruppo una lista di opzioni, condensate all'interno di un'unica voce;
Android separa il codice per l'istanza del menu da quello per la gestione degli eventi.
Per l'istanza di un menu Android richiede al programmatore la descrizione dello stesso all'interno di una risorsa xml e la sovrascrittura di alcuni metodi dell'activity. La descrizione del menu avviene attraverso i seguenti tag xml:
  • menu: è l'elemento radice che descrive il menu, il contenitore;
  • item: permette, in fase di inflating, l'istanza di oggetti MenuItem (interfaccia del package android.view). Questo elemento può a sua volta contenere altri elementi menu per descrivere, quindi, sottomenu. Le proprietà android:id, android:icon e android:title permettono di specificare, rispettivamente: l'id della voce del menu, il riferimento a una risorsa drawable e il testo per la voce del menu;
  • group: è un contenitore (invisibile) di elementi item (gli oggetti del menu), permette di raggruppare vari item sotto un'unica categoria (eventualmente da abilitare o disabilitare);
Il tag menu richiede come attributo il nome dello schema xml (attributo xmlns:android), che per Android è http://schemas.android.com/apk/res/android. L'elemento item può caratterizzare l'oggetto istanziato attraverso molti attributi, alcuni di questi sono: android:titleCondensed, per assegnare all'icona un testo più breve (Android userà questo testo qualora quello assegnato di default, con l'attributo visto prima, risulti lungo e quindi illeggibile); android:onClick, per collegare l'evento generato dalla selezione di un bottone a un metodo presente nell'activity (il metodo deve ricevere come argomento un oggetto MenuItem); android:visibile, specifica con un valore logico la visibilità dell'item; android:enabled, specifica con un valore logico se l'item è abilitato. Gli attributi visti per item sono validi anche per gli elementi group del menu. Ovviamente essendo group un elemento che fa da contenitore non prevede l'attributo android:onClick. Per questo elemento è invece valido l'attributo android:checkableBehavior, che permette di specificare il comportamento di un gruppo di item attraverso le costanti: none (nessuna voce del gruppo può essere selezionato), all (tutti le voci del gruppo possono essere selezionati) e single (una sola voce del gruppo può essere selezionato).
Il menu per le opzioni dell'applicazione viene istanziato una sola volta, non appena l'utente preme il bottone sul dispositivo. Il metodo dell'activity che viene invocato in corrispondenza di questo evento è public boolean onCreateOptionsMenu(Menu menu). Pertanto, se vogliamo dotare la nostra activity di un comodo menu dobbiamo intervenire in questo punto!
L'activity della nostra applicazione eredita tale metodo che va dunque riscritto, occorre passare all'oggetto che istanzia il menu un riferimento alla risorsa xml che lo descrive. La classe MenuInflater, del package android.MenuInflater, è usata da Android per istanziare un oggetto Menu a partire da una risorsa xml. E' possibile ottenere un riferimento a questo oggetto attraverso il metodo getMenuInflater() (della classe Activity). Infine, per caratterizzare il menu con la risorsa xml che lo descrive passiamo al metodo inflate() i riferimenti alla stessa e all'oggetto menu del metodo. Ecco un esempio:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater=getMenuInflater();
inflater.inflate(R.menu.options,menu);
return true;
}
Il metodo ritorna true se il menu deve essere visibile all'utente, false in caso contrario. Per la getsione degli eventi collegati a un menu Android usa il metodo public boolean onOptionsItmSelected(MenuItem item). Al metodo viene passato l'id dell'item selezionato dall'utente, il codice dovrà dunque riconoscere tale id e chiamare, a seconda dei casi, un metodo dell'activity per la gestione della selezione. Trattandosi di un metodo ereditato dalla classe Activity va quindi riscritto. Ecco un esempio:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.option1:
method1();
return true;
case R.id.option2:
method2();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Ogni caso previsto dalla struttura di controllo ritorna true per indicare al chiamante (l'activity dell'applicazione in esecuzione) la gestione dell'opzione selezionata. Il caso usato di default passa l'item selezionato alla superclasse che, non sapendo gestire alcuna opzione, ritorna sempre false! L'attributo android:onClick permette di specificare durante la scrittura del file xml il metodo da invocare alla pressione dell'item selezionato. Se questo attributo è presente nella risorsa xml, Android delega la gestione dell'evento al metodo indicato dalla stessa. In altre parole, l'attributo android:onClick ha la precedenza sul metodo onOptionsItemSelected() nella gestione degli eventi legati a una voce di un menu. Tale attributo, tuttavia, è valido solo per API di livello 11 e successive versioni. Qui trovate un esempio di menu per le opzioni. Segue l'immagine del menu aggiunto all'applicazione:


Android permette di disporre nel menu delle opzioni le prime sei voci selezionabili, se il menu ne prevede più di sei l'ultima icona, allora, permetterà l'accesso alle altre opzioni non visibili. Ecco come Android dispone gli item di un menu con più di sei opzioni:



Il menu per la scelta rapida è il menu che Android mostra quando l'utente preme a lungo (non più di 2 secondi) su un item o una view. Per assegnare un menu per la scelta rapida a un oggetto View è necessario registrare l'oggetto al menu, con il metodo registerForContextMenu(). Al metodo va ovviamente passato il riferimento dell'oggetto View da registrare. L'istanza del menu avviene sovrascrivendo il metodo public void onCreateContextMenu(), per la gestione degli eventi il metodo da riscrivere è invece public boolean onContextItemSelected(). Per l'istanza del menu possiamo fare riferimento a questo pezzo di codice:
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater=getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
}
Anche in questo caso l'obiettivo è ottenere un riferimento all'oggetto che istanzia il menu (metodo getMenuInflater()) e passare a quest'ultimo un riferimento alla risorsa xml che descrive il menu (con il metodo inflate()). Il metodo prevede come argomenti un oggetto ContextMenu (il menu per la scelta rapida che verrà istanziato), il riferimento all'oggetto View che dovrà ricevere un menu per la scelta rapida e un oggetto ContextMenuInfo (con informazioni aggiuntive sull'oggetto View).
Quando l'utente esegue una scelta all'interno di un menu per la scelta rapida viene invocato il metodo public boolean onContextItemSelected(MenuItem item), il cui codice è molto simile al metodo onOptionItemSelected() già visto in precedenza.
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info=(AdapterContextMenuInfo)item.getMenuInfo();
switch(item.getItemId()){
case R.id.option1:
method1();
return true;
case R.id.option2:
method2();
return true;
default:
return super.onContextItemSelected(itm);
}
}
Qui trovate un esempio di menu per la scelta rapida. L'immagine che segue mostra la comparsa del menu dopo la pressione di un elemento della lista:


Quando il menu per la scelta rapida contiene troppe opzioni è allora utile organizzare le stesse opzioni all'interno di un sottomenu, possibilmente raggruppandole (con il tag xml group). La realizzazione di un sottomenu è davvero molto semplice, è sufficiente aggiungere all'interno della risorsa xml che descrive il menu per la scelta rapida un nuovo elemento menu con altre opzioni. Le voci aggiunte all'interno del sottomenu saranno poi accessibili tramite l'opzione del menu per la scelta rapida che le contiene.


Come potete notare dalle immagini mostrate sopra non è possibile aggiungere un'icona all'interno di un menu per la scelta rapida (Android ignora il riferimento alla risorsa drawable eventualmente passata con l'attributo android:icon).
Vi invito a vedere il codice sorgente per il menu di scelta rapida, all'interno dei sottomenu ho avuto alcuni problemi nella gestione degli elementi della lista. Il metodo getMenuInfo() ritorna, infatti, un valore nullo per un item che appartiene a un sottomenu. Non ne sono sicuro ma dovrebbe trattarsi di un bug, possiamo comunque rimediare salvando il riferimento tornato dal metodo getMenuInfo() in una variabile di appoggio e usare la stessa all'interno della porzione di codice per il sottomenu.


Se volete assegnare al menu per la scelta rapida un titolo usate all'interno del codice Java il metodo setHeaderTitle(String title) (non sono riuscito a trovare nella documentazione un attributo per il tag xml), su un oggetto Menu. In realtà Android offre numerosi metodi per generare menu e sottomenu attraverso il codice Java. Questi metodi possono essere utili anche a run-time, per modificare ad esempio il menu dell'applicazione dopo qualche modifica ai dati, li trovate all'interno della documentazione. Potete installare l'applicazione GList (v.2) sul vostro dispositivo attraverso il seguente codice QR:

qrcode

Cosa ho messo nella lista questa volta? Facilissimo, l'occorrente per fare gli gnocchi alla sorrentina!

sabato 16 aprile 2011

Un cestino per rm

Il comando rm (come già detto qui) non usa un cestino per i file cancellati e va usato sempre con cautela. Vi propongo un piccolissimo script che aggiunge un nuovo comando alla vostra shell, l'ho chiamato srm e sta per secure rm. Il comando crea nella home dell'utente un cestino per i file cancellati con il comando srm, potete crearlo aprendo l'editor con il comando sudo mousepad /usr/bin/srm e scrivendo:
#!/bin/bash
TRASH=$HOME/Trash
mkdir $TRASH &> /dev/null
while [ ! -z "$1" ]
do
mv "$1" $TRASH
shift
done
Per poter eseguire i comandi dovete assegnare i permessi con sudo chmod +x /usr/bin/srm. Lo script provvede a spostare i file nel cestino (la cartella Trash/) finché esiste ancora un argomento passato al comando (il path o il nome di un file), quindi incrementa l'indice al prossimo argomento e ripete la condizione prevista dal ciclo. Qualcuno potrebbe pensare: perché non creare un alias per rm, ad esempio con il comando alias rm='srm'?
La risposta è semplice, se create l'alias detto sopra poi non potete più cancellare i file nel cestino con il comando rm (che altrimenti invocherebbe se stesso e quindi lo script visto sopra, generando un errore). Pertanto, se decidete di usare questa soluzione ricordatevi di cancellare i file con il nuovo comando srm.
Dimenticavo, se avete creato l'alias per il comando rm potete annullarne gli effetti con il comando unalias rm.

venerdì 15 aprile 2011

Conky, widget orizzontale


Lo spazio sul desktop di un netbook è sempre poco e in questi giorni sto sperimentando nuove soluzioni. L'ultima versione di Xfce è davvero funzionale ed elegante, ho disposto tutti i collegamenti su un unico pannello a scomparsa. Le informazioni sulle risorse di sistema, invece, sono descritte da uno script per Conky che riporto qui di seguito (file /etc/conky/conky.conf):
background yes
use_xft yes
xftfont DejaVu Sans Mono:size=8
xftalpha 0.8
out_to_console no
update_interval 5.0
total_run_times 0
draw_shades no
own_window yes
own_window_type override
own_window_transparent yes
double_buffer yes
default_color 555755
color1 grey
alignment top_middle
gap_x 0
gap_y 2
#no_buffers yes
use_spacer yes

TEXT
${image /opt/conky/cpu.png -p 0,0}${image /opt/conky/ram.png -p 150,0}${image /opt/conky/hdd.png -p 305,0}${image /opt/conky/swap.png -p 535,0}${image /opt/conky/temperature.png -p 768,0}
${goto 40}${color}cpu: ${color1}${cpu cpu}% ${cpubar 5,50 cpu}${goto 192}${color}ram: ${color1}$memperc% ${membar 5,50}${color}${goto 348}ssd: ${color1}${fs_used /}/${fs_size /} ${fs_bar 5,50 /}${color}${goto 578}swap: ${color1}${swap}/${swapmax} ${swapbar 5,50}${color}${goto 810}temperature: ${color1}${exec tempcpu}${goto 150}
Aprite il file conky.conf e copiate le istruzioni riportate sopra (sudo mousepad /etc/conky/conky.conf). Prestate attenzione, il widget descritto nello script si sviluppa su due sole righe (una per le icone e una per il testo). Le icone usate nello script vanno in /opt/conky/, le aggiungo qui di seguito:


Questi, dall'alto verso il basso, i nomi dati alle icone: cpu.png, hdd.png, ram.png, swap.png e temperature.png (i nomi dopo il download dovrebbero essere già quelli detti sopra).
Per leggere la temperatura del processore ho invece scritto questo piccolo script per shell (create il file per lo script con sudo mousepad /usr/bin/tempcpu):
#!/bin/sh
temp=$(cat /sys/class/thermal/thermal_zone0/temp)
unit=1000
temperature=$(($temp/$unit))
echo ${temperature}C°
Assegnate allo script i permessi per l'esecuzione, con sudo chmod +x /usr/bin/tempcpu. All'interno di Conky lo script viene eseguito dall'istruzione ${exec tempcpu}. Il valore della temperatura viene letto al path indicato all'interno dello script dal comando cat, che sul mio Aspire One è scritto dal modulo acerhfd. Infine, per dare modo ad Xfce di caricare il desktop e il pannello inferiore, ritardo l'avvio di Conky attraverso questo semplice script (create il file per lo script con sudo mousepad /usr/bin/execconky.sh):
#!/bin/sh
sleep 15
exec conky
Per l'esecuzione automatica di Conky richiamo lo script detto sopra (non dimenticate di assegnare, anche in questo caso, i permessi per l'esecuzione dello script, con sudo chmod +x /usr/bin/execconky.sh) all'interno della scheda Avvio automatico della finestra Sessione e avvio del menu Impostazioni di Xfce (Menu di Xfce > Impostazioni > Gestore delle impostazioni > Sessione e avvio > scheda Avvio automatico, bottone Add). Alla riga Comando della finestra Aggiungi applicazione va dunque scritto execconky.sh.

lunedì 11 aprile 2011

Android: GridView


GridView è un oggetto ViewGroup, come ListView. La particolarità di questo componente è ovviamente il layout applicato agli elementi di una sorgente di dati. GridView, infatti, dispone i dati all'interno di una griglia scorrevole. L'oggetto può essere dichiarato all'interno dei file xml, attraverso l'omonimo tag GridView, che all'interno del codice Java, attraverso l'omonima classe del package android.widget. GridView dispone di diversi attributi per la regolazione delle proprietà di layout:
  • android:columnWidth specifica la larghezza delle colonne, la stessa cosa viene fatta all'interno del codice con il metodo setColumnWidth(int);
  • android:horizontalSpacing specifica lo spazio vuoto fra le righe della griglia, nel codice Java va usato il metodo setHorizontalSpacing(int);
  • android:verticalSpacing specifica lo spazio vuoto fra le colonne della griglia, nel codice Java va usato il metodo setVerticalSpacing(int);
  • android:stretchMode specifica come gli oggetti devono occupare lo spazio messo a disposizione dalla griglia, nel codice Java va usato il metodo setStretchMode(int) a cui va passata una delle possibili costanti definite all'interno della classe (come ad esempio NO_STRETCH e STRETCH_COLUMN_WIDTH);
Trovate altre informazioni nella guida in linea per Android. Nell'esempio che vi propongo l'oggetto GridView viene utilizzato per la raccolta di immagini che compongono un puzzle. Il click su una immagine riassegna all'oggetto nella griglia una nuova immagine, prendendola dal set di pezzi che compone il puzzle.
Il layout principale dell'activity descrive un bottone per il reset (che riassegna nuovi tasselli alla griglia) e la griglia per le immagini. Vi riporto il pezzo di codice che descrive l'oggetto GridView:
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/PuzzleGridView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="62dp"
android:numColumns="3"
android:verticalSpacing="2dp"
android:horizontalSpacing="2dp"
android:stretchMode="columnWidth"
android:gravity="center"
Trovate il codice sorgente dell'intera applicazione a questo indirizzo. Il codice del metodo onCreate() è il seguente:
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 GridView gridview=(GridView) findViewById(R.id.PuzzleGridView);
 adapterForImage=new ImageAdapter(this);
 gridview.setAdapter(adapterForImage);
 gridview.setOnItemClickListener(new OnItemClickListener() {
  public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
   adapterForImage.moveImage(position);
   adapterForImage.notifyDataSetChanged();
  }
 });
}
Dopo l'assegnazione del layout principale (con setContentView()) il codice procede con la ricerca della view dell'oggetto GridView, a cui viene assegnato l'Adapter per la sorgente di dati. Viene, poi, descritto il gestore per gli eventi (che in questo esempio viene chiamato al tocco della View, metodo onItemClickListener()). Particolarmente importante è il metodo getView() della classe ImageAdapter (che estende BaseAdapter, una classe astratta che è alla base di altri Adapter). Il compito di questo metodo è la restituzione della View da inserire nella griglia.
public View getView(int position, View convertView, ViewGroup parent) {
 ImageView imageViewOfPiece;
 if (convertView==null) { 
  imageViewOfPiece=new ImageView(mContext);
  imageViewOfPiece.setLayoutParams(new GridView.LayoutParams(100,100));
  imageViewOfPiece.setScaleType(ImageView.ScaleType.FIT_XY);
  imageViewOfPiece.setPadding(1,1,1,1);
 } else imageViewOfPiece=(ImageView)convertView;
 imageViewOfPiece.setImageResource(table[position]);
 return imageViewOfPiece;
}
Per far avanzare le immagini toccate a quelle successive (in modo pseudo-casuale) ho scritto il seguente metodo:
public void moveImage(int position) {
 if(i==pieces.length-1) i=0;
 else i++;
 table[position]=pieces[i];
 nClick++;
 if(nClick==3) {
  i=(int)(Math.random()*(pieces.length-1));
  nClick=0;
 }
}
I primi tre click spostano di una posizione l'indice che punta alla successiva immagine dell'array. Dopo il terzo click l'indice assume un valore casuale, incrementato di una posizione per i nuovi prossimi tre click e così via... Viene effettuato anche un controllo ciclico sull'indice che non deve mai superare la capacità dell'array (diminuita di uno). Questo il puzzle completo:


Potete installare l'applicazione vista in questo esempio attraverso il seguente codice QR:

qrcode

domenica 3 aprile 2011

Android: override del metodo getView (GList v.1)


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:

qrcode

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!