Sei sulla pagina 1di 91

Laboratorio di Ingegneria del Software A.

A 2009/2010 Programmazione su Android A cura di Carlo Pelliccia

Lezione 1

Primi passi con Android


Meno di tre anni fa Google ha rilasciato una versione preliminare del kit di sviluppo di Android, il suo nuovo sistema operativo dedicato agli smartphone. Futurologi e semplici appassionati si divisero immediatamente tra entusiastici e scettici. I detrattori, in particolar modo, hanno sempre visto in Android un esperimento, e non qualcosa di reale al quale i produttori di dispositivi avrebbero creduto. A loro favore ha deposto il fatto che, per un periodo piuttosto lungo, nessuno smartphone equipaggiato con Android ha fatto capolino sul mercato, bench il sistema ed i suoi strumenti di sviluppo fossero ormai disponibili da parecchio tempo. La tecnica di Google, in realt, era ed ancora quella di sempre: far venire lacquolina in bocca (e far parlare di s) con versioni preliminari dei suoi software e dei suoi servizi. Nel caso di Android, molti sviluppatori sono stati fidelizzati e fatti appassionare ad un sistema che, allora, non era ancora sul mercato. Nel frattempo le cose sono cambiate: Android stato consolidato, e molti produttori di dispositivi mobili hanno aderito o stanno aderendo allalleanza capeggiata da Google. Grazie alle strategie di Google, esiste oggi una comunit molto ampia di sviluppatori, estremamente produttiva, che altri sistemi mobili non possono vantare. Migliaia di applicazioni sono state sviluppate, e molte altre lo saranno nei prossimi tempi. Il sistema appare inoltre stabile ed offre potenzialit molto ampie. Come fatto Android Android, essendo un sistema operativo di moderna fattura, abbastanza complesso. Anche se il suo target sono i dispositivi mobili, larchitettura di Android ha poco da invidiare a quelle dei comuni sistemi per desktop o laptop. Tale architettura presentata schematicamente in Figura 1.

Figura 1 - L'architettura di Android

Come si evince dalla figura, Google ha attinto a piene mani dal mondo Open Source. Il cuore di ogni sistema Android, tanto per cominciare, un kernel Linux, versione 2.6. Direttamente nel kernel sono inseriti i driver per il controllo dellhardware del dispositivo: driver per la tastiera, lo schermo, il touch screen, il Wi-Fi, il Bluetooth, il controllo dellaudio e cos via. Sopra il kernel poggiano le librerie fondamentali, anche queste tutte mutuate dal mondo Open Source. Da citare sono senzaltro OpenGL, per la grafica, SQLite, per la gestione dei dati, e WebKit, per la visualizzazione delle pagine Web. Larchitettura prevede poi una macchina virtuale ed una libreria fondamentale che, insieme, costituiscono la piattaforma di sviluppo per le applicazioni Android. Questa macchina virtuale si chiama Dalvik, e sostanzialmente una Java Virtual Machine. Come verificheremo pi tardi, alcune

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

delle caratteristiche di Dalvik e della sua libreria non permettono di identificare immediatamente la piattaforma Java disponibile in Android con una di quelle di riferimento (Java SE, Java ME). Nel penultimo strato dellarchitettura possibile rintracciare i gestori e le applicazioni di base del sistema. Ci sono gestori per le risorse, per le applicazioni installate, per le telefonate, il file system ed altro ancora: tutti componenti di cui difficilmente si pu fare a meno. Infine, sullo strato pi alto dellarchitettura, poggiano gli applicativi destinati allutente finale. Molti, naturalmente, sono gi inclusi con linstallazione di base: il browser ed il player multimediale sono dei facili esempi. A questo livello si inseriranno anche le applicazioni che, insieme, impareremo a sviluppare nellarco di questo corso. Installazione dellAndroid SDK Per sviluppare applicazioni che siano in grado di girare su sistemi Android, necessario installare sul proprio PC un apposito kit di sviluppo (SDK), che sia completo di emulatore, librerie e documentazione. LAndroid SDK disponibile gratuitamente per sistemi Windows, Linux e MacOS X. possibile scaricarlo collegandosi allindirizzo: http://developer.android.com/sdk/ Vi verr proposto di scaricare la pi recente versione disponibile. Procedete pure al download del pacchetto adatto al vostro sistema. Al momento della stesura di questa lezione, la versione scaricabile dalla pagina la r05 (che sta per release 5), ma se ne trovate di pi recenti fate pure: non dovrebbero differire troppo da quella presa qui a riferimento. Linstallazione del kit veramente semplice. Lunica cosa di cui bisogna accertarsi, prima di procedere, di soddisfare i requisiti di base. In particolare, richiesto che il sistema disponga gi di un Java SDK (JDK) versione 5 o successiva. strettamente indispensabile soddisfare questo requisito, poich Android si programma in Java, e senza un JDK non possibile compilare il codice. Dopo aver verificato i requisiti, possibile procedere. Prendete larchivio ZIP scaricato da Internet e scompattatelo dove meglio preferite. Accedete alla cartella cos ottenuta e lanciate lapplicazione SDK Setup, utile per completare linstallazione e la configurazione dello strumento di sviluppo.

Figura 2 - L'SDK, al primo avvio, propone l'installazione delle diverse piattaforme Android disponibili.

Verr caricata una GUI che vi permette di gestire i device virtuali ed i componenti aggiuntivi. Appena installato, lSDK praticamente vuoto. Contiene solo gli strumenti di sviluppo, ed al suo interno non c traccia di alcuna versione del sistema Android. Al primo avvio, dunque, viene eseguita unanalisi e proposto allutente di scaricare allinterno dellSDK le diverse piattaforme
Pagina 2

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

Android disponibili, che potranno essere successivamente utilizzate per lo sviluppo ed il test della applicazioni. Se non viene proposto automaticamente il download di questi componenti, si pu procedere manualmente selezionando la voce Available Packages e selezionando le piattaforme desiderate. Procedete al download e allinstallazione automatica dei componenti proposti automaticamente o selezionati manualmente. Gestione dei device virtuali Il kit di sviluppo comprende un emulatore che ci consentir di provare le nostre creazioni sul PC, prima di installarle su un reale dispositivo equipaggiato con Android. Per sviluppare le applicazioni, quindi, dobbiamo imparare ad interagire con questo emulatore. Il primo concetto che si deve assimilare quello che ha nome Android Virtual Device (AVD), cio dispositivo virtuale Android. Nel nostro PC possiamo creare e configurare quanti dispositivi virtuali vogliamo. come avere tanti differenti smartphone da utilizzare per i propri test, solo che invece di dispositivi di plastica e silicio si tratta di macchine virtuali, fatte cio di puro software, da eseguire attraverso lemulatore. In questo modo anche possibile avviare contemporaneamente sullo stesso PC due o pi dispositivi virtuali, ad esempio per testare unapplicazione che fa interagire pi smartphone, come una chat o un gioco multiplayer. NellSDK di Android selezionate la voce Virtual Devices. Accederete cos allelenco dei device virtuali configurati. Inizialmente lelenco vuoto. Create uno o pi device virtuali, con il tasto New.

Figura 3 - La maschera di creazione di un nuovo dispositivo virtuale.

Le informazioni da fornire sono: Name: il nome che si vuole attribuire al dispositivo virtuale, ad esempio Device1. Target: la piattaforma Android che sar installata nel dispositivo. Lelenco a tendina permette di scegliere fra le differenti piattaforme scaricate ed integrate allinterno del proprio SDK. SD Card: qui possibile dotare il dispositivo virtuale di una scheda di memoria virtuale. possibile specificare sia il percorso di un file di immagine di una scheda di memoria, se si

Pagina 3

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

vuole riutilizzare una memoria virtuale esistente, sia una dimensione di spazio, per creare una nuova memoria virtuale. Skin: la risoluzione del dispositivo. Si pu specificare manualmente o selezionare fra quelle predefinite. Hardware: permette di impostare alcuni requisiti hardware del dispositivo, come la densit dei pixel, la presenza di un accelerometro e cos via.

Una volta creato, il nuovo dispositivo virtuale entrer a far parte dellelenco gestito dallSDK, e potr quindi essere utilizzato per eseguire il debug ed il test delle applicazioni. Lemulatore Android Se non siete gi pratici nellutilizzo di Android, prima di iniziare a programmare meglio che ci prendiate confidenza. Esplorando le applicazioni di base potrete cos entrare nellottica del sistema, per imparare i principi di funzionamento e di design delle sue applicazioni. Potete avviare un dispositivo virtuale dallinterno dellSDK, selezionandone uno creato in precedenza ed attivando il tasto Start. Qualche istante di pazienza (al primo lancio anche qualcosa in pi) e lemulatore caricher e render disponibile il dispositivo virtuale Android selezionato. Con il mouse possibile simulare il touchscreen del dispositivo, cliccando sullo schermo. Fatevi un giro e prendete pure confidenza con lambiente.

Figura 4 - L'emulatore Android.

Come prima cosa utilizzate le applicazioni di base, come il browser o la rubrica: vi aiuteranno molto nel comprendere i principi di utilizzo del sistema. Poi passate a del materiale pi tecnico: il men principale contiene la voce Dev Tools, che raccoglie una serie di strumenti dedicati a chi Android vuole programmarlo, e non solo farci un giro di prova. Tra questi spicca lemulatore di terminale, che permette di avere una shell di sistema per uninterazione di pi basso livello con il dispositivo. ADT per Eclipse Bench lAndroid SDK disponga di script che automatizzano linstallazione delle applicazioni, il lancio dellemulatore ed il debug del codice, lavorare in un ambiente integrato, con ogni opzione a portata di clic, sicuramente pi facile. Specie quando lambiente integrato si chiama Eclipse. Nel
Pagina 4

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

sito di Android contattato in precedenza disponibile anche un plug-in per la celebre piattaforma di sviluppo Open Source. Questo add-on si chiama Android Development Tools for Eclipse, che abbreviato diventa ADT. Il modulo, al momento della stesura di questa lezione, funziona con le pi recenti versioni di Eclipse, che sono la 3.3, la 3.4 e la 3.5. Pu essere installato direttamente dallinterno della piattaforma di sviluppo. Avviate Eclipse ed eseguite il wizard per linstallazione di nuovi componenti. In Eclipse 3.5 lo si fa con la voce di men Help Install New Software. Giunti a destinazione, specificate come fonte del plug-in il seguente indirizzo: https://dl-ssl.google.com/android/eclipse/ Digitate lindirizzo, premete invio ed attendente che venga presentata la lista dei componenti disponibili per il download. Selezionateli tutti e procedete. Il plug-in per lo sviluppo del software Android sar automaticamente scaricato ed installato.

Figura 5 - Installazione del plug-in ADT in Eclipse 3.5.

Dopo il riavvio di Eclipse, recatevi immediatamente nella schermata delle preferenze dellambiente (voce di men Window Preferences). Qui troverete disponibile la nuova categoria Android, nellelenco sulla sinistra. Selezionatela.

Pagina 5

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

Figura 6 - La schermata di configurazione dell'ADT.

Impostate il percorso del vostro Android SDK: necessario affinch Eclipse possa agganciare il kit di sviluppo. Durante questa fase dovreste anche ricevere un pop-up per laccettazione della licenza del plug-in. Ciao, Mondo Androide! venuto il momento di utilizzare ADT e lemulatore per programmare la nostra prima applicazione Android. Naturalmente sar una variante del classico Ciao, Mondo!. Avviate Eclipse. Grazie ad ADT disponete ora di una nuova categoria di progetto, chiamata Android Project. Create un progetto di questo tipo.

Figura 7 - Eclipse dispone ora di una categoria per progetti e file Android.

Pagina 6

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

Figura 8 - La maschera di creazione di un progetto Android.

Nel wizard di creazione del progetto specificate la seguente configurazione: Project name: CiaoMondoAndroide Build target: selezionate la pi recente fra le piattaforme Android installate. Application name: Ciao Mondo Package name: mieapplicazioni.helloandroid Create Activity: CiaoMondoAndroideActivity Min SDK Version: lasciate bianco

Il progetto, a questo punto, pu essere creato, azionando il tasto Finish. Eclipse popoler automaticamente il progetto, inserendo le librerie di Android e la struttura di base dei progetti per questa piattaforma. In uno slancio di generosit, Eclipse provveder anche alla creazione della prima classe della soluzione, chiamata CiaoMondoAndroideActivity (come specificato alla voce Create Activity) ed inserita nel pacchetto it.ioprogrammo.helloandroid (come alla voce Package name). Aprite il codice della classe e modificatelo alla seguente maniera:
package mieapplicazioni.helloandroid; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class CiaoMondoAndroideActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

Pagina 7

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android
TextView tv = new TextView(this); tv.setText("Ciao, Mondo Androide!"); setContentView(tv); } }

Ora selezionate la radice del progetto CiaoMondoAndroide, selezionate la voce di men Run Run e nella finestra di selezione della tipologia di applicazione scegliete Android Application.

Figura 9 - Il progetto deve essere eseguito come una Android Application.

Lemulatore verr caricato. Eclipse provveder automaticamente ad installare al suo interno lapplicazione CiaoMondoAndroide, per poi avviarla non appena loperazione sar completata. fatta: il vostro primo software per Android sta girando davanti ai vostri occhi.

Figura 10 - L'applicazione CiaoMondoAndroide eseguita nell'emulatore.

Successivamente, accedendo alle configurazioni di esecuzione (voce di men Run Run Configurations), sar possibile alterare i parametri di avvio dellemulatore e dellapplicazione. Tra questi, anche il dispositivo virtuale sul quale sar installato ed avviato il software. Fate qualche esperimento. Provate, ad esempio, a creare differenti AVD, collaudando cos il software con schermi di differenti dimensioni e proporzioni. Un altro esperimento interessante, che si consiglia di compiere prima di procedere oltre, lutilizzo del debugger di Eclipse con lapplicazione Android. Ponete un breakpoint sulla classe realizzata ed avviate di nuovo emulatore ed applicazione, questa volta in modalit debug. Dalvik e le librerie Android Superata la prova del primo progetto Android, torniamo ad occuparci dei concetti fondamentali per la programmazione in questo ambiente. Come abbiamo appreso e dimostrato, la piattaforma di sviluppo di natura Java. Tuttavia si tratta di una piattaforma particolare e personalizzata, che vale la pena approfondire. La macchina virtuale, chiamata Dalvik, sembra essere una Java Virtual Machine, ma in realt non lo del tutto. Mi spiego meglio: una Java Virtual Machine esegue del codice bytecode, giusto? Ecco, la Dalvik Virtual Machine non esegue bytecode standard, ma un
Pagina 8

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

altro linguaggio, chiamato DEX (Dalvik EXecutable), studiato appositamente per una migliore resa in uno smartphone. Con lAndroid SDK ed Eclipse, ad ogni modo, ci sembrer di utilizzare una regolare Java Virtual Machine. Lambiente di sviluppo, infatti, provvede automaticamente alla generazione del codice DEX, ri-compilando il bytecode che a sua volta frutto di una prima comune compilazione Java. Per lo sviluppatore, ad ogni modo, tutto trasparente. Questa peculiarit di Dalvik, quindi, non influenzer il nostro modo di programmare. La stessa considerazione, invece, non pu essere fatta riguardo la libreria di base che affianca Dalvik. Aprite il documento al percorso docs/reference/packages.html, nel vostro Android SDK. lindice dei package Java compresi nella libreria di base. Scorretela velocemente e traete pure le prime conclusioni. C parecchio della Standard Edition di Java, ma non c tutto. Ad esempio non ci sono AWT e Swing. I pacchetti fondamentali, per, ci sono tutti, ed appaiono in larga misura identici a come li vuole Sun. Davvero poco viene dalla Micro Edition, praticamente nulla. La piattaforma Java ME stata snobbata da Android, che le ha preferito una libreria pi simile a quella di un sistema desktop. Non passano poi inosservati i tanti package con prefisso android che, naturalmente, sono esclusivi di questa speciale piattaforma. Servono per linterazione diretta con le funzionalit del sistema sottostante. Ad esempio: il package android.widget contiene i componenti custom di Android per la costruzione delle interfacce grafiche (in CiaoMondoAndroide abbiamo usato TextView); nel pacchetto android.graphics ci sono le funzioni primitive per la grafica di pi basso livello; in android.location ci sono gli strumenti per interagire con un eventuale ricevitore GPS compreso nel dispositivo. Ciascuno dei pacchetti android, naturalmente, meriterebbe una trattazione estesa e completa, tanti sono i possibili campi di applicazione. Ne emerge il profilo di una piattaforma di sviluppo complessa, perch molto ricca, ma semplice, perch ordinata e perch condivide parecchio con ledizione tradizionale di Java. Il consiglio, naturalmente, quello di tenere sempre a portata di mano la documentazione delle API di Android. Fatevi poi guidare dalla curiosit: date pure una prima occhiata alle classi che pi stuzzicano la vostra fantasia. Principi di programmazione Chi programma con Java ME sa che le MIDlet sono il mattone fondamentale delle applicazioni MIDP; chi crea applicazioni Web con Java EE non pu ignorare cosa sia una Servlet; persino i programmatori meno esperti sanno che le applicazioni Java, per girare in un browser, devono essere inglobate in una Applet. Tutto questo per dire che ciascun ambiente, Java e non, dispone dei suoi mattoni fondamentali, che lo sviluppatore pu estendere ed implementare per trovare un punto di aggancio con la piattaforma. Android non sfugge alla regola, anzi la amplifica. A seconda di quel che si intende fare disponibile un diverso modello. Android fornisce quattro mattoni di base: Attivit Le attivit sono quei blocchi di unapplicazione che interagiscono con lutente utilizzando lo schermo ed i dispositivi di input messi a disposizione dallo smartphone. Comunemente fanno uso di componenti UI gi pronti, come quelli presenti nel pacchetto android.widget, ma questa non necessariamente la regola. La classe dimostrativa CiaoMondoAndroideActivity unattivit. Le attivit sono probabilmente il modello pi diffuso in Android, e si realizzano estendendo la classe base android.app.Activity. Servizio Un servizio gira in sottofondo e non interagisce direttamente con lutente. Ad esempio pu riprodurre un brano MP3, mentre lutente utilizza delle attivit per fare altro. Un servizio si realizza estendendo la classe android.app.Service.
Pagina 9

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

Broadcast Receiver Un Broadcast Receiver viene utilizzato quando si intende intercettare un particolare evento, attraverso tutto il sistema. Ad esempio lo si pu utilizzare se si desidera compiere unazione quando si scatta una foto o quando parte la segnalazione di batteria scarica. La classe da estendere android.content.BroadcastReceiver. Content Provider I Content Provider sono utilizzati per esporre dati ed informazioni. Costituiscono un canale di comunicazione tra le differenti applicazioni installate nel sistema. Si pu creare un Content Provider estendendo la classe astratta android.content.ContentProvider. Unapplicazione Android costituita da uno o pi di questi elementi. Molto frequentemente, contiene almeno unattivit, ma non detto che debba sempre essere cos. I pacchetti APK Le applicazioni Android sono distribuite sotto forma di file APK (Android Package). Al loro interno vengono raccolti gli eseguibili in formato DEX, le eventuali risorse associate ed una serie di descrittori che delineano il contenuto del pacchetto. In particolare, nel cosiddetto manifesto, vengono dichiarate le attivit, i servizi, i provider ed i receiver compresi nel pacchetto, in modo che il sistema possa agganciarli ed azionarli correttamente. Torniamo, in Eclipse, sul progetto CiaoMondoAndroide. Al suo interno troverete un file chiamato AndroidManifest.xml, fatto come segue:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mieapplicazioni.helloandroid" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".CiaoMondoAndroideActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

il manifesto descrittore citato poco fa. Al suo interno potete e dovete dichiarare i componenti del vostro software. Eclipse, allatto di creazione del progetto, ha gi eseguito su di esso alcune configurazioni iniziali. Ad esempio ha registrato lattivit CiaoMondoAndroideActivity, ha specificato le propriet generali dellapplicazione ed ha anche generato ed impostato delle icone per il programma (res/drawable-qualcosa/icon.png). Ovviamente queste scelte possono essere alterate, e nuovi componenti possono essere aggiunti al progetto. Con lo speciale editor visuale messo a disposizione da Eclipse, vi risulter tutto molto semplice: sufficiente fare un po di pratica ed approfondire di volta in volta laspetto dinteresse. Una volta che il lavoro stato completato, possibile esportare il file APK da distribuire ai fortunati possessori di un sistema Android. Prima di distribuire in giro il pacchetto per necessario apporre su di esso una firma digitale. In caso contrario, Android non potr installarne i contenuti. Questo lunico vincolo imposto dal sistema. Il fatto che un pacchetto debba essere firmato non deve preoccupare lo sviluppatore: non necessario che una certification authority riconosca la chiave utilizzata per la firma. Di conseguenza possibile firmare un pacchetto APK anche
Pagina 10

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

servendosi di un certificato fatto in casa. In parole semplici: non bisogna pagare nessuno perch i nostri software siano autorizzati, possiamo fare tutto da noi. In Eclipse, ancora una volta, questione di pochi clic: selezionate la radice del progetto, attivate la voce di men File Export e scegliete il wizard Export Android Application.

Figura 11 - Selezione del wizard di esportazione di un'applicazione Android.

Al secondo step del wizard di generazione del pacchetto, vi verr chiesto da dove prelevare la firma digitale. Solitamente gli oggetti di questo tipo vengono raccolti e conservati allinterno di un keystore. In un keystore, cio, ci sono pi firme digitali. Se non avete mai formato un keystore in precedenza, o se semplicemente ne volete iniziare uno nuovo, selezionate lopzione Create new keystore.

Figura 12 - La selezione o creazione di un keystore.

Il keystore verr conservato allinterno di un file, il cui percorso va obbligatoriamente specificato. Scegliete dove riporre il keystore (nel caso in Figura 12, la directory C:\keystores) e date un nome a vostro piacimento a file (android_keystore, nel caso in immagine). Non c bisogno di usare unestensione particolare per il nome del file. invece buona pratica proteggere i propri keystore con una password, in modo che le nostre firme digitali non possano essere utilizzate nel caso in cui qualcuno ci rubi il file. Pertanto abbiate cura di impostare una password sufficientemente sicura. Siccome il keystore appena creato vuoto, il passo successivo del wizard ci fa creare una chiave, cio una firma digitale. Dobbiamo inserire il nome della chiave (detto alias), la password per lutilizzo della chiave, una validit in anni (di solito si usa il valore 25) ed i dati anagrafici di base del firmatario (nome e cognome).

Pagina 11

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 1 Primi passi con Android

Figura 13 - La maschera per la creazione di una nuova chiave di firma digitale.

Superata la fase di creazione o selezione del keystore e della chiave, il wizard fa scegliere dove salvare il pacchetto APK che sar generato. Scegliete la destinazione e concludete loperazione. fatta: il pacchetto stato generato e firmato. Potete ora installarlo su un dispositivo Android reale, in plastica, metallo e silicio.

Pagina 12

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android A cura di Carlo Pelliccia

Lezione 2

Gestione delle risorse


Se c un aspetto di Android dal quale si evince la modernit di questo sistema, la sua maniera di gestire le risorse ed i dati. Nelle piattaforme di sviluppo meno moderne, spesso e volentieri, le risorse esterne come i dati di configurazione, i messaggi di interfaccia, le immagini o altro materiale simile, sono trattate senza alcun riguardo speciale. Android, invece, richiede che i progetti siano organizzati in una certa maniera. La corretta gestione delle risorse, in questa piattaforma, importante tanto quanto la stesura del codice. In un certo senso, con Android non si pu imparare a programmare se prima non si apprende come organizzare e richiamare le risorse. La struttura dei progetti Android Avviamo Eclipse e torniamo sul progetto CiaoMondoAndroide, realizzato nella lezione precedente per dimostrare le funzionalit di base del kit di sviluppo per Android. Quando abbiamo creato il progetto, Eclipse ha predisposto per noi un albero di cartelle, allinterno del quale sono stati generati automaticamente diversi file. Guardate la Figura 1, che mostra la situazione del file system allatto di creazione del progetto.

Figura 1 - L'organizzazione di default di un progetto Android.

Tra i file generati automaticamente c AndroidManifest.xml, cio il descrittore dellapplicazione, che gi abbiamo iniziato a conoscere. Torneremo ad approfondirlo mano a mano che gli argomenti trattati ce ne daranno occasione. Oltre al descrittore c il file default.properties, poco rilevante per noi, poich serve esclusivamente al sistema di build automatico. Ci sono poi delle directory: src, assets, res e gen. La prima, src, quella dove dobbiamo andare a realizzare i package e le classi della nostra applicazione. Le cartelle res e assets servono per ospitare le risorse esterne necessarie allapplicazione, come le immagini, i file audio ed altro ancora. La cartella res, in particolar modo, gode di una speciale struttura predefinita, formata dalle sotto-directory drawable, layout e values. Le cartelle del gruppo drawable servono per le immagini utilizzate dal software, mentre layout e values ospitano dei speciali file XML utili per definire in maniera dichiarativa laspetto dellapplicazione ed i valori utilizzati al suo interno. Oltre a src, assets e res c infine la cartella gen, che contiene la speciale classe chiamata R. Invocando questa classe possibile richiamare via codice le risorse memorizzate sotto la directory res. Impareremo oggi stesso come farlo. Sappiate

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 2 Gestione delle risorse

comunque che la classe R viene generata automaticamente dal sistema e non deve mai essere modificata a mano. Le risorse in Android La speciale cartella res di un qualunque progetto Android ospita le risorse gestite dal sistema. Dentro di essa lo sviluppatore pu inserire una o pi delle seguenti sotto-directory: anim. Questa directory ospita i file XML che descrivono delle animazioni. drawable. Questa directory deve essere usata per le immagini necessarie al software. Sono supportati i formati PNG e JPEG. layout. Questa directory ospita i file XML che descrivono il layout delle interfacce utente. values. Questa directory ospita i file XML che descrivono stringhe e altri parametri utilizzati dallapplicazione. xml. Questa directory ospita file XML di tipo qualsiasi, che il sistema interpreta e rende semplici da acquisire. raw. Questa directory ospita file binari di tipo qualsiasi (ad esempio audio o filmati) che lapplicazione poi carica. Ciascuna di queste directory pu comparire, anche pi di una volta, accompagnata da un qualificatore. I principali qualificatori possibili sono: Lingua e regione. Questo qualificatore si formula servendosi del codice ISO a due caratteri di una lingua (it per italiano, en per inglese, fr per francese, de per tedesco ecc.). Opzionalmente si pu aggiungere anche la regione, aggiungendo alla sigla della lingua un underscore, una r ed il codice ISO a due caratteri della regione (it_rIT indica italiano e Italia, mentre it_rCH indica italiano e svizzera). Dimensioni dello schermo. Valori possibili per il qualificatore sono small (piccolo), normal (normale) e large (grande). Orientamento dello schermo. Valori possibili sono port (schermo pi alto che largo), land (schermo pi largo che alto) e square (schermo quadrato). Densit dei pixel dello schermo. Valori possibili sono ldpi (bassa densit, grosso modo intorno ai 120 dpi), mdpi (media densit, intorno ai 160 dpi), hdpi (alta densit, intorno ai 220 dpi) e nodpi (speciale qualificatore che ospita le immagini che non devono essere scalate in base alla densit). Quando si vuole abbinare un qualificatore ad una cartella di risorse, ad esempio a drawable, sufficiente inserirne il valore in coda al nome della cartella, al seguente modo: drawable-small Pi qualificatori, purch di gruppi diversi, possono essere associati ad una sola cartella: drawable-small-land-mdpi-it Ciascuna cartella pu comparire pi di una volta, con qualificatori diversi. Ad esempio lecito avere sotto res tutte le seguenti cartelle insieme: values
Pagina 2

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 2 Gestione delle risorse

values-it values-fr values-en Quando si richiama una risorsa (tra poco vedremo come si fa), Android sceglie automaticamente da quale directory andarla a pescare, cercando in base al tipo della risorsa e ai qualificatori abbinati alla cartella che la contiene. In questa maniera diventa molto semplice gestire ladattamento delle risorse in base alla lingua del dispositivo (internazionalizzazione) o alle sue caratteristiche hardware (come le dimensioni dello schermo). Gestione dei valori Il primo tipo di risorse che impareremo a manipolare sono i valori. Si tratta di coppie chiave-valore dichiarate allinterno dei file XML che sono al percorso di progetto res/values. Eclipse, per default, crea a questo percorso il file strings.xml, pensato per raccogliere le stringhe usate dallapplicazione che sar sviluppata. Ad ogni modo potete rinominare il file o aggiungerne quanti altri ne volete, al fine di categorizzare al meglio i valori necessari alla vostra applicazione. Limportante che tutti i file presenti nella cartella values seguano il modello:
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- valori qui --> </resources>

Allinterno del tag <resources> </resources> possibile dichiarare differenti tipi di valori. Supponiamo di voler dichiarare un valore di tipo stringa chiamato nome e con contenuto Carlo:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="nome">Carlo</string> </resources>

Ci sono numerosi tipi di dati supportati. Ecco un elenco completo: Stringhe Si usa il tag <string> e servono per definire i messaggi testuali che saranno usati allinterno dellapplicazione. Colori Si usa il tag <color> con valori espressi in forma esadecimale secondo i modelli #RRGGBB o #AARRGGBB (AA sta per il canale alpha, che regola la trasparenza del colore). Ad esempio: <color name="rosso">#FF0000</color> Misure e dimensioni Si usa il tag <dimen> e con valori numerici decimali accompagnati da ununit di misura che pu essere px (pixel), in (pollici), mm (millimetri), pt (punti a risoluzione 72dpi), dp (punti di densit) e sp (punti di densit e scala). Ad esempio: <dimen name="lato">180px</dimen>

Pagina 3

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 2 Gestione delle risorse

Rettangoli di colore Si usa il tag <drawable>. I valori possibili sono colori esadecimali come nel caso del tag <color>. Ad esempio: <drawable name="verde">#00FF00</drawable> Array di interi Si usa il tag <integer-array>. Gli elementi dellarray vanno espressi con pi occorrenze del tag annidato <item>. Ad esempio: <integer-array name="numeriPrimi"> <item>2</item><item>3</item> <item>5</item><item>7</item> </integer-array> Array di stringhe Si usa il tag <string-array>. Anche in questo caso si usa il tag <item>. Ad esempio: <string-array name="nomi"> <item>Carlo</item> <item>Claudia</item> <item>Nami</item> </string-array> Stili e temi Si usa il tag <style>. Servono per creare degli stili di disegno ed impaginazione, un po come fanno i fogli di stile CSS nel caso di HTML. Dentro il tag <style> vanno inseriti dei tag <item> con le singole voci che compongono lo stile. Gli stili possono essere applicati alle interfacce grafiche. Ad esempio: <style name="titolo"> <item name="android:textSize">18sp</item> <item name="android:textColor">#000088</item> </style> Usando Eclipse, comunque, non c bisogno di imparare lelenco a memoria: qualsiasi file XML posto sotto la directory res/values viene automaticamente lavorato con un apposito editor.

Pagina 4

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 2 Gestione delle risorse

Figura 2 - L'editor di Eclipse per i file XML nella directory values.

In un file di valori si pu inserire qualunque mistura degli elementi presentati sopra. La prassi, tuttavia, suggerisce di ripartire i differenti tipi di valore nei seguenti file: arrays.xml, per gli array. colors.xml, per i colori. dimens.xml, per le dimensioni. strings.xml, per le stringhe. styles.xml, per gli stili.

Richiamare le risorse da XML Come scritto in apertura, la modernit di Android pu essere evinta proprio dalla sua maniera di gestire le risorse. Le piattaforme di una volta non concedevano sistemi agevolati, finendo cos per favorire laccoppiamento fra codice e dati. Tuttora non raro vedere dei sorgenti in Java, in C o in qualsiasi altro linguaggio, con valori e messaggi digitati direttamente dentro il codice. Questa pratica non corretta ed sconsigliata da ogni manuale: sempre meglio separare i dati dal codice, perch in questa maniera il software pi facile sia da realizzare sia da mantenere. Android intende favorire la pratica del disaccoppiamento fra dati e codice, e lo fa attraverso gli strumenti che si stanno prendendo in considerazione in questa sede. I valori dichiarati nei file XML sotto values, cos come tutte le altre risorse della cartella res e delle sue annidate, sono trattati dal sistema in maniera speciale. Il kit di sviluppo, infatti, fornisce delle agevolazioni per richiamare le risorse dalle varie parti del software. Sostanzialmente unapplicazione Android costituita da file dichiarativi XML e da classi Java. Sia in un caso sia nellaltro, ci sono scorciatoie per richiamare le risorse incluse in res. Cominciamo dal caso XML e prendiamo a riferimento il pi importante dei file di questo tipo: AndroidManifest.xml. Quando, al suo interno, si dichiarano i dettagli dellapplicazione, possibile scrivere qualcosa come:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mypackage" android:versionCode="1" android:versionName="1.0"> <application android:label="LaMiaApplicazione"> ... </application> </manifest>

Il nome dellapplicazione, cio LaMiaApplicazione, stato in questo caso digitato direttamente dentro il codice XML. Con Android questo corretto, tuttavia si pu fare di meglio. Si pu includere il titolo dellapplicazione nel file res/values/strings.xml, alla seguente maniera:
<?xml version="1.0" encoding="utf-8"?>

Pagina 5

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 2 Gestione delle risorse
<resources> <string name="app_name">LaMiaApplicazione</string> </resources>

A questo punto il descrittore dellapplicazione pu essere riscritto come segue:


<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mypackage" android:versionCode="1" android:versionName="1.0"> <application android:label="@string/app_name"> ... </application> </manifest>

Anzich scrivere LaMiaApplicazione, si usato il riferimento @string/app_name. Questa scorciatoia, come intuibile, viene sostituita dalla risorsa di tipo stringa con nome app_name, che nel file strings.xml abbiamo dichiarato essere proprio LaMiaApplicazione. La regola generale per richiamare una risorsa in un file XML, quindi, basata sul modello: @tipo/nome I tipi validi sono: @array, per gli array. @color, per i colori. @dimen, per le dimensioni. @drawable, per i valori drawable, ma anche per le immagini messe in res/drawable. @layout, per richiamare i layout presenti nella cartella res/layout. @raw, per i file nella cartella res/raw (cfr. box laterale). @string, per le stringhe. @style, per gli stili.

Con @drawable, in particolar modo, possibile riferire sia i valori dichiarati con i tag <drawable> in res/values, sia le immagini conservate nella cartella res/drawable (o nelle sue varianti). Ad esempio, se in res/drawable viene messa unicona chiamata icon.png, dar possibile richiamarla con la formula @drawable/icon. Ad esempio lo si pu fare in AndroidManifest.xml, per associare licona allapplicazione:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mypackage" android:versionCode="1" android:versionName="1.0"> <application android:label="@string/app_name" android:icon="@drawable/icon"> ... </application> </manifest>

Richiamare le risorse da Java Valori e risorse possono essere richiamati da codice Java servendosi della classe android.content.res.Resources. Stando allinterno di una attivit, cio di una classe che estende android.app.Activity, sufficiente richiamare il metodo getResources() per ottenere il punto daccesso alle risorse dellapplicazione: Resources res = getResources();
Pagina 6

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 2 Gestione delle risorse

Una volta ottenuto loggetto, possibile invocare su di esso la seguente serie di metodi: public int getColor(int id) Restituisce il colore avente lidentificativo di risorsa specificato. public float getDimension(int id) Restituisce la dimensione avente lidentificativo di risorsa specificato. public Drawable getDrawable(int id) Restituisce loggetto disegnabile avente lidentificativo di risorsa specificato. public int[] getIntArray(int id) Restituisce larray di interi avente lidentificativo di risorsa specificato. public String getString(int id) Restituisce la stringa avente lidentificativo di risorsa specificato. public String[] getStringArray(int id) Restituisce larray di stringhe avente lidentificativo di risorsa specificato.

Tutti questi metodi agganciano la risorsa desiderata attraverso un identificativo numerico (int id). Ma come fare a conoscere gli ID associati alle risorse e ai valori inseriti nella cartella res? Lo si pu fare passando per la speciale classe autogenerata R. Al suo interno sono contenute delle sottoclassi statiche, una per ciascuna tipologia di risorsa presente nel progetto: R.string, R.drawable, R.color e cos via. In ciascuno di questi gruppi vengono introdotti gli ID numerici che corrispondono alle risorse e ai valori conservati in res e nelle sue sotto-cartelle. Proviamo con un esempio pratico: facciamo il caso che nel file res/values/strings.xml sia stata dichiarata la stringa app_name. Per richiamarla da codice Java, stando allinterno di una attivit, si deve fare alla seguente maniera: Resources res = getResources(); String appName = res.getString(R.string.app_name); Ciao Mondo Reloaded Mettiamo a frutto le nozioni acquisite in questa lezione assemblando per la seconda volta un esempio del tipo Ciao, Mondo!. Questa volta, per, useremo la pi corretta pratica delle risorse esterne per il nome dellapplicazione e per il messaggio presentato sullo schermo. Andiamo a modificare il progetto CiaoMondoAndroide realizzato nel corso della lezione precedente. Inseriamo il seguente contenuto al percorso di progetto res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Ciao Mondo</string> <string name="message">Ciao, Mondo Androide!</string> </resources>

Sono state dichiarate due stringhe: app_name con valore Ciao Mondo e message con valore Ciao, Mondo Androide!.
Pagina 7

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 2 Gestione delle risorse

Andiamo ora su AndroidManifest.xml:


<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mieapplicazioni.helloandroid" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".CiaoMondoAndroideActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

Il nome dellapplicazione, citato ben due volte allinterno del file, stato richiamato mediante il riferimento @string/app_name. Allo stesso modo licona per lapplicazione, creata automaticamente da Eclipse al percorso res/drawable/icon.png, stata riferita attraverso la dicitura @drawable/icon. Adesso tocca alla classe CiaoMondoAndroideActivity, il cui codice riportato di seguito:
package mieapplicazioni.helloandroid; import import import import android.app.Activity; android.content.res.Resources; android.os.Bundle; android.widget.TextView;

public class CiaoMondoAndroideActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Resources res = getResources(); String message = res.getString(R.string.message); TextView tv = new TextView(this); tv.setText(message); setContentView(tv); } }

Il messaggio Ciao, Mondo Androide!, questa volta, stato caricato attraverso la classe Resources ed il riferimento R.string.message.

Differenza tra res e assets La differenza tra le cartelle res e assets poco evidente, eppure c. La directory res pensata per gestire le risorse in maniera struttura, ed infatti suddivisa in sottocartelle. Tutte le risorse posizionate in res vengono prese in esame dal sistema di build e riferite nella speciale classe R. Quelle dentro res, dunque, sono delle risorse gestite. Sotto assets, invece, possibile depositare qualsiasi file si desideri senza che il sistema di build esegua unanalisi preventiva e crei il riferimento in R. Le risorse esterne conservate nella directory assets possono essere caricate servendosi della classe android.content.res.AssetManager. Nella maggior parte dei casi, comunque, non c bisogno di ricorrere alla cartella assets, poich res offre una maniera semplificata e completa per laccesso alle risorse.

Pagina 8

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android A cura di Carlo Pelliccia

Lezione 3

Le attivit
Le applicazioni Android, come si accennato durante la prima lezione, si compongono di quattro mattoni fondamentali: le attivit (activity), i servizi (service), i broadcast receiver ed i content provider. Ogni applicazione formata da uno o pi di questi mattoni. Non detto che li contenga tutti: ad esempio potrebbe essere costituita da due attivit e da un servizio, senza avere n broadcast receiver n content provider. Nella stragrande maggioranza dei casi, comunque, le applicazioni comprendono almeno unattivit. Le attivit, di conseguenza, sono il pi fondamentale dei componenti di base delle applicazioni Android. Cos unattivit Stando alla documentazione ufficiale, unattivit una singola e precisa cosa che lutente pu fare. Proviamo ad indagare le implicazioni di questa affermazione. Partiamo dal fatto che lutente, per fare qualcosa, deve interagire con il dispositivo. Domandiamoci come avvenga, nel caso di uno smartphone, linterazione tra luomo e la macchina. Un ruolo essenziale, naturalmente, svolto dai meccanismi di input come la tastiera ed il touchscreen, che permettono allutente di specificare il proprio volere. Le periferiche di input, tuttavia, da sole non bastano. Affinch lutente sappia cosa pu fare e come debba farlo, ma anche affinch il software possa mostrare allutente il risultato elaborato, necessario un canale aggiuntivo. Nella maggior parte dei casi questo canale il display. Nella superficie dello schermo il software disegna tutti quegli oggetti con cui lutente pu interagire (bottoni, men, campi di testo), e sempre sullo schermo viene presentato il risultato dellelaborazione richiesta. Il ragionamento ci porta alla conclusione che, per fare qualcosa con il dispositivo, necessario usare lo schermo. Esiste perci un parallelo tra il concetto di attivit, in Android, e quello di finestra, in un sistema desktop, bench non siano esattamente la stessa cosa. In generale, ad ogni modo, possiamo assumere con tranquillit che le attivit sono quei componenti di unapplicazione Android che fanno uso del display e che interagiscono con lutente. Dal punto di vista del programmatore, poi, possiamo spingerci oltre e semplificare ulteriormente. In maniera pi pragmatica, unattivit una classe che estende android.app.Activity. Lautore del codice, realizzando lattivit, si serve dei metodi ereditati da Activity per controllare cosa appare nel display, per assorbire gli input dellutente, per intercettare i cambi di stato e per interagire con il sistema sottostante. Ciclo di vita di unattivit In un sistema desktop il monitor sufficientemente spazioso da poter mostrare pi finestre simultaneamente. Perci non affatto raro lavorare con pi programmi contemporaneamente attivi, le cui finestre vengono affiancate o sovrapposte. Gli smartphone, invece, funzionano diversamente. Prima di tutto il display piccolo, e pertanto ha poco senso affiancare due o pi finestre di applicazioni differenti. Poi non bisogna dimenticare che le risorse di calcolo sono modeste, e perci non buona cosa tenere simultaneamente in vita troppi programmi. Per questi motivi le attivit di Android hanno carattere di esclusivit. possibile mandare in esecuzione pi attivit simultaneamente, ma soltanto unattivit alla volta pu occupare il display. Lattivit che occupa il display in esecuzione ed interagisce direttamente con lutente. Le altre, invece, sono ibernate e tenute nascoste in sottofondo, in modo da ridurre al minimo il consumo delle risorse di calcolo. Lutente, naturalmente, pu ripristinare unattivit ibernata e riprenderla da dove laveva interrotta, riportandola in primo piano. Lattivit dalla quale si sta allontanando, invece, sar ibernata e mandata in sottofondo al posto di quella ripristinata. Il cambio di attivit pu anche avvenire a causa di un evento esterno. Il caso pi ricorrente quello della telefonata in arrivo: se il telefono squilla mentre si sta usando la calcolatrice, questultima sar automaticamente ibernata e mandata in sottofondo. Lutente, conclusa la chiamata, potr richiamare lattivit interrotta e riportarla in vita, riprendendo i calcoli esattamente da dove li aveva interrotti.

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 3 Le attivit

Siccome le attivit ibernate, in termini di risorse di calcolo, non consumano nulla, in Android il concetto di chiusura delle attivit secondario e tenuto nascosto allutente. Ci, di solito, spiazza chi al suo primo confronto con la programmazione dei dispositivi portatili. Le attivit di Android non dispongono di un bottone x, o di un tasto equivalente, con il quale possibile terminarle. Lutente, di conseguenza, non pu chiudere unattivit, ma pu solo mandarla in sottofondo. Questo, comunque, non significa che le attivit non muoiano mai, anzi! Per prima cosa le attivit possono morire spontaneamente, perch hanno terminato i loro compiti. Insomma, anche se il sistema non ci fornisce automaticamente un bottone chiudi, possiamo sempre includerlo noi nelle nostre applicazioni. In alternativa, la distruzione delle attivit completamente demandata al sistema. I casi in cui unattivit pu terminare sono due: Lattivit ibernata ed il sistema, arbitrariamente, decide che non pi utile e perci la distrugge. Il sistema a corto di memoria, e per recuperare spazio inizia ad uccidere bruscamente le attivit in sottofondo.

Esistono poi dei task manager di terze parti che permettono di uccidere le attivit in sottofondo, ma non sono previsti nel sistema di base. I differenti passaggi di stato di unattivit attraversano alcuni metodi della classe Activity che, come programmatori, possiamo ridefinire per intercettare gli eventi di nostro interesse.

Figura 1 - Ciclo di vita di un'attivit. Pagina 2

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 3 Le attivit

La figura illustra la sequenza di chiamate ai metodi di Activity eseguite durante i passaggi di stato dellattivit. Entriamo nel dettaglio: protected void onCreate(android.os.Bundle savedInstanceState) Richiamato non appena lattivit viene creata. Largomento savedInstanceState serve per riportare un eventuale stato dellattivit salvato in precedenza da unaltra istanza che stata terminata. Largomento null nel caso in cui lattivit non abbia uno stato salvato. protected void onRestart() Richiamato per segnalare che lattivit sta venendo riavviata dopo essere stata precedentemente arrestata. protected void onStart() Richiamato per segnalare che lattivit sta per diventare visibile sullo schermo. protected void onResume() Richiamato per segnalare che lattivit sta per iniziare linterazione con lutente. protected void onPause() Richiamato per segnalare che lattivit non sta pi interagendo con lutente. protected void onStop() Richiamato per segnalare che lattivit non pi visibile sullo schermo. protected void onDestroy() Richiamato per segnalare che lapplicazione sta per essere terminata. La prassi richiede che, come prima riga di codice di ciascuno di questi metodi, si richiami limplementazione di base del metodo che si sta ridefinendo. Ad esempio:
@Override protected void onStart() { super.onStart(); // proprio codice a seguire }

importante non dimenticare questa regola, altrimenti le attivit sviluppate potrebbero non funzionare correttamente. Descrivere unattivit Dopo che si creata unattivit, la si deve registrare allinterno del descrittore dellapplicazione (il file AndroidManifest.xml) affinch il sistema sappia della sua esistenza. Per farlo si usa un tag <activity> allinterno del tag <application>:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package=" mypackage.mysubpackage " ... > <application ... >

<activity android:name=".MyActivity" ... > ... </activity>


... </application> </manifest>

Pagina 3

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 3 Le attivit

Con lattributo android:name si specifica il nome della classe registrata come attivit. Si pu esprimere sia il suo percorso completo (ad esempio mypackage.mysubpackage.MyActivity) sia il nome relativo rispetto al package dichiarato nel tag <manifest> sovrastante (ad esempio .MyActivity). Altri attributi possono opzionalmente essere inseriti nel tag <activity>, allo scopo di meglio dettagliare lattivit che si sta registrando. Tra le tante cose che si possono fare, una delle pi importanti quella di attribuire una label, cio unetichetta, allattivit. Si tratta, sostanzialmente, di un titolo che il sistema user per riferirsi allattivit e per presentarla allutente. Lattributo da utilizzare android:label. Come si spiegato nella lezione precedente, in casi come questo possibile sia scrivere la stringa direttamente nellXML sia fare riferimento ad una stringa memorizzata in un file strings.xml sotto la directory res/values. Nel primo caso, quindi, si user una formula del tipo:
<activity android:name=".MyActivity" android:label="La mia attivit">

Nel secondo caso, invece, si far alla seguente maniera:


<activity android:name=".MyActivity" android:label="@string/my_activity_label">

Un altro attributo spesso usato con il tag <activity> android:icon, che permette di specificare unicona per lattivit. In questo caso si usa sempre il riferimento ad una immagine presente nella cartella res/drawable, qualcosa come:
<activity android:name=".MyActivity" android:icon="@drawable/my_activity_icon">

Se non si specifica alcuna icona, lattivit eredita automaticamente licona definita nel sovrastante tag <application>. Allinterno della coppia di tag <activity> </activity>, invece, possono essere allacciate delle relazioni particolari fra lattivit e lapplicazione e fra lattivit ed il sistema. In particolar modo, possibile collegare allattivit un intent-filter:
<activity ... > <intent-filter> ... </intent-filter> </activity>

Nel dizionario di Android, un intent la descrizione di unoperazione che deve essere eseguita. Pi semplicemente, gli intent sono dei messaggi che il sistema manda ad unapplicazione quando si aspetta che questa faccia qualcosa. Di come funzionano gli intent e di cosa si compongono torneremo certamente a parlare in futuro. Per ora ci basta sapere che le attivit, attraverso un intentfilter, possono essere attivate in risposta ad uno specifico evento. Gli intent-filter accettano figli di tre tipi: <action android:name="nome-azione"> Individua gli intent che richiedono lazione specificata. <category android:name="nome-categoria"> Individua gli intent che appartengono alla categoria specificata.

Pagina 4

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 3 Le attivit

<data android:mimeType="nome-tipo-mime"> <data android:scheme="nome-schema-url"> Individua gli intent che portano dati del tipo specificato. Le azioni, le categorie ed i tipi di dato che possono essere usati sono, naturalmente, moltissimi. Per ora ci interessa sapere che unattivit dotata di un intent-filter che include lazione android.intent.action.MAIN e la categoria android.intent.category.LAUNCHER viene identificata come lattivit principale dellapplicazione. Ci significa che lapplicazione sar elencata nel men di sistema e che, quando lutente lavvier, sar lanciata proprio lattivit marcata in tale maniera. Ecco perch, in tutti gli esempi dei mesi precedenti, abbiamo sempre usato una formulazione del tipo:
<activity android:name=".MyActivity" ... > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>

Oltre allattivit principale, possiamo registrare in AndroidManifest.xml quante attivit vogliamo, da lanciare secondo necessit, come vedremo tra due paragrafi. ActivityDemo Fermiamoci un attimo con la teoria e mettiamo insieme in un esempio pratico i concetti appresi finora. Lo scopo dimostrare il ciclo di vita delle attivit, attraverso una activity che registri in un log tutti i suoi cambi di stato. Creiamo il progetto ActivityDemo, con package di riferimento mieapplicazioni.activitydemo, ed inseriamo al suo interno la seguente omonima attivit:
package mieapplicazioni.activitydemo; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class ActivityDemoActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i("ActivityDemo", "Richiamato onCreate() con bundle " + savedInstanceState); } @Override protected void onRestart() { super.onRestart(); Log.i("ActivityDemo", "Richiamato onRestart()"); } @Override protected void onStart() { super.onStart(); Log.i("ActivityDemo", "Richiamato onStart()"); } @Override protected void onResume() { super.onResume(); Log.i("ActivityDemo", "Richiamato onResume()"); } @Override protected void onPause() {

Pagina 5

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 3 Le attivit
super.onPause(); Log.i("ActivityDemo", "Richiamato onPause()"); } @Override protected void onStop() { super.onStop(); Log.i("ActivityDemo", "Richiamato onStop()"); } @Override protected void onDestroy() { super.onDestroy(); Log.i("ActivityDemo", "Richiamato onDestroy()"); } }

Questo codice, oltre a mostrarci la prassi corretta da applicare quando si ridefiniscono i metodi che intercettano i cambi di stato dellattivit, ci fa fare conoscenza con la classe android.util.Log. Come facile intuire, la classe contiene dei metodi statici per scrivere nel log di sistema. Il metodo i(), usato nellesempio, salva delle righe di log di livello INFO. Altri metodi disponibili sono v() per il livello VERBOSE, d() per il livello DEBUG, w() per il livello WARN, ed e() per il livello ERROR. Registriamo lattivit nel manifesto dellapplicazione, marcandola come attivit principale di lancio:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mieapplicazioni.activitydemo" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".ActivityDemoActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

Non dimentichiamo di includere un file res/values/strings.xml con le risorse riferite nel manifesto:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">ActivityDemo</string> </resources>

Lapplicazione ora pronta per essere avviata. Lanciate lemulatore e fate qualche prova. Mandate lattivit in secondo piano, magari azionando qualche altra applicazione fra quelle disponibili nel sistema. Quindi date unocchiata ai log emessi, per verificare il ciclo di vita dellattivit. Usando Eclipse i log possono essere consultati nella prospettiva di lavoro chiamata DDMS, nella scheda LogCat.

Figura 2 - Nella scheda LogCat possibile seguire i cambi di stato dell'applicazione. Pagina 6

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 3 Le attivit

Sotto-attivit Come spiegato in apertura, unapplicazione Android pu contenere pi di unattivit. In questo caso una soltanto sar marcata come attivit principale di lancio. Le altre saranno invece delle sottoattivit, che lattivit principale potr lanciare quando ce n bisogno. Realizzare una sotto-attivit semplice tanto quanto realizzare lattivit principale: ancora una volta sufficiente estendere android.app.Activity. Le attivit secondarie vanno poi registrate nel file AndroidManifest.xml, senza per applicare lintent-filter con lazione e la categoria usate invece dallattivit principale. Lattivit principale pu lanciare delle sotto-attivit ricorrendo al metodo startActivity(). Questo accetta come argomento un oggetto di tipo android.content.Intent che, come facile intuire, rappresenta un intent. Con questo intent bisogna esprimere quale sotto-attivit deve essere avviata. Immaginiamo di voler lanciare la sotto-attivit rappresentata dalla classe MySubActivity. Tutto quello che dovremo fare, dallinterno dellattivit principale, formulare unistruzione del tipo:
startActivity(new Intent(this, new MySubActivity));

La sotto-attivit verr lanciata e prender lo schermo al posto di quella principale. SubActivityDemo Mettiamo in pratica quanto descritto nel paragrafo precedente. Il progetto si chiama SubActivityDemo, ed il package di riferimento mieapplicazioni.subactivitydemo. Allinterno del package andremo ad inserire due attivit: MainActivity e SubActivity. La prima, come il suo nome lascia intendere, sar poi registrata come attivit principale e conterr un tasto che permetter il lancio della seconda. SubActivity conterr invece un tasto per poter essere chiusa e favorire cos il ritorno allattivit principale. Cominciamo con MainActivity:
package mieapplicazioni.subactivitydemo; import import import import import android.app.Activity; android.content.Intent; android.os.Bundle; android.view.View; android.widget.Button;

public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button button = new Button(this); button.setText("Lancia SubActivity"); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startSubActivity(); } }); setContentView(button); } private void startSubActivity() { Intent intent = new Intent(this, SubActivity.class); startActivity(intent); } }

Quanto spiegato nel paragrafo precedente stato direttamente messo in pratica allinterno del metodo startSubActivity(). Andiamo quindi a studiare il codice di SubActivity:

Pagina 7

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 3 Le attivit
package mieapplicazioni.subactivitydemo; import import import import android.app.Activity; android.os.Bundle; android.view.View; android.widget.Button;

public class SubActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button button = new Button(this); button.setText("Termina SubActivity"); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); setContentView(button); } }

Da questo codice apprendiamo una nozione nuova: unattivit, sia principale sia secondaria, pu terminare e chiudersi spontaneamente invocando il proprio metodo finish(). Registriamo adesso le due attivit nel descrittore dellapplicazione:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mieapplicazioni.subactivitydemo" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/main_activity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SubActivity" android:label="@string/sub_activity" /> </application> </manifest>

Per completare il lavoro abbiamo bisogno di un file res/values/strings.xml:


<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">SubActivityDemo</string> <string name="main_activity">MainActivity</string> <string name="sub_activity">SubActivity</string> </resources>

Abbiamo completato. Non resta che eseguire lesempio e provare il lancio e la chiusura della sottoattivit.

Pagina 8

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android A cura di Carlo Pelliccia

Lezione 4

Widget e Layout (Java)


Dopo aver scoperto cosa sono e come funzionano le attivit, studiamo ora come si costruiscono e si gestiscono le interfacce utente in Android. Attraverso un percorso che durer alcune lezioni, apprenderemo i principi e le pratiche utili per costruire oggetti grafici in grado di interagire con chi impugna il device. Si tratta di un passaggio cruciale del percorso di apprendimento: tutti i dispositivi mobili di nuova generazione puntano tantissimo sullinterazione con lutente. Unapplicazione, per essere cool, deve essere chiara e di bellaspetto. Android fornisce numerosi strumenti per raggiungere questo obbiettivo: scopriamoli ed impariamo ad usarli. View e ViewGroup I primi due concetti da assorbire si chiamano View e ViewGroup, e corrispondono alla maniera di Android di classificare ed organizzare ci che sullo schermo. I bottoni, i campi di testo, le icone e tutti gli altri congegni di uninterfaccia grafica sono oggetti View. I ViewGroup, invece, sono dei contenitori che possono mettere insieme pi oggetti View. I ViewGroup, inoltre, sono a loro volta degli oggetti View, e di conseguenza un possono contenere altri ViewGroup. Grazie a questa intuizione possibile organizzare i componenti sullo schermo secondo uno schema ad albero, come quello in figura.

Figura 1 - La maniera di Android di organizzare i componenti sullo schermo, attraverso componenti View e ViewGroup.

I componenti View estendono tutti la classe base android.view.View. Nella libreria standard di Android ci sono gi molti componenti di questo tipo, soprattutto nel pacchetto android.widget. Oltre ai widget di base, ad ogni modo, sempre possibile estendere la classe View e realizzare i propri componenti custom. Il pi delle volte non c bisogno di farlo, poich quelli forniti da Android bastano per tutte le principali necessit. comunque importante che sia data questa possibilit. La classe android.view.ViewGroup una speciale estensione di View. Come accennato in precedenza, e come rappresentato in figura, un ViewGroup una speciale View che pu contenere altre View. Per questo motivo gli oggetti ViewGroup dispongono di diverse implementazioni del metodo addView(), che permette proprio di aggiungere una nuova View al gruppo: public void addView(View child) Aggiunge un oggetto View al gruppo. public void addView(View child, int index) Aggiunge un oggetto View al gruppo, specificandone la posizione attraverso un indice (index).

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)

public void addView(View child, int width, int height) Aggiunge un oggetto View al gruppo, specificandone larghezza (width) ed altezza (height). public void addView(View child, ViewGroup.LayoutParams params) Aggiunge un oggetto View al gruppo, applicando una serie di parametri di visualizzazione ed organizzazione del componente (params). public void addView(View child, int index, ViewGroup.LayoutParams params) Aggiunge un oggetto View al gruppo, specificandone la posizione attraverso un indice (index) ed applicando una serie di parametri di visualizzazione ed organizzazione del componente (params). ViewGroup una classe astratta. Pertanto non pu essere istanziata direttamente. Come nel caso di View, possibile realizzare il proprio ViewGroup custom, ma il pi delle volte conviene scegliere fra le tante implementazioni messe a disposizione dalla libreria Android. Queste implementazioni differiscono nella maniera di presentare i componenti che sono al loro interno: alcuni li mettono uno dopo laltro, altri li organizzano in una griglia, altri ancora possono essere usati per avere una gestione a schede dello schermo, e cos via. Ovviamente conosceremo presto tutte le principali implementazioni di ViewGroup. Una volta che, combinando oggetti View e ViewGroup, si ottenuta linterfaccia utente che si desidera, necessario che questa venga mostrata sullo schermo. Come abbiamo scoperto mediante alcuni esempi preliminari, le attivit (cio gli oggetti android.app.Activity) mettono a disposizione un metodo setContentView(), disponibile nelle seguenti forme: public void setContentView(View view) Mostra sullo schermo loggetto View specificato. public void setContentView(View view, ViewGroup.LayoutParams params) Mostra sullo schermo loggetto View specificato, applicando una serie di parametri di visualizzazione ed organizzazione del componente (params). Widget Con il termine widget (congegno) si indicano quei componenti di base per linterazione con lutente, come i bottoni, le check box, le liste, i campi di testo e cos via. I widget predefiniti di Android estendono tutti (direttamente o indirettamente) la classe View, e sono conservati nel package android.widget. Esaminiamone alcuni in una veloce panoramica: android.widget.TextView Permette di mostrare del testo allutente. Il messaggio da visualizzare pu essere impostato con il metodo setText(), che pu accettare come parametro sia una stringa sia un riferimento a risorsa preso dal gruppo R.string.

Figura 2 - L'aspetto di un componente TextView.

Pagina 2

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)

android.widget.EditText Estende TextView e permette allutente di modificare il testo mostrato. Il testo digitato pu essere recuperato con il metodo getText(), che restituisce un oggetto del tipo android.text.Editable. Gli oggetti Editable sono simili alle stringhe, ed infatti implementano linterfaccia java.lang.CharSequence.

Figura 3 - L'aspetto di un componente EditText.

android.widget.Button Realizza un bottone che lutente pu premere o cliccare. Il componente espande TextView, e per questo possibile impostare il testo mostrato al suo interno con il metodo setText(), sia con parametro stringa sia con riferimento a risorsa del gruppo R.string.

Figura 4 - L'aspetto di un componente Button.

android.widget.ImageView Un componente utile per mostrare unimmagine, ad esempio unicona. Metodi utili per impostare limmagine mostrata sono: setImageBitmap(), che accetta un oggetto di tipo android.graphics. Bitmap; setImageDrawable(), che accetta un argomento android.graphics.drawable.Drawable; setImageResource(), che accetta un riferimento a risorsa drawable, tipicamente dal gruppo R.drawable.

Figura 5 - L'aspetto di un componente ImageView.

Pagina 3

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)

android.widget.ImageButton Un bottone con unimmagine. Estende ImageView, e quindi espone gli stessi metodi di questultima per impostare limmagine mostrata.

Figura 6 - L'aspetto di un componente ImageButton.

android.widget.CheckBox Questo componente realizza una casella di spunta (check box, appunto). Estende Button e TextView, pertanto il testo a fianco della casella pu essere impostato con i metodi setText() gi noti.

Figura 7 - L'aspetto di un componente CheckBox.

android.widget.RadioButton Questo componente realizza un bottone radio. Come nel caso di CheckBox, le classi base Button e TextView forniscono i metodi necessari per limpostazione del testo visualizzato. Un bottone radio, da solo, non ha senso. Due o pi bottoni radio, pertanto, possono essere raggruppati allinterno di un android.widget.RadioGroup. Lutente, cos, potr attivare soltanto una delle opzioni del gruppo.

Figura 8 - L'aspetto di tre componenti RadioButton raccolti in un contenitore RadioGroup.

android.widget.ToggleButton Un bottone ad interruttore, che pu essere cio on o off. Pu essere usato per far attivare o disattivare delle opzioni.

Figura 9 - L'aspetto di un componente ToggleButton.

Pagina 4

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)

android.widget.DatePicker Un componente che permette di scegliere una data selezionando giorno, mese ed anno. La data impostata dallutente pu essere recuperata servendosi dei metodi getDayOfMonth(), getMonth() e getYear().

Figura 10 - L'aspetto di un componente DatePicker.

android.widget.TimePicker Un componente che permette di scegliere un orario selezionando ora e minuto. Lorario impostato dallutente pu essere recuperato servendosi dei metodi getCurrentHour() e getCurrentMinute().

Figura 11 - L'aspetto di un componente TimePicker.

android.widget.AnalogClock Un componente che mostra allutente un orologio analogico.

Figura 12 - L'aspetto di un componente AnalogClock.

android.widget.DigitalClock Un componente che mostra allutente un orologio digitale.

Figura 13 - L'aspetto di un componente DigitalClock.

Tutti gli oggetti discussi finora richiedono, nei loro costruttori, un oggetto che estenda la classe astratta android.content.Context. Si tratta di una struttura che permette laccesso al sistema e che costituisce il contesto di esecuzione dellapplicazione. Non dovete preoccuparvi di come ottenere
Pagina 5

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)

oggetti di questo tipo: android.app.Activity estende indirettamente Context, per cui dallinterno di unattivit, vi sar sufficiente usare la parola chiave this. Ad esempio:
Button button = new Button(this);

La considerazione vale per le attivit, ma anche per tanti altri contesti della programmazione Android: pi o meno tutte le classi che sono mattoni fondamentali del sistema estendono direttamente o indirettamente la classe astratta android.content.Context. Layout Con il termine layout (disposizione, impaginazione), in Android, si identificano tutti quei ViewGroup utilizzabili per posizionare i widget sullo schermo. Android fornisce una serie di layout predefiniti. Esaminiamone alcuni. android.widget.FrameLayout Il pi semplice e basilare dei layout: accetta un widget, lo allinea in alto a sinistra e lo estende per tutta la dimensione disponibile al layout stesso. Ecco un semplice esempio di utilizzo, che allarga un bottone allintera area a disposizione di unattivit:
Button button = new Button(this); button.setText("Bottone"); FrameLayout layout = new FrameLayout(this); layout.addView(button); setContentView(layout);

Figura 14 - Un bottone, inserito in un FrameLayout, viene espanso fino alla dimensione massima a disposizione del layout che, nel caso specifico, corrisponde allintero schermo.

android.widget.RelativeLayout A differenza di FrameLayout, disegna un componente aggiunto al suo interno nelle sue dimensioni ideali, senza allargarlo per ricoprire lintera area a disposizione. Per default, il componente aggiunto viene allineato in alto a sinistra, ma possibile controllare lallineamento servendosi del metodo setGravity(). Questo accetta un argomento di tipo int, che bene scegliere fra le costanti messe a disposizione nella classe android.view.Gravity. I valori possibili, nel caso di RelativeLayout, sono i seguenti: Gravity.TOP allinea il widget in alto. Gravity.BOTTOM allinea il widget in basso.
Pagina 6

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)

Gravity.LEFT allinea il widget a sinistra. Gravity.RIGHT allinea il widget a destra. Gravity.CENTER_HORIZONTAL allinea il widget al centro orizzontalmente. Gravity.CENTER_VERTICAL allinea il widget al centro verticalmente. Gravity.CENTER allinea il widget al centro sia orizzontalmente sia verticalmente.

Pi costanti Gravity, purch non in contrasto fra di loro, possono essere concatenate in un solo valore servendosi delloperatore binario OR (che in Java si rende con il simbolo |, detto pipe). Ad esempio per allineare in basso a destra si scrive:
Gravity.BOTTOM | Gravity.RIGHT

Ecco un campione di codice che dimostra luso di un RelativeLayout allinterno di unattivit:


Button button = new Button(this); button.setText("Bottone"); RelativeLayout layout = new RelativeLayout(this); layout.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); layout.addView(button); setContentView(layout);

Figura 15 - Un RelativeLayout utilizzato per disporre un bottone, nelle sue dimensioni ideali, in alto al centro dello spazio disponibile.

android.widget.LinearLayout Un layout utile per disporre pi componenti uno di seguito allaltro, sia orizzontalmente sia verticalmente. Una volta creato il layout, il suo orientamento pu essere stabilito chiamando il metodo setOrientation(), con argomento pari a LinearLayout.HORIZONTAL o LinearLayout. VERTICAL. Con lorientamento orizzontale i componenti verranno messi tutta sulla stessa riga, uno dopo laltro. Con lallineamento verticale, invece, si procede lungo una colonna, e quindi i widget saranno uno sopra laltro. Esaminiamo il caso dellallineamento orizzontale. In questo caso i componenti vengono introdotti lungo una sola linea. Il sistema accetta di aggiungere componenti finch c spazio. Se si va di poco oltre la dimensione della riga, il sistema tenta un aggiustamento restringendo i componenti al di sotto delle loro dimensioni ideali. Raggiunto un certo limite, comunque, il sistema si rifiuta di andare oltre, ed i componenti di troppo non saranno pi visualizzati. Il metodo setGravity(), nellallineamento orizzontale, pu essere usato per decidere dove posizionare e come organizzare la riga dei componenti rispetto allo spazio disponibile. Ecco un esempio:
Button button1 = new Button(this); button1.setText("Bottone 1"); Button button2 = new Button(this); button2.setText("Bottone 2"); Button button3 = new Button(this); button3.setText("Bottone 3"); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.HORIZONTAL); layout.setGravity(Gravity.CENTER_HORIZONTAL); layout.addView(button1); layout.addView(button2); layout.addView(button3);

Pagina 7

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)
setContentView(layout);

Figura 16 - Un LinearLayout che allinea tre bottoni in orizzontale, al centro.

Nei LinearLayout verticali i componenti vengono aggiunti uno sopra allaltro, ed espansi in orizzontale fino ad occupare tutto lo spazio a disposizione del layout. In questo caso setGravity() pu essere usato per decidere se allineare la colonna in alto, in basso o al centro. Il sistema aggiunge componenti finch c spazio nella colonna. Superato il limite, i componenti di troppo non vengono visualizzati. Ecco un esempio:
Button button1 = new Button(this); button1.setText("Bottone 1"); Button button2 = new Button(this); button2.setText("Bottone 2"); Button button3 = new Button(this); button3.setText("Bottone 3"); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); layout.setGravity(Gravity.TOP); layout.addView(button1); layout.addView(button2); layout.addView(button3); setContentView(layout);

Figura 17 - Un LinearLayout che allinea tre bottoni in verticale, in alto.

La classe android.widget.RadioGroup, presentata sopra ed utile per mettere insieme pi RadioButton, estende LinearLayout e gode pertanto di tutte le propriet appena mostrate.

android.widget.TableLayout Un layout che permette di sistemare i componenti secondo uno schema a tabella, suddiviso cio in righe e colonne. I TableLayout vanno costruiti aggiungendo al loro interno degli oggetti TableRow, ciascuno dei quali forma una riga della tabella. Ogni riga suddivisa in colonne. In ciascuna cella pu essere inserito un componente. La gravit, cio il metodo setGravity(), pu essere usato sia su TableLayout che su TableRow, per stabilire gli allineamenti relativi. Ecco un semplice esempio di tre righe e tre colonne:
int rows = 3; int columns = 3; TableLayout layout = new TableLayout(this); layout.setGravity(Gravity.CENTER); for (int i = 0; i < rows; i++) { TableRow tableRow = new TableRow(this); tableRow.setGravity(Gravity.CENTER);

Pagina 8

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)
for (int j = 0; j < columns; j++) { Button button = new Button(this); button.setText("Bottone " + ((columns * i) + j + 1)); tableRow.addView(button); } layout.addView(tableRow); } setContentView(layout);

Figura 18 - Un TableLayout con nove bottoni disposti su tre righe e tre colonne.

Si faccia attenzione al fatto che, se la tabella eccede le dimensioni a disposizione, una parte di essa non sar visibile. Su come Android ripartisca la dimensione da assegnare a ciascuna colonna, si pu agire con i seguenti metodi: public void setColumnCollapsed(int columnIndex, boolean isCollapsed) Stabilisce se una colonna collapsed. Quando una colonna collapsed, non viene mostrata sullo schermo. public void setColumnShrinkable(int columnIndex, boolean isShrinkable) Stabilisce se una colonna shrinkable. Quando una colonna shrinkable, il sistema cerca di restringerla il pi possibile, per fare in modo che occupi poco spazio. public void setColumnStretchable(int columnIndex, boolean isStretchable) Stabilisce se una colonna stretchable. Quando una colonna stretchable, il sistema tende ad allargarla fornendogli lo spazio extra di cui dispone. Si faccia attenzione al fatto che gli indici delle colonne, in Android, partono da 0. TableLayout dispone inoltre di alcuni metodi di comodo che permettono, in un sol colpo, di applicare le medesime impostazioni di shrink o di stretch a tutte le colonne. Questi metodi sono setShrinkAllColumns() e setStretchAllColumns(). Mettere insieme widget e layout I widget ed i layout illustrati sinora, naturalmente, devono essere combinati in maniera coerente. I layout, in maniera particolare, possono e devono essere annidati luno dentro laltro, finch non si ottiene il design desiderato. Proviamo insieme con un esempio semplice ed efficace. Facciamo il caso che dobbiamo realizzare, in unattivit, una maschera di input attraverso la quale lutente pu specificare il proprio nome, il proprio cognome ed il proprio sesso. Al termine delloperazione, lutente pu salvare i dati con un tasto Salva o annullarli con il bottone Annulla. Ecco il codice necessario per realizzare quanto teorizzato:
TextView label1 = new TextView(this); label1.setText("Nome:"); EditText edit1 = new EditText(this); TextView label2 = new TextView(this); label2.setText("Cognome:");

Pagina 9

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)
EditText edit2 = new EditText(this); TextView label3 = new TextView(this); label3.setText("Sesso:"); RadioButton radio1 = new RadioButton(this); radio1.setText("M"); RadioButton radio2 = new RadioButton(this); radio2.setText("F"); RadioGroup radioGroup1 = new RadioGroup(this); radioGroup1.setOrientation(LinearLayout.HORIZONTAL); radioGroup1.setGravity(Gravity.CENTER); radioGroup1.addView(radio1); radioGroup1.addView(radio2); Button button1 = new Button(this); button1.setText("Salva"); Button button2 = new Button(this); button2.setText("Annulla"); TableRow row1 = new TableRow(this); row1.setGravity(Gravity.CENTER); row1.addView(label1); edit1.setGravity(Gravity.FILL); row1.addView(edit1); TableRow row2 = new TableRow(this); row2.setGravity(Gravity.CENTER); row2.addView(label2); row2.addView(edit2); TableRow row3 = new TableRow(this); row3.setGravity(Gravity.CENTER); row3.addView(label3); row3.addView(radioGroup1); TableLayout tableLayout = new TableLayout(this); tableLayout.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); tableLayout.addView(row1); tableLayout.addView(row2); tableLayout.addView(row3); tableLayout.setColumnShrinkable(0, true); tableLayout.setColumnStretchable(1, true); LinearLayout linearLayout1 = new LinearLayout(this); linearLayout1.setGravity(Gravity.CENTER); linearLayout1.setOrientation(LinearLayout.HORIZONTAL); linearLayout1.addView(button1); linearLayout1.addView(button2); LinearLayout linearLayout2 = new LinearLayout(this); linearLayout2.setGravity(Gravity.CENTER); linearLayout2.setOrientation(LinearLayout.VERTICAL); linearLayout2.setPadding(5, 5, 5, 5); linearLayout2.addView(tableLayout); linearLayout2.addView(linearLayout1); setContentView(linearLayout2);

Figura 19 - Una maschera di immissione dati realizzata combinando pi widget e pi layout.

Si sono adoperati dei widget TextView, per le etichette, EditText, per i campi ad immissione libera, RadioButton (con RadioGroup), per la selezione tra un elenco di opzioni, e Button, per i bottoni di azione finali. I componenti sono stati disposti sullo schermo annidando diversi tipi di layout. I campi del modulo sono stati messi insieme servendosi di un TableLayout, che dispone le etichette sulla colonna di sinistra ed i widget manipolabili su quella di destra. La pulsantiera con i bottoni Salva e Annulla stata invece realizzata servendosi di un LinearLayout orizzontale, che affianca e centra i due widget. I due layout, infine, sono stati messi insieme servendosi di un terzo
Pagina 10

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 4 Widget e Layout (Java)

contenitore, nello specifico un LinearLayout verticale, che ha disposto la tabella in alto e la pulsantiera sotto di questa. Tutto, infine, stato centrato sullo schermo.

Dimensioni dei widget I widget sono in grado di calcolare automaticamente una propria dimensione ideale, che varia in base allo stile ed al contenuto applicati. La dimensione ideale di un bottone con una lunga scritta al suo interno, ad esempio, sar calcolata automaticamente pi ampia di un altro bottone con un testo breve. La dimensione calcolata dai widget definita dimensione ideale. In ogni caso, non detto che il contenitore in cui il widget sar inserito rispetter la sua dimensione ideale. Alcuni tipi di layout, ad esempio, estendono i componenti al loro interno per occupare aree pi ampie, mentre altri li comprimono. Un certo grado di controllo, poi, lasciato al programmatore: diversi widget (ma non tutti) dispongono di metodi setWidth() e setHeight() con i quali possibile variare le loro dimensioni ideali. In certi casi possibile anche specificare delle dimensioni minime e delle dimensioni massime entro le quali i layout sono pregati di scegliere quelle pi adattate al componente. I metodi utili sono: setMinWidth(), setMinHeight(), setMaxWidth() e setMaxHeight().

Padding Quando si gestisce un contenitore possibile impostare su di esso un padding. Significa che ai componenti che saranno inseriti nel contenitore verr applicato un margine che li distanzier dai bordi del contenitore. La classe View, da cui discendono tutti i componenti compresi i contenitori, permette il controllo del padding attraverso il metodo setPadding(). Questo metodo richiede quattro argomenti di tipo int che definiscono la misura del margine in pixel da applicare, rispettivamente, dal bordo sinistro, da quello in alto, da quello a destra e da quello in basso.

Pagina 11

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android A cura di Carlo Pelliccia

Lezione 5

Widget e Layout (XML)


Con la precedente lezione abbiamo imparato a disporre sullo schermo i principali widget messi a disposizione da Android: bottoni, caselle di testo, check box e via discorrendo. La logica che permea la loro creazione ed il loro utilizzo, come abbiamo potuto osservare, non si discosta di molto da quella adottata da altre librerie Java per le interfacce utente, come AWT e Swing. Questo modo di creare le GUI potente, ma anche estremamente tedioso. Ogni volta che si deve utilizzare un widget, lo si deve creare, personalizzare ed inserire in un contenitore predisposto in precedenza. Per ogni componente e per ogni contenitore ci vogliono diverse righe di codice, anche nei casi pi semplici. La programmazione di interfacce complesse, di conseguenza, poco intuitiva e piuttosto noiosa. Sin dalle origini delle interfacce basate sui widget, i creatori delle piattaforme di sviluppo hanno cercato di porre rimedio a questo difetto. Nella maggior parte dei casi si fatto ricorso ad editor visuali: il programmatore, anzich scrivere codice, trascina i componenti sulleditor, dimensionandoli ad occhio ed impostandone le caratteristiche salienti mediante delle procedure guidate. Il lavoro sporco lo fa leditor in sottofondo, generando ed interpretando il codice di programmazione necessario. Questo approccio valido, ma da solo non costituisce una vera e propria soluzione del problema. Il codice prolisso e difficile da gestire, infatti, ancora l: lambiente di sviluppo non ha fatto altro che nascondere la sporcizia sotto il tappeto. Gli editor visuali, poi, sono molto difficili da realizzare, perch devono interpretare e generare del codice complesso, ed infatti ne esistono davvero pochi di buona qualit. Il codice generato automaticamente, infine, spesso un obbrobrio. Lambiente, infatti, non ha lintelligenza sufficiente per scrivere e mantenere un codice leggibile e performante. Apriamo ora una parentesi, per ricollegarci allargomento pi tardi. Un tempo si riteneva che la programmazione Web fosse, in qualche misura, meno pregiata della programmazione di applicazioni native. Molti puristi hanno creduto (ed alcuni credono ancora oggi) che fare software per il Web significhi lavorare con piattaforme inferiori e di scarsa qualit, ed hanno guardato con sdegno strumenti quali HTML, XML e JavaScript. I fatti, invece, hanno poi dimostrato il contrario. Oggi lo sviluppo delle applicazioni Web complesso tanto quanto quello delle applicazioni desktop, se non addirittura di pi. Con lavvento dei browser moderni, di AJAX e degli interpreti di nuova concezione, si sono portate sul Web molte applicazioni che, fino a ieri, erano appannaggio esclusivo degli ambienti desktop e dei linguaggi compilati. I client di posta elettronica, ad esempio, stanno scomparendo a favore delle webmail. Il proliferare delle applicazioni Web sta facendo maturare velocemente gli strumenti di sviluppo propri di questo ambito. Allinizio la programmazione Web sottraeva idee alla programmazione delle applicazioni native, emulandone approcci e strumenti; ora, invece, si assiste ad uninversione di tendenza. La programmazione Web, ad esempio, ha dimostrato quanto sia pi facile gestire uninterfaccia utente descrivendone i componenti con un linguaggio a marcatori, anzich con un linguaggio di programmazione. I linguaggi a marcatori come HTML ed XML ben si prestano a questo genere di operazioni: sono pi facili da leggere e da scrivere, sia per luomo sia per la macchina (cio per gli editor visuali). Cos oggi le piattaforme moderne applicano alla programmazione di applicazioni native il medesimo principio, fornendo agli sviluppatori framework ed editor basati perlopi su XML. Uno dei primi tentativi in tal senso ad aver avuto successo stato XUL, nato in casa Mozilla ed impiegato per le GUI di Firefox e di altre applicazioni della fondazione, poi importato anche in altri ambiti ed ambienti (ad esempio ne esiste unimplementazione per Swing). Torniamo ora ad Android, ed ancora una volta constatiamo come esso sia un sistema operativo di moderna concezione. Come abbiamo imparato nella lezione precedente, le GUI possono essere realizzate con un approccio classico, basato sul codice di programmazione. Spesso e volentieri, per, pi semplice ricorrere allapproccio moderno, basato su XML, che Android rende disponibile nativamente.

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

Layout XML Veniamo al dunque. Avrete sicuramente notato che la struttura predefinita di un progetto Android creato in Eclipse contiene sempre la directory res/layout. Abbiamo gi conosciuto alcune fra le sottodirectory di res e, in tutti i casi, abbiamo osservato come la piattaforma di sviluppo gestisse in maniera speciale le differenti categorie di risorse possibili. La cartella layout non fa eccezione. Al suo interno possono essere disposti dei file XML che il sistema interpreter come descrizioni dichiarative dei layout e dei widget che saranno poi usati in una o pi attivit dellapplicazione. Un esempio, in questo caso, vale pi di mille parole. La lezione precedente abbiamo chiuso con questo codice:
TextView label1 = new TextView(this); label1.setText("Nome:"); EditText edit1 = new EditText(this); TextView label2 = new TextView(this); label2.setText("Cognome:"); EditText edit2 = new EditText(this); TextView label3 = new TextView(this); label3.setText("Sesso:"); RadioButton radio1 = new RadioButton(this); radio1.setText("M"); RadioButton radio2 = new RadioButton(this); radio2.setText("F"); RadioGroup radioGroup1 = new RadioGroup(this); radioGroup1.setOrientation(LinearLayout.HORIZONTAL); radioGroup1.setGravity(Gravity.CENTER); radioGroup1.addView(radio1); radioGroup1.addView(radio2); Button button1 = new Button(this); button1.setText("Salva"); Button button2 = new Button(this); button2.setText("Annulla"); TableRow row1 = new TableRow(this); row1.setGravity(Gravity.CENTER); row1.addView(label1); edit1.setGravity(Gravity.FILL); row1.addView(edit1); TableRow row2 = new TableRow(this); row2.setGravity(Gravity.CENTER); row2.addView(label2); row2.addView(edit2); TableRow row3 = new TableRow(this); row3.setGravity(Gravity.CENTER); row3.addView(label3); row3.addView(radioGroup1); TableLayout tableLayout = new TableLayout(this); tableLayout.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); tableLayout.addView(row1); tableLayout.addView(row2); tableLayout.addView(row3); tableLayout.setColumnShrinkable(0, true); tableLayout.setColumnStretchable(1, true); LinearLayout linearLayout1 = new LinearLayout(this); linearLayout1.setGravity(Gravity.CENTER); linearLayout1.setOrientation(LinearLayout.HORIZONTAL); linearLayout1.addView(button1); linearLayout1.addView(button2); LinearLayout linearLayout2 = new LinearLayout(this); linearLayout2.setGravity(Gravity.CENTER); linearLayout2.setOrientation(LinearLayout.VERTICAL); linearLayout2.setPadding(5, 5, 5, 5); linearLayout2.addView(tableLayout); linearLayout2.addView(linearLayout1); setContentView(linearLayout2);

Si tratta del codice necessario per assemblare un modulo per linserimento dei dati anagrafici di una persona (nome, cognome, sesso). stato un buon espediente per mostrare lutilizzo combinato di
Pagina 2

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

alcuni layout (LinearLayout, TableLayout) ed alcuni widget (TextView, EditText, Button, RadioButton).

Figura 1 - Un form che permette allutente linserimento dei suoi dati anagrafici di base.

Lesempio ha leffetto collaterale di dimostrare come sia poco naturale usare Java per assemblare uninterfaccia utente: il codice poco leggibile, i nomi delle variabili poco significativi, e bisogna concentrarsi molto per capire chi contiene cosa. il classico caso in cui il formato XML vantaggioso:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="5px" android:gravity="center" android:orientation="vertical" android:layout_gravity="center"> <TableLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="top|center_horizontal" android:shrinkColumns="0" android:stretchColumns="1"> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="fill_horizontal|center_vertical"> <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="Nome:" /> <EditText android:layout_width="fill_parent" android:layout_height="fill_parent" /> </TableRow> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="fill_horizontal|center_vertical"> <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="Cognome:" /> <EditText android:layout_width="fill_parent" android:layout_height="fill_parent" /> </TableRow> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="fill_horizontal|center_vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Sesso:" />

Pagina 3

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)
<RadioGroup android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="center"> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="M" /> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="F" /> </RadioGroup> </TableRow> </TableLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Salva" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Annulla" /> </LinearLayout> </LinearLayout>

Questo XML, dopo averci fatto locchio, risulta molto pi semplice da gestire del suo corrispettivo Java. Anzitutto, per quanto riguarda la composizione dei layout, lutilizzo dellindentatura permette di individuare immediatamente chi il contenitore e chi il contenuto. Gli attributi di XML, poi, sono molto pi semplici ed intuitivi, rispetto ai metodi del tipo setPropriet() di Java. Con gli attributi pi semplice impostare le propriet di ogni singolo componente, come il testo visualizzato, il padding, la gravit e cos via. Creare un editor visuale in grado di leggere e scrivere questo XML, inoltre, estremamente pi facile che realizzare un editor in grado di fare lo stesso con del codice Java. In Eclipse, insieme con il plug-in ADT per lo sviluppo dei progetti Android, ne avete gi installato uno. Provate a creare un file di layout in un progetto Android, sotto il percorso res/layout. Facendo doppio clic sul file lambiente lo aprir nel suo editor visuale.

Figura 2 - Leditor visuale compreso con ADT per la creazione guidata dei layout XML in ambiente Eclipse.

Pagina 4

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

Qui possibile aggiungere layout e widget semplicemente pescandoli dal men sulla sinistra e trascinandoli sulla schermata al centro, che rappresenta linterfaccia grafica cos come apparir nello smartphone. Selezionando un componente possibile accedere allelenco delle sue propriet, mostrate nella scheda Properties in basso. Da qui possibile manipolare i parametri del componente. Con un po di pratica si riesce a costruire velocemente qualsiasi tipo di interfaccia. comunque possibile lavorare a mano sul codice XML, attivando la linguetta in basso che riporta il nome del file aperto. Lambiente, anche in questo caso, fornisce alcune utili agevolazioni, come lauto-completamento dei tag, degli attributi e dei valori, con tanto di aiuto in linea, come mostrato in figura.

Figura 3 - Anche passando allediting manuale dei layout XML possibile usufruire di alcune agevolazioni in ambiente Eclipse ADT, come lauto-completamento e lhelp in linea per tag ed attributi.

Richiamare un layout XML Nella directory res/layout si possono memorizzare quanti file si desidera. Lambiente li compila e genera automaticamente un riferimento verso ciascuno di essi nella classe R, nel gruppo layout. Ad esempio il file res/layout/mioLayout.xml avr il suo riferimento in R.layout.mioLayout. Questo riferimento, passando al codice Java, pu essere utilizzato per invocare ed adoperare il layout realizzato. La classe Activity, ad esempio, dispone di una versione di setContentView() che accetta come argomento un riferimento ad un oggetto di tipo layout. Continuando con lesempio XML del paragrafo precedente (che va salvato al percorso res/layout/main.xml), realizziamo unattivit in grado di caricare e mostrare il layout realizzato:
package mieapplicazioni.test; import android.app.Activity; import android.os.Bundle; public class TestActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }

Regole generali Entriamo un po pi nel dettaglio del formato XML usato per descrivere un layout. Come facile intuire, i tag adoperabili al suo interno corrispondono ai nomi dei widget e dei layout manager
Pagina 5

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

descritti nella lezione precedente. In pratica al widget Java android.widget.TextView corrisponde il tag XML <TextView>, al layout manager android.widget.LinearLayout corrisponde <LinearLayout>, e cos via. Potete riprendere la lezione precedente e desumere da voi le corrispondenze fra classi e tag. invece importante sapere che il namespace da utilizzare : http://schemas.android.com/apk/res/android Normalmente lo si fa dichiarando il namespace nel primo tag utilizzato (quello di ordine superiore), abbinandogli lo shortname android. In breve, il modello da seguire il seguente:
<tag1 xmlns:android="http://schemas.android.com/apk/res/android" android:attr1="val1" android:attr2="val2"> <tag2 android:attr1="val1" android:attr2="val2" /> <tag2 android:attr1="val1" android:attr2="val2" /> </tag1>

L dove gli attributi sono utilizzati per immettere un valore libero, ad esempio un testo, una dimensione, un colore, unimmagine e cos via, possibile sfruttare i riferimenti ad altri materiali conservati nella gerarchia della cartella res. Quando stato trattato lo speciale file AndroidManifest.xml abbiamo imparato ad usare i riferimenti del tipo:
@tipo/nome

Ad esempio, per richiamare la stringa titoloApplicazione definita in un XML sotto res/values, possibile fare:
@string/titoloApplicazione

Bene, la stessa identica cosa pu essere fatta in un XML di layout. Ad esempio, si pu ricorrere a questa funzionalit quando si imposta il testo mostrato in un bottone. Invece di scrivere:
<Button android:text="Salva" />

Si pu scrivere:
<Button android:text="@string/etichettaBottoneSalva" />

A patto, ovviamente, di aver creato in res/values un file XML che definisca la risorsa stringa etichettaBottoneSalva, come ad esempio:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="etichettaBottoneSalva">Salva</string> </resources>

La regola vale non soltanto per le stringhe, ma per tutte le categorie di risorse. Ricapitoliamole: @array, per gli array. @color, per i colori. @dimen, per le dimensioni. @drawable, per i valori drawable, ma anche per le immagini messe in res/drawable. @layout, per richiamare altri layout.
Pagina 6

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

@raw, per i file nella cartella res/raw. @string, per le stringhe. @style, per gli stili.

Assegnare un ID ai componenti A ciascun componente dichiarato nellXML, sia esso un contenitore o un widget, possibile assegnare un identificativo, utile per rintracciare successivamente il componente. Per assegnare un identificativo si deve utilizzare lattributo id:
<Tag android:id="..." />

Per assegnare lID necessario seguire una particolare sintassi, basata sul seguente modello:
@+nomeGruppo/nomeId

Questa sintassi fa s che nella classe R venga introdotto, se non esiste gi, il gruppo nomeGruppo, e che al suo interno venga memorizzato il riferimento allID nomeId. Sar pertanto possibile ricorrere allID, nel codice Java, usando il riferimento:
R.nomeGruppo.nomeId

Facciamo il caso di questo bottone:


<Button android:id="@+idBottoni/salva" android:text="Salva" />

In Java sar possibile richiamarlo adoperando il riferimento:


R.idBottoni.salva

Le attivit dispongono del metodo findViewById(), che come il nome lascia presupporre, ricercano nel layout caricato un componente avente lID specificato come argomento. Il bottone pu a questo punto essere recuperato e manipolato (ad esempio per aggiungere un gestore di evento, come impareremo prossimamente):
Button button = (Button) findViewById(R.idBottoni.salva);

Attributi comuni Gli attributi applicabili ad un tag variano a seconda del componente cui fanno riferimento. Ad esempio il tag <Button> dispone dellattributo text con il quale possibile impostare il testo visualizzato allinterno del bottone, mentre il tag <LinearLayout> ha lattributo orientation che serve per stabilire la direzione in cui disporre i widget. In alcuni casi gli attributi sono obbligatori, mentre in altri sono opzionali. La maggior parte dei componenti, poi, dispone davvero di molti attributi. Per esplorare tutti gli attributi di ogni specifico widget, di conseguenza, meglio affidarsi alle procedure guidate di un editor visuale, come quello di Eclipse ADT descritto sopra. Gli stessi attributi, naturalmente, sono esaustivamente riportati anche nella documentazione ufficiale. Tutto ci per dire che, in generale, non ci inalbereremo nello studio di tutti gli attributi possibili. Ci concentreremo invece su quelli comuni o comunque pi interessanti. I primi due che esaminiamo sono sempre obbligatori, e si chiamano layout_width e layout_height. Servono per decretare come si relazioni loggetto rispetto alle dimensioni del suo contenitore. Due sono i valori possibili:

Pagina 7

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

wrap_content Rende il componente grande tanto quanto impongono i suoi sotto-componenti. In pratica tutti i sotto-componenti vengono dimensionati rispetto, possibilmente, alla loro dimensione ideale, e poi il contenitore che li contiene viene dimensionato di conseguenza. fill_parent Allarga il componente fino a fargli occupare tutto lo spazio a sua disposizione concessogli dal suo contenitore dordine superiore. Ad esempio:
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Salva" />

Questo bottone sar grande tanto quanto basta a mostrare la scritta Salva. Questaltro invece:
<Button android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="Salva" />

Sar allargato, sia in orizzontale sia in verticale, fino ad occupare tutto lo spazio messo a sua disposizione.

Figura 4 - Le quattro differenti maniere di dimensionare un componente rispetto al suo contenitore. Da sinistra a destra: wrap_content su layout_width e layout_height; wrap_content su layout_width e fill_parent su layout_height; fill_parent su layout_width e wrap_content su layout_height; fill_parent su layout_width e su layout_height.

Altri attributi condivisi da tutti i componenti sono quelli definiti nella classe View, ereditati poi da tutti i widget e tutti i layout. Fra questi si segnalano: minWidth e minHeight Permettono di specificare una dimensione minima per il componente. La misura deve essere espressa letteralmente, specificando lunit di misura, oppure riferendo una dimensione definita sotto res/values. paddingLeft, paddingTop, paddingRight, paddingBottom e padding

Pagina 8

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

Permettono di specificare il padding (margine interno) del componente. I primi quattro attributi permettono di distinguere la misura di padding assegnata in ogni direzione, mentre il quinto permette di assegnare a tutte e quattro le direzioni il medesimo padding con unistruzione sola. I valori espressi devono essere dotati di unit di misura, o in alternativa fare riferimento ad una dimensione nota in res/values. visibility Accetta tre possibili valori: 0 (visibile), 1 (invisibile), 2 (scomparso). Nel primo caso, che quello di default, il componente visibile. Nel secondo caso il componente non visibile, ma lo spazio spettante viene riservato e mantenuto vuoto. Nel terzo caso il componente invisibile e nessuno spazio gli viene assegnato durante il disegno del layout, proprio come non fosse mai esistito. La visibilit di un componente pu essere poi manipolata a runtime da codice, con il metodo setVisibility(). Esistono poi dei tag non comuni a tutti i widget, ma molto diffusi. Tra questi: width e height Permettono di stabilire una dimensione precisa del componente, attraverso una misura scritta letteralmente o riferita da res/values. maxWidth e maxHeight Permettono di stabilire una dimensione massima del componente, attraverso una misura scritta letteralmente o riferita da res/values. gravity Stabilisce la gravit applicata al componente. I valori possibili sono: top, bottom, left, right, center_vertical, center_horizontal, center, fill_horizontal, fill_vertical, fill, clip_vertical e clip_horizontal. Pi valori compatibili possono essere concatenati con un carattere di pipe. Ad esempio: gravity="top|center_horizontal". I tanti componenti che derivano da TextView (tra cui i diversi tipi di bottoni) hanno a loro disposizione: text Permette di impostare il testo visualizzato, attraverso un valore letterale o un riferimento a stringa memorizzata sotto res/values. Una particolare estensione di TextView EditText. Con EditText si realizza una casella di input, che permette allutente di immettere del testo. In questo caso possono tornare utili i seguenti attributi: hint Un suggerimento da visualizzare quando non c del testo visualizzato. password Un booleano (true o false) che indica se il campo contiene una password. In questo caso il testo viene camuffato in modo da non risultare leggibile ad un occhio indiscreto.

Pagina 9

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

numeric Rende il campo di tipo numerico. Si deve specificare uno o pi valori (separandoli con pipe) fra integer, signed e decimal. Il primo indica un valore intero, il secondo un valore numerico con segno, il terzo di un valore decimale. In questa maniera, in un campo EditText o derivato, possibile controllare linput dellutente, in modo che il valore inserito sia sempre della natura che ci si aspetta. digits Da usare se il campo numerico. Permette di specificare quali cifre possibile utilizzare. Ad esempio il valore 123 far s che lutente possa inserire nel campo sono le cifre 1, 2 e 3. Consultando la documentazione ufficiale si pu completare in autonomia la panoramica.

XML Se non siete pratici di XML e di linguaggi a marcatori correte il rischio di non condividere quanto ribadito in questa lezione, e di considerare pi semplice il design delle interfacce utente a mezzo di un linguaggio di programmazione come Java. Se la pensate cos solo perch ancora non siete riusciti a cogliere lessenza di XML e del suo utilizzo. Non dovete farvi spaventare: XML nato per essere semplice, e dunque anche facile. Ecco alcuni link per studiare cosa sia XML e come funzioni: http://it.wikipedia.org/wiki/XML http://xml.html.it/guide/leggi/58/guida-xml-di-base/ http://www.mrwebmaster.it/xml/guide/guida-xml_9/ http://www.risorse.net/xml/guida.asp

Namespace I namespace di XML, per semplificare, sono lequivalente dei package in Java. Chi inventa un nuovo gruppo di tag, per regola, deve inserirli in un namespace, in modo che possano essere utilizzati senza andare in collisione con eventuali altri tag omonimi di un altro autore. I package di Java hanno la forma aaa.bbb.ccc, mentre i namespace di XML sono un URL del tipo http://aaa/bbb/ccc. Bench questo sia un indirizzo Web a tutti gli effetti, non detto che debba per forza esserci un file da scaricare a quel percorso, anche se in genere lautore ci mette la definizione dei tag e degli attributi previsti dal namespace. A ciascuno dei namespace importati in un tag XML possibile associare uno shortname, cio un nome breve, utile per richiamare tag ed attributi del namespace senza ambiguit. Per approfondire i namespace e per qualche esempio pratico: http://xml.html.it/articoli/leggi/1648/il-misterioso-mondo-dei-namespaces/

Pagina 10

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 5 Widget e Layout (XML)

Attributi XML sulla documentazione Android eredita dalle piattaforme Java la maniera di organizzare la documentazione delle sue API. Lindirizzo http://developer.android.com/reference/ deve essere sempre tra i preferiti di ogni programmatore Android. Proprio come la documentazione javadoc di Java, la reference di Android riporta lelenco dei package e delle classi disponibili nel sistema. Per ciascuna classe sono riportate propriet e metodi. In pi, per tutte le classi che corrispondono a widget e layout manager richiamabili anche da XML, la documentazione riporta inoltre anche gli attributi previsti. Si veda, ad esempio, android.widget.TextView: http://developer.android.com/reference/android/widget/TextView.html

Pagina 11

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android A cura di Carlo Pelliccia

Lezione 6

Gestione degli eventi dei widget


Nelle due precedenti lezioni abbiamo conosciuto i principali widget di Android e le tecniche utili per richiamarli e disporli nel display dello smartphone. In questa lezione impareremo a raccogliere linput dellutente, e lo faremo intercettando gli eventi scatenati dai widget. I metodi di callback Tutti i widget di Android dispongono di una serie di metodi di callback, con nomi del tipo onTipoEvento(). Questi metodi vengono richiamati automaticamente ogni volta che il corrispondente evento riscontrato sul widget. Pensiamo ad un esempio concreto. La classe android.widget.Button, che abbiamo conosciuto nelle lezioni precedenti, definisce uno speciale metodo chiamato onTouchEvent(). Questo metodo eseguito automaticamente ogni volta che il bottone viene toccato dallutente (in gergo, tap), attraverso il touch screen del suo dispositivo. Sono molti i metodi di questa categoria, ed ogni widget ha i propri. Ciascun metodo ha le sue regole e la sua firma: parametri e valore di ritorno, insomma, cambiano di caso in caso. La documentazione ufficiale delle API di Android, come sempre, presenta lelenco esaustivo (in inglese) per ciascun widget. Lo sviluppatore interessato alla gestione di uno specifico evento deve individuare il metodo di suo interesse, quindi estendere il widget e ridefinire il metodo individuato. Proviamo a farlo proprio con il metodo onTouchEvent() di Button, rendendo il bottone reattivo al singolo tap, cio al clic da dito:
package mieapplicazioni.test; import import import import android.content.Context; android.view.MotionEvent; android.widget.Button; android.widget.Toast;

public class MyButton extends Button { public MyButton(Context context) { super(context); } @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { Toast toast = Toast.makeText( getContext(), "Bottone cliccato!", Toast.LENGTH_SHORT); toast.show(); return true; } return false; } }

Toast Toast la parola usata nel gergo dei dispositivi mobili per identificare le finestrelle pop-up con un avviso rivolto allutente. Si chiamano cos perch nelle loro implementazioni pi classiche spuntano fuori dalla parte bassa del display e somigliano ad un toast che salta fuori dal tostapane a cottura ultimata. In Android, come si evince dagli esempi mostrati nellarticolo, gli avvisi toast possono essere realizzati servendosi della classe android. widget.Toast. Ad ogni modo, ne parleremo nuovamente in seguito.

Il metodo onTouchEvent() riceve in ingresso un argomento di tipo android.view.MotionEvent, che riporta tutte le informazioni relative allevento di tocco riscontrato sul bottone. La versione superiore del metodo (cio quella definita in Button) viene richiamata con la riga:
super.onTouchEvent(event);

Questo, nel caso specifico, necessario affinch levento venga gestito graficamente: la definizione base del metodo contenuta in Button fa s che il bottone, quando sottoposto a pressione, cambi il suo

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget

aspetto ed il suo colore, per mostrare reattivit al tocco dellutente (il bottone cambia da grigio ad arancione). Dopo aver eseguito la versione base del metodo, la nostra versione ridefinita controlla i dettagli dellevento. Se si tratta del primo tocco riscontrato sul bottone (ACTION_DOWN), viene mostrato allutente un messaggio di notifica (Bottone cliccato!). Altri filtri possono essere applicati per intercettare ulteriori varianti dellevento di tocco, ad esempio ACTION_MOVE per un trascinamento e ACTION_UP per lazione di rilascio del pulsante (dito sollevato dal display). Il metodo onTouchEvent(), infine, richiede la restituzione di un valore booleano, che serve per indicare se levento stato effettivamente gestito oppure no. Limplementazione restituisce true nel caso di un evento ACTION_DOWN, effettivamente gestito attraverso il messaggio mostrato allutente, mentre restituisce false in tutti gli altri casi. La classe MyButton pu essere ora impiegata allinterno di unattivit, come abbiamo imparato a fare due lezioni fa:
package mieapplicazioni.test; import import import import android.app.Activity; android.os.Bundle; android.view.Gravity; android.widget.LinearLayout;

public class MyButtonDemoActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MyButton button = new MyButton(this); button.setText("Toccami!"); LinearLayout layout = new LinearLayout(this); layout.setGravity(Gravity.CENTER); layout.addView(button); setContentView(layout); } }

Figura 1 - In questo esempio levento di primo tocco sul bottone viene gestito mostrando un avviso allutente.

Event listener La ridefinizione dei metodi di callback una tecnica che funziona, ma non molto pratica: ogni volta che si usa un widget bisogna estenderlo e generare quindi unaltra classe, in modo da poter
Pagina 2

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget

ridefinire il metodo (o i metodi) di callback di proprio interesse. Questo, di per s, scomodo e prolisso. Quanti widget possono esserci in unapplicazione di media complessit? Fra bottoni, checkbox, etichette, campi di testo e via discorrendo, il numero sicuramente elevato. Ecco allora che la velocit di sviluppo viene frenata dal dover definire e catalogare tante classi quanti sono i widget che si vogliono utilizzare. Un vero incubo! La ridefinizione dei metodi di callback, pertanto, una pratica che serve solo in determinati casi, principalmente durante la creazione di componenti custom. Ad esempio possibile estendere View per creare un componente personalizzato. lecito, lo si pu fare, e probabilmente prima o poi vi servir di farlo. Per un intervento di questo genere, dunque, non c nulla di male nel ridefinire ed utilizzare i metodi di callback, anzi loperazione sarebbe sicuramente necessaria. Per tutti gli altri usi quotidiani dei widget pronti alluso, invece, Android mette a disposizione un meccanismo pi semplice e sbrigativo, basato sullutilizzo dei cosiddetti event listener. Tutti i widget mettono a disposizione una seconda serie di metodi, questa volta del tipo setOnTipoEventoListener(). Il widget Button, ad esempio, dispone del metodo setOnClickListener(). Attraverso i metodi di questa categoria possibile registrare al widget degli event listener, cio delle istanze di speciali classi, realizzate appositamente per ricevere notifica ogni volta che lo specifico evento accade. Per ciascun differente tipo di event listener esiste uninterfaccia apposita, che lo sviluppatore deve implementare per creare il suo gestore dellevento. Ad esempio, linterfaccia da implementare per gli eventi di clic android.view.View.OnClickListener (interfaccia innestata nella classe View). Ciascuna interfaccia, ovviamente, richiede limplementazione di uno o pi metodi. Nel caso di OnClickListener, per proseguire con lesempio, il metodo da ridefinire :
public void onClick(View v) { ... }

Proviamo a ripetere lesempio del paragrafo precedente, questa volta utilizzando il principio degli event listener e, pi nello specifico, linterfaccia OnClickListener:
package mieapplicazioni.test; import android.view.View; import android.view.View.OnClickListener; import android.widget.Toast; public class MyClickListener implements OnClickListener { @Override public void onClick(View view) { Toast toast = Toast.makeText( view.getContext(), "Bottone cliccato!", Toast.LENGTH_SHORT); toast.show(); } }

Questa volta, invece di estendere Button e realizzare cos un componente custom, abbiamo semplicemente implementato uninterfaccia. Nel metodo onClick() abbiamo scritto il codice necessario per gestire levento di clic. Il parametro ricevuto dal metodo, nel caso specifico, rappresenta loggetto View o derivato sul quale levento stato riscontrato. Affinch la classe MyClickListener venga utilizzata come gestore dellevento di clic su uno specifico widget, necessario registrarne unistanza sul widget stesso, servendosi del metodo setOnClickListener() citato in precedenza. Lo si pu fare quando si allestisce o si richiama il layout dalla schermata. Ecco una Activity equivalente a quella del paragrafo precedente, ma che a

Pagina 3

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget

differenza di questultima utilizza un widget Button standard insieme con levent listener di poco sopra:
package mieapplicazioni.test; import import import import import android.app.Activity; android.os.Bundle; android.view.Gravity; android.widget.Button; android.widget.LinearLayout;

public class OnClickListenerDemoActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button button = new Button(this); button.setText("Toccami!"); button.setOnClickListener(new MyClickListener()); LinearLayout layout = new LinearLayout(this); layout.setGravity(Gravity.CENTER); layout.addView(button); setContentView(layout); } }

In questa maniera non stato necessario creare un componente custom: stato sufficiente registrare sul Button levent listener realizzato pocanzi, con la riga:
button.setOnClickListener(new MyClickListener());

La tattica degli event listener, inoltre, si sposa meglio con la possibilit messa in campo da Android di definire risorse e layout attraverso dei file XML. Il layout realizzato nellattivit mostrato poco sopra, ad esempio, potrebbe essere definito in un file XML indipendente. Chiamiamolo main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center"> <Button android:id="@+id/bottone01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Toccami!" /> </LinearLayout>

Lattivit, di conseguenza, andrebbe riscritta alla seguente maniera:


package mieapplicazioni.test; import android.app.Activity; import android.os.Bundle; import android.widget.Button; public class OnClickListenerDemoActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button button = (Button) findViewById(R.id.bottone01); button.setOnClickListener(new MyClickListener()); }

Pagina 4

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget
}

Dopo aver richiamato il layout definito nel file XML, non si deve far altro che recuperare il bottone al quale si vuole collegare levento e registrare su di esso il proprio listener personalizzato. Come scrivere meno codice Qualcuno potrebbe obiettare che, con gli event listener, comunque necessario creare una classe distinta per ciascun gestore previsto, con il rischio di avere pi codice dedicato alla cattura degli eventi che non alla loro gestione. Esistono diversi trucchi applicabili con gli event listener che aiutano ad evitare le situazioni di questo tipo. Ne esamineremo un paio. Per realizzare un event listener bisogna estendere uninterfaccia. Java non supporta lereditariet multipla, e quindi una classe pu avere una sola super-classe. Questo limite per non vale nel caso delle interfacce: una classe ne pu implementare un numero qualsiasi. Ecco allora che, nel caso di GUI non troppo complesse, si pu fare che la Activity che controlla lo schermo sia essa stessa event listener di uno o pi eventi, per uno o pi widget. Vediamo un esempio pratico che applica tale tecnica. Prendiamo in considerazione il seguente layout, come al solito da battezzare main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center"> <Button android:id="@+id/bottone01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bottone 1" /> <Button android:id="@+id/bottone02" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bottone 2" /> </LinearLayout>

Lesempio lievemente pi complesso del precedente: qui i bottoni sono diventati due.

Figura 2 - Questa volta bisogna gestire i clic su due bottoni differenti.

Pagina 5

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget

Realizziamo unattivit che carichi questo layout e gestisca gli eventi di clic su ambo i bottoni:
package mieapplicazioni.test; import import import import import import android.app.Activity; android.os.Bundle; android.view.View; android.view.View.OnClickListener; android.widget.Button; android.widget.Toast;

public class TwoButtonsDemoActivity extends Activity implements OnClickListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button bottone01 = (Button) findViewById(R.id.bottone01); Button bottone02 = (Button) findViewById(R.id.bottone02); bottone01.setOnClickListener(this); bottone02.setOnClickListener(this); } @Override public void onClick(View v) { String messaggio; if (v.getId() == R.id.bottone01) { messaggio = "Hai cliccato il bottone 1!"; } else if (v.getId() == R.id.bottone02) { messaggio = "Hai cliccato il bottone 2!"; } else { messaggio = "widget non riconosciuto!"; } Toast toast = Toast.makeText(this, messaggio, Toast.LENGTH_SHORT); toast.show(); } }

In questo caso la stessa attivit ad implementare linterfaccia OnClickListener, definendo di conseguenza il metodo onClick(). Cos facendo, non stato necessario creare (e manutenere) una classe apposita. Inoltre levent listener realizzato stato adoperato su due bottoni differenti (bottone01 e bottone02). stato possibile farlo servendosi degli id assegnati ai due bottoni: allinterno del codice del solo metodo onClick() realizzato, si andato a mostrare un messaggio differente a seconda della sorgente dellevento. Unaltra tecnica che permette di risparmiare codice e lavoro consiste nelladoperare le classi innestate anonime di Java. possibile fare qualcosa di questo tipo:
button.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { // gestione evento } });

Di fatto si crea e si registra allo stesso tempo il gestore dellevento di clic. Ci pensa il compilatore a separare la classe anonima innestata su un file .class differente. Riscriviamo allora lesempio precedente secondo questaltra tecnica:
package mieapplicazioni.test; import android.app.Activity; import android.os.Bundle;

Pagina 6

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget
import import import import android.view.View; android.view.View.OnClickListener; android.widget.Button; android.widget.Toast;

public class TestActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button bottone01 = (Button) findViewById(R.id.bottone01); Button bottone02 = (Button) findViewById(R.id.bottone02); bottone01.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { clickSuBottone01(); } }); bottone02.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { clickSuBottone02(); } }); } private void clickSuBottone01() { Toast toast = Toast.makeText(this, "Hai cliccato il bottone 1!", Toast.LENGTH_SHORT); toast.show(); } private void clickSuBottone02() { Toast toast = Toast.makeText(this, "Hai cliccato il bottone 2!", Toast.LENGTH_SHORT); toast.show(); } }

Cos facendo non c bisogno di far implementare alcuna interfaccia allattivit. La tecnica consente di scrivere davvero poco codice per intercettare gli eventi, lasciando il programmatore libero di concentrarsi sulla logica della loro gestione. Il pi delle volte, proprio questultima la tecnica da preferirsi. Panoramica sugli eventi Ora che abbiamo imparato ad utilizzare gli event listener, la prima cosa da fare chiedersi: quanti e quali sono gli eventi che possibile gestire? La risposta non semplice: ogni widget ha i suoi eventi e, di conseguenza, i suoi event listener. Anche in questo caso, quindi, la documentazione una risorsa fondamentale, che bisogna assolutamente imparare a leggere. Soprattutto nel caso dei widget pi particolari, quindi, dovrete cavarvela da soli. Insieme, per, possiamo prendere in esame i tipi di eventi pi universali e diffusi, riconosciuti e gestibili su qualsiasi widget. A definirli, tanto per cambiare, la madre di tutti i widget: la classe android.view.View. Ecco una panoramica degli eventi pi importanti: Click. Lo abbiamo usato in tutti gli esempi precedenti. Il metodo sul widget setOnClickListener(), e linterfaccia per il gestore da implementare View.OnClickListener. Il metodo richiesto dallinterfaccia onClick(View view), che in parametro riporta il widget che ha subito levento.

Pagina 7

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget

Click lungo. Un evento che accade quando si clicca su un widget e si mantiene la pressione per qualche istante. Il metodo per registrare levent listener setOnLongClickListener(), e linterfaccia per il gestore View.OnLongClickListener. Il metodo da implementare onLongClick(View view). Il metodo, come nel caso precedente, riceve come parametro un riferimento al widget su cui si prodotto levento. In pi, il metodo deve restituire un booleano per segnalare se levento stato completamente gestito (true) oppure no (false). Tocco. Un evento pi generico dei due precedenti: serve per rilevare un tocco qualsiasi su un componente. Il metodo per registrare il listener sul widget setOnTouchListener(), mentre linterfaccia per implementarlo View.OnTouchListener. Linterfaccia richiede il metodo onTouch(View view, MotionEvent event). Come nei casi precedenti, view il widget che ha subito levento. Il parametro di tipo MotionEvent riporta invece i dettagli dellevento (tipo di azione, coordinate, durata ecc.), come nel caso documentato nel primo esempio di oggi. Il metodo deve restituire un booleano per segnalare se levento stato completamente gestito (true) oppure no (false). Digitazione. Un evento usato per segnalare la pressione o il rilascio di un tasto della tastiera hardware. Il metodo per registrare il listener sul widget setOnKeyListener(), mentre linterfaccia per implementarlo View.OnKeyListener. Linterfaccia richiede il metodo onKey(View view, int keyCode, KeyEvent event). Come nei casi precedenti, view il widget che ha subito levento. Il parametro keyCode riporta il codice associato al tasto premuto, mentre quello di tipo KeyEvent riporta ulteriori dettagli (tasto pigiato, tasto rilasciato, eventuali modificatori e cos via). Il metodo deve restituire un booleano per segnalare se levento stato completamente gestito (true) oppure no (false).

Modificatori Quando si riceve un evento di digitazione, attraverso listanza di KeyEvent, possibile sapere se insieme al tasto principale che riguarda levento sono stati attivati anche uno o pi tasti modificatori. I tasti modificatori, in Android, sono tre: ALT, SHIFT e SYM. KeyEvent permette di controllare lo stato dei tre modificatori attraverso i metodi booleani isAltPressed(), isShiftPressed() e isSymPressed().

Focus. Quando un widget riceve il focus, significa che selezionato, e che tutti gli eventi di digitazione saranno lui rivolti. possibile sapere quando un widget riceve o perde il focus. Il metodo per registrare il listener sul widget setOnFocusChangeListener(), mentre linterfaccia per implementarlo View.OnFocusChangeListener. Linterfaccia richiede il metodo onFocusChange(View view, boolean hasFocus). Il parametro view il widget che ha subito levento, mentre il booleano hasFocus indica se il componente ha ricevuto il focus (true) oppure se lo ha perso (false). Siccome di esempi dedicati ai clic ne abbiamo gi visti diversi, proviamo ora a gestire i due eventi di tocco e digitazione. Prepariamo il seguente layout, da chiamare come al solito main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center"> <EditText android:id="@+id/eventArea" android:layout_width="wrap_content"

Pagina 8

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget
android:layout_height="wrap_content" android:background="#990000" android:textColor="#FFFFFF" android:width="200px" android:height="200px" /> <TextView android:id="@+id/logArea" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Compi un evento sull'area rossa qui sopra" /> </LinearLayout>

Il layout dispone, al centro del display, due componenti. C un EditText con sfondo di colore rosso e dimensioni 200 x 200 pixel, che useremo per far compiere allutente delle operazioni di digitazione e di tocco. C poi un TextView, che useremo invece per descrivere ogni evento riscontrato sul componente precedente, dimostrando cos di averlo intercettato correttamente.

Figura 3 - Lapplicazione intercetta gli eventi di digitazione e tocco riscontrati sullarea rossa, e poi li descrive allutente che li ha compiuti.

Andiamo ora a realizzare, mediante una Activity, quanto proposto:


package mieapplicazioni.test; import import import import import import import import import android.app.Activity; android.os.Bundle; android.util.Log; android.view.KeyEvent; android.view.MotionEvent; android.view.View; android.view.View.OnKeyListener; android.view.View.OnTouchListener; android.widget.TextView;

public class TestActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); View eventArea = (View) findViewById(R.id.eventArea); // Tocco eventArea.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { String action; switch (event.getAction()) {

Pagina 9

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 6 Gestione degli eventi dei widget
case MotionEvent.ACTION_DOWN: action = "DOWN"; break; case MotionEvent.ACTION_MOVE: action = "MOVE"; break; case MotionEvent.ACTION_UP: action = "UP"; break; default: action = "OTHER"; break; } float x = event.getX(); float y = event.getY(); log("tocco: " + action + " su (" + x + "," + y + ")"); return true; } }); eventArea.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View view, int keyCode, KeyEvent event) { char c = event.getDisplayLabel(); String action; switch (event.getAction()) { case KeyEvent.ACTION_DOWN: action = "DOWN"; break; case KeyEvent.ACTION_UP: action = "UP"; break; default: action = "OTHER"; break; } log("Tasto: codice " + keyCode + " (" + c + "), azione " + action); return true; } }); } private void log(String text) { // Logga Log.i("EVENT", text); // Mostra su schermo. TextView logArea = (TextView) findViewById(R.id.logArea); logArea.setText(text); } }

SullEditText vengono registrati due listener, uno per il tocco e laltro per la digitazione. La tecnica usata per gli event listener quella della classe anonima innestata. Gli eventi intercettati vengono esaminati (attraverso le istanze di MotionEvent e KeyEvent) e descritte, sia nei log dellapplicazione (che potrete controllare comodamente usando Eclipse) sia nella TextView che ne d immediato riscontro allutente.

Figura 4 - Nella scheda LogCat della prospettiva DDMS di Eclipse possibile controllare i log emessi dallapplicazione, che descrivono la sequenza degli eventi riscontrati.

Pagina 10

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android A cura di Carlo Pelliccia

Lezione 7

Men
I men sono una parte importante di qualsiasi applicazione moderna. Da anni gli utenti sono abituati ad avere a che fare con il concetto di men, al quale si rivolgono ogni volta che vogliono cercare i comandi o modificare le opzioni delle loro applicazioni. Ci risulta vero tanto nellambito dei software desktop quanto in quello delle applicazioni mobili per cellulari e smartphone. Con questo articolo impareremo a conoscere i men previsti da Android, ponendo particolare attenzione alle regole per la costruzione di interfacce grafiche semplici ed efficaci, con men concisi e facili da raggiungere e navigare. I men in Android In Android esistono tre differenti tipi di men, che lo sviluppatore pu collegare ad una Activity: Options menu Sono i men concepiti per raggruppare le opzioni ed i comandi di unapplicazione. Si dividono in due sotto-gruppi, icon menu ed expanded menu, descritti di seguito. Icon menu Sono i men con le opzioni principali di unapplicazione. Vengono visualizzati nella parte bassa dello schermo quando si schiaccia il tasto men del dispositivo. Vengono chiamati icon menu perch gli elementi contenuti al loro interno, in genere, sono delle grosse icone che lutente pu selezionare con il polpastrello. Costituiscono il men principale di ogni attivit e dovrebbero contenere sempre e solo le opzioni principali. Questi men sono di rapido accesso, ma soffrono per questo di alcune limitazioni: possono contenere al massimo sei elementi, e non possibile inserire negli icon menu gli elementi avanzati come le caselle di spunta (checkbox) e i bottoni radio.

Figura 1 - Licon menu utilizzato dal browser di Android. Al suo interno sono presenti i comandi pi fondamentali dellapplicazione. Il tasto altro permette poi laccesso al men secondario.

Expanded menu Quando il primo tipo di men non sufficiente per esporre tutti i comandi e tutte le opzioni di unapplicazione, le attivit fanno ricorso agli expanded menu (letteralmente men espansi). Quando ci avviene il men principale, come suo ultimo tasto, presenta il bottone altro. Attivandolo si accede ad una lista aperta a tutto schermo, che permette la consultazione delle altre opzioni di men. Gli

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

expanded menu, quindi, possono essere considerati come dei men secondari, allinterno dei quali inserire le opzioni di configurazione del software, oppure i comandi che vengono utilizzati meno frequentemente.

Figura 2 - Lexpanded menu del browser di Android. Contiene i comandi secondari e laccesso alle opzioni di configurazione.

Context menu I men contestuali sono quelli che appaiono quando si mantiene il tocco per qualche istante su un widget che ne dotato. Ad esempio nel browser possibile eseguire un tocco di questo tipo sopra ad unimmagine. Dopo qualche istante verr aperto un men contestuale con alcune opzioni relative alla pagina corrente e allimmagine selezionata, come ad esempio i comandi per salvarla in locale e condividerla con gli amici. Come nel caso precedente, questo genere di men si presenta come una lista a tutto schermo, che pu contenere numerose opzioni.

Figura 3 - Il context menu del browser di Android visualizzato quando si tiene il tocco per qualche secondo sopra ad unimmagine contenuta in una pagina Web.

Submenu Le applicazioni che dispongono di molti comandi possono usufruire anche dei submenu. In pratica, in uno qualsiasi dei men descritti in precedenza, possibile inserire un elemento che, invece di compiere unazione diretta, va ad aprire un sotto-men, nel quale si possono
Pagina 2

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

presentare ulteriori possibilit di scelta. A differenza dei sistemi desktop, dove i men possono essere annidati a piacimento luno dentro laltro, in Android non possibile spingersi oltre il sotto-men di primo livello. In parole pi semplici: non possibile avere il sotto-men di un sotto-men. Questa scelta frutto della precisa volont dei designer di Android di mantenere semplici le applicazioni. In un display di pochi centimetri, se i men potessero avere profondit qualsiasi, lutente avrebbe difficolt ad orientarsi. La scelta degli ingegneri di Google, quindi, costringe gli sviluppatori a pensare le proprie applicazioni affinch qualsiasi loro funzionalit sia accessibile con al pi tre tocchi. Nei prossimi paragrafi impareremo a programmare tutti e tre i tipi di men presentati.

La regola dei tre click Una vecchia regola di usabilit dice che lutente deve sempre poter trovare quel che cerca con al massimo tre click. Se non ci riesce, lutente inizia a provare frustrazione. Questa regola stata inizialmente concepita per il Web ma, di fatto, oggi pu essere applicata anche alle applicazioni mobili e desktop dedicate allutente finale e ad un pubblico quanto pi ampio possibile. I men di Android, evidente, sono stati concepiti tenendo a mente la regola. Per approfondire: http://en.wikipedia.org/wiki/Three-click_rule

Options menu Cominciamo dagli options menu che, come abbiamo detto, costituiscono il men principale di qualsiasi applicazione. Il men delle opzioni un concetto strettamente legato a quello di singola attivit. Ogni Activity, infatti, pu avere un solo options menu. La classe Activity dispone di un metodo definito al seguente modo: public boolean onCreateOptionsMenu(Menu menu) Questo metodo, nel ciclo di vita dellattivit, viene richiamato automaticamente dal sistema la prima volta che lutente preme il tasto men del suo dispositivo. Largomento passato, un oggetto di tipo android.view.Menu, costituisce loptions menu inizialmente vuoto. Ridefinendo il metodo possibile intercettare queste chiamate e popolare cos il men fornito con le voci utili alla propria applicazione. Il metodo onCreateOptionsMenu(), al termine dei propri compiti, deve restituire un booleano: true per rendere attivo il men realizzato, false per dichiarare che lattivit non dispone di un men, e quindi alla pressione del tasto men del dispositivo non si deve mostrare nulla. Programmando nel corpo del metodo, il proprio options menu pu essere assemblato servendosi dei metodi messi a disposizione dagli oggetti di tipo android.view.Menu (per lesattezza, Menu uninterfaccia). Aggiungere un elemento al men molto semplice, basta servirsi dei metodi: public MenuItem add(CharSequence title) public MenuItem add(int titleRes) Ambo i metodi aggiungono un elemento al men. Il titolo dellelemento (cio la scritta che sar mostrata per qualificarlo) pu essere espressa con una stringa, nel primo caso, o con il riferimento ad una risorsa di tipo stringa memorizzata su file XML esterno, nel secondo caso.
Pagina 3

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

Un controllo pi granulare sugli elementi inseriti reso possibile da queste altre due varianti del metodo add(): public MenuItem add(int groupId, int itemId, int order, CharSequence title) public MenuItem add(int groupId, int itemId, int order, int titleRes) In questo caso, oltre al titolo, possibile specificare altre tre propriet di ciascun elemento del men: groupId Con questo valore possibile assegnare lelemento ad un gruppo. Si pu specificare un qualsiasi valore maggiore di zero (ovviamente deve essere lo stesso per due o pi elementi che si intende assegnare al medesimo gruppo), oppure si pu usare lo zero o la costante Menu.NONE se non si vuole assegnare lelemento ad un gruppo. I gruppi sono delle facilitazioni che possono essere utilizzate per compiere delle azioni cumulative su una serie di elementi (ad esempio attivare o disattivare insieme tutti gli elementi di un gruppo). Ne riparleremo pi tardi. itemId Con questo valore si assegna un identificativo allelemento stesso. Questo identificativo torna utile in seguito, quando si vuole distinguere un elemento da un altro. Come nel caso precedente, bisogna usare un valore maggiore di zero affinch la caratteristica possa essere sfruttata. Se non si interessati allassegnare un identificativo allelemento, sufficiente usare il valore zero o la costante Menu.NONE. order Se si vuole assegnare uno specifico ordine allelemento, possibile in questo caso specificarlo esplicitamente con un valore da uno in su. Usando lo zero o la costante Menu.NONE si lascia stabilire al sistema lordine dellelemento nel men di appartenenza.

Sperimentiamo subito quanto descritto finora. Realizziamo unattivit dimostrativa cos formulata:
package mieapplicazioni.test; import android.app.Activity; import android.view.Menu; public class MenuDemo01Activity extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.NONE, 1, 1, "Comando 1"); menu.add(Menu.NONE, 2, 2, "Comando 2"); menu.add(Menu.NONE, 3, 3, "Comando 3"); menu.add(Menu.NONE, 4, 4, "Comando 4"); menu.add(Menu.NONE, 5, 5, "Comando 5"); menu.add(Menu.NONE, 6, 6, "Comando 6"); return true; } }

Pagina 4

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

Abbiamo popolato il men con sei semplici comandi, etichettati rispettivamente Comando 1, Comando 2, Comando 3 e cos via. Installate lapplicazione su un dispositivo e provare il suo men. Dovreste ottenere un risultato simile a quello in figura.

Figura 4 - Un semplice icon menu, con sei comandi testuali.

Come possibile osservare, i sei elementi introdotti sono andati a costituire licon menu dellattivit. Provate ora la seguente variante:
package mieapplicazioni.test; import android.app.Activity; import android.view.Menu; public class MenuDemo02Activity extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.NONE, 1, 1, "Comando 1"); menu.add(Menu.NONE, 2, 2, "Comando 2"); menu.add(Menu.NONE, 3, 3, "Comando 3"); menu.add(Menu.NONE, 4, 4, "Comando 4"); menu.add(Menu.NONE, 5, 5, "Comando 5"); menu.add(Menu.NONE, 6, 6, "Comando 6"); menu.add(Menu.NONE, 7, 7, "Comando 7"); menu.add(Menu.NONE, 8, 8, "Comando 8"); menu.add(Menu.NONE, 9, 9, "Comando 9"); return true; } }

In questo caso i comandi sono nove. Lanciate lattivit e verificate cosa accade.

Pagina 5

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

Figura 5 - Sono stati aggiunti nove comandi al men. I primi cinque sono entrati a far parte dellicon men, mentre i restanti quattro sono stati posizionati nellexpanded menu dellattivit.

Mentre, nel caso precedente, tutti e sei i comandi previsti sono diventati parte dellicon menu dellattivit, ora le cose sono andate diversamente. Come accennato in apertura, un icon menu pu contenere al massimo sei elementi, come nel caso del primo esempio. Ora che gli elementi sono diventati nove, il sistema ha posizionato nellicon menu solo i primi cinque del lotto. Il sesto spazio disponibile nellicon menu stato automaticamente occupato con un elemento di tipo altro, che lavora come pulsante daccesso per lexpanded menu dellattivit. In questultimo sono stati inseriti i restanti quattro comandi (dal sesto al nono) previsti dalla nostra programmazione. Se ne ricava che non c una vera e propria differenza tra licon menu e lexpanded menu, ed ecco perch i due tipi di men sono stati presentati inizialmente come due aspetti delloptions menu. Se ne deduce inoltre una regola di usabilit da non dimenticare mai: quando si realizza unapplicazione, i suoi comandi pi frequentemente utilizzati vanno aggiunti per primi o comunque con un ordine superiore, in modo che possano entrare a far parte delloptions menu e restare cos ad un solo tocco di distanza dallutente. Gli elementi che confluiscono nellicon menu possono utilizzare delle icone al posto del testo. Usufruire di questa funzionalit molto semplice. Per prima cosa dovete fare caso al fatto che i metodi add() di Menu restituiscono un oggetto di tipo android.view.MenuItem. Come facile intuire, loggetto restituito rappresenta lelemento appena introdotto nel men. Questo genere di oggetti dispone di metodi che permettono il controllo dellelemento. Fra questi segnaliamo i seguenti: public MenuItem setIcon(Drawable icon) public MenuItem setIcon(int iconRes) Ambo i metodi servono per aggiungere unicona allelemento. Si pu usare un oggetto graphics.drawable.Drawable, caricato o realizzato in precedenza, oppure il riferimento ad unimmagine conservata nella directory res/drawable del progetto. Una volta impostata unicona su un elemento, questa sar mostrata soltanto se lelemento parte dellicon menu. Ecco un esempio che dimostra la funzionalit:
package mieapplicazioni.test; import android.app.Activity; import android.view.Menu; import android.view.MenuItem;

Pagina 6

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men
public class MenuDemo03Activity extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuItem comando1 = menu.add(Menu.NONE, 1, 1, "Comando 1"); comando1.setIcon(R.drawable.play); MenuItem comando2 = menu.add(Menu.NONE, 2, 2, "Comando 2"); comando2.setIcon(R.drawable.pause); MenuItem comando3 = menu.add(Menu.NONE, 3, 3, "Comando 3"); comando3.setIcon(R.drawable.stop); return true; } }

Affinch lesempio funzioni correttamente, necessario disporre delle immagini play, pause e stop nella directory res/drawable del progetto. Una volta fatta funzionare lattivit, il suo icon menu sar tipo quello mostrato in figura.

Figura 6 - Le icone degli elementi vengono mostrate nellicon menu.

Scorciatoie da tastiera Per velocizzare la selezione di una voce di men, possibile associargli una scorciatoia da tastiera. Affinch la scorciatoia possa essere realmente applicata, necessario che il dispositivo in uso disponga di una tastiera numerica o alfanumerica hardware. A seconda del tipo di tastiera installata, possibile stabilire una differente associazione. I metodi di MenuItem utili per far ci sono: public MenuItem setAlphabeticShortcut(char alphaChar) public MenuItem setNumericShortcut(char numericChar) public MenuItem setShortcut(char numericChar, char alphaChar) I tre metodi permettono di impostare, rispettivamente, il carattere di scorciatoia alfanumerico, il carattere di scorciatoia numerico, o entrambi in un colpo solo.

Pagina 7

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

Ora che abbiamo imparato a disporre gli elementi nelloptions menu, impariamo anche come gestire gli eventi di attivazione di ciascuno degli elementi introdotti. Esistono un paio di maniere per intercettare gli eventi di tocco riscontrati sugli elementi di un options menu. La prima tecnica consiste nel ridefinire un metodo di Activity cos definito: public boolean onOptionsItemSelected(MenuItem item) Questo metodo viene richiamato automaticamente ogni volta che uno degli elementi delloptions menu dellattivit viene selezionato dallutente. Lo specifico elemento selezionato, naturalmente, quello riportato in argomento, mentre il valore di ritorno serve per indicare al sistema se levento stato gestito (true) oppure no (false). Ridefinendo il metodo ed applicando dei filtri sullidentificativo dellelemento segnalato ( possibile recuperarlo con il metodo getId() di MenuItem) possibile riconoscere e gestire la specifica azione eseguita dallutente. Ecco un esempio che mostra un avviso differente in base alla voce di men selezionata:
package mieapplicazioni.test; import import import import android.app.Activity; android.view.Menu; android.view.MenuItem; android.widget.Toast;

public class MenuDemo04Activity extends Activity { private static final int MENUITEM_COMANDO_1 = 1; private static final int MENUITEM_COMANDO_2 = 2; private static final int MENUITEM_COMANDO_3 = 3; @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.NONE, MENUITEM_COMANDO_1, 1, "Comando 1"); menu.add(Menu.NONE, MENUITEM_COMANDO_2, 2, "Comando 2"); menu.add(Menu.NONE, MENUITEM_COMANDO_3, 3, "Comando 3"); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case MENUITEM_COMANDO_1: Toast.makeText(this, "Comando 1", Toast.LENGTH_SHORT).show(); return true; case MENUITEM_COMANDO_2: Toast.makeText(this, "Comando 2", Toast.LENGTH_SHORT).show(); return true; case MENUITEM_COMANDO_3: Toast.makeText(this, "Comando 3", Toast.LENGTH_SHORT).show(); return true; } return false; } }

Questo stralcio di codice mette in atto una tecnica consigliata: memorizzare gli identificativi degli elementi del men attraverso delle costanti. In questo modo gli ID risultano pi leggibili e si meno inclini a commettere errori di battitura o distrazione.

Pagina 8

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

Una seconda tecnica per la gestione degli eventi consiste nellutilizzare il seguente metodo di MenuItem: public MenuItem setOnMenuItemClickListener( MenuItem.OnMenuItemClickListener menuItemClickListener) La tecnica richiama le logiche di gestione degli eventi che abbiamo conosciuto nella lezione precedente. Linterfaccia MenuItem.OnMenuItemClickListener richiede limplementazione del metodo: public boolean onMenuItemClick(MenuItem item) Il metodo viene richiamato quando lelemento selezionato dallutente. Ecco un esempio analogo al precedente, ma basato su questa seconda tecnica di gestione degli eventi:
package mieapplicazioni.test; import import import import import android.app.Activity; android.view.Menu; android.view.MenuItem; android.view.MenuItem.OnMenuItemClickListener; android.widget.Toast;

public class MenuDemo05Activity extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuItem comando1 = menu.add("Comando 1"); comando1.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { Toast.makeText(MenuDemo05Activity.this, "Comando 1", Toast.LENGTH_SHORT).show(); return true; } }); MenuItem comando2 = menu.add("Comando 2"); comando2.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { Toast.makeText(MenuDemo05Activity.this, "Comando 2", Toast.LENGTH_SHORT).show(); return true; } }); MenuItem comando3 = menu.add("Comando 3"); comando3.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { Toast.makeText(MenuDemo05Activity.this, "Comando 3", Toast.LENGTH_SHORT).show(); return true; } }); return true; } }

Pagina 9

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

La classe Activity, infine, dispone di altri due metodi collegati alla gestione del suo options menu: public boolean onPrepareOptionsMenu(Menu menu) Viene richiamato quando loptions menu sta per essere visualizzato. Infatti il metodo onCreateOptionsMenu(), usato in tutti gli esempi precedenti, viene richiamato solo la prima volta che il men deve essere mostrato. Se si intende modificare il men costruito in precedenza, si pu ridefinire questo secondo metodo. Gli oggetti Menu, a tal proposito, dispongono di una serie di metodi che permettono di recuperare, modificare e rimuovere gli elementi introdotti al suo interno in precedenza. Anche se non un caso molto comune, quindi, Android permette lo sviluppo di options menu dinamici, che cambiano in base allo stato dellapplicazione. public void onOptionsMenuClosed(Menu menu) Viene richiamato quando loptions menu, dopo essere stato visualizzato, viene chiuso. public void openOptionsMenu() Apre il men automaticamente, senza che sia necessario premere il tasto men del dispositivo. Ridefinendo questi metodi potrete ottenere un controllo pi di dettaglio sugli eventi collegati allapertura e alla chiusura delloptions menu delle vostre attivit. Context menu I men contestuali permettono di associare particolari opzioni o comandi ai singoli widget di unattivit. La creazione e lutilizzo dei context menu sono molto simili a quelli delloptions menu. La prima cosa che si deve fare, dichiarare che uno o pi widget dellattivit devono disporre di un men contestuale. Lo si pu fare con il metodo registerForContextMenu(View view) di Activity. Ad esempio, per dotare un bottone di un men contestuale si deve fare cos:
Button button = new Button(this); // ... registerForContextMenu(button);

Se il widget stato dichiarato in un XML di layout, lo si pu caricare attraverso il suo identificativo:


View widget = findViewById(R.id.mioWidget); registerForContextMenu(widget);

Fatto ci, possibile ridefinire uno o pi metodi di Activity in modo da generare e gestire il men contestuale. Questi metodi sono: public void onCreateContextMenu( ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) Questo metodo viene richiamato ogni volta che il men contestuale per il widget v deve essere mostrato. Il men deve essere costruito aggiungendo elementi al parametro menu. Il terzo argomento, menuInfo, viene usato solo in alcuni casi, come ad esempio quando si ha a che fare con le liste.

Pagina 10

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

public boolean onContextItemSelected(MenuItem item) Richiamato quando un elemento di un context menu viene selezionato. public void onContextMenuClosed(Menu menu) Richiamato quando il men contestuale viene chiuso. Gli oggetti android.view.ContextMenu dispongono di tutti i metodi gi visti con gli oggetti Menu (ContextMenu, infatti, uninterfaccia che estende Menu). Fate per attenzione al fatto che gli elementi dei men contestuali non supportano n le icone n le scorciatoie da tastiera. In compenso gli oggetti ContextMenu si specializzano attraverso i seguenti metodi: public ContextMenu setHeaderTitle(CharSequence title) public ContextMenu setHeaderTitle(int titleRes) Associa un titolo al men contestuale, che sar mostrato nellintestazione del men. public ContextMenu setHeaderIcon(Drawable icon) public ContextMenu setHeaderIcon(int iconRes) Associa unicona al men contestuale, che sar mostrata nellintestazione del men. public ContextMenu setHeaderView(View view) Imposta loggetto view come intestazione del men, sostituendo licona ed il titolo dei metodi precedenti. public void clearHeader() Ripulisce lintestazione del men. Ecco un esempio di codice:
package mieapplicazioni.test; import import import import import import import import import import android.app.Activity; android.os.Bundle; android.view.ContextMenu; android.view.Menu; android.view.MenuItem; android.view.View; android.view.ContextMenu.ContextMenuInfo; android.widget.Button; android.widget.LinearLayout; android.widget.Toast;

public class MenuDemo06Activity extends Activity { private private private private static static static static final final final final int int int int MENUITEM_COMANDO_1 MENUITEM_COMANDO_2 MENUITEM_COMANDO_3 MENUITEM_COMANDO_4 = = = = 1; 2; 3; 4;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button button1 = new Button(this); button1.setId(1); button1.setText("Bottone 1"); Button button2 = new Button(this); button2.setId(2); button2.setText("Bottone 2"); LinearLayout layout = new LinearLayout(this); layout.addView(button1);

Pagina 11

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men
layout.addView(button2); setContentView(layout); registerForContextMenu(button1); registerForContextMenu(button2); } @Override public void onCreateContextMenu(ContextMenu menu, View ContextMenuInfo menuInfo) { if (v.getId() == 1) { menu.setHeaderTitle("Men bottone 1"); menu.add(Menu.NONE, MENUITEM_COMANDO_1, menu.add(Menu.NONE, MENUITEM_COMANDO_2, } else if (v.getId() == 2) { menu.setHeaderTitle("Men bottone 2"); menu.add(Menu.NONE, MENUITEM_COMANDO_3, menu.add(Menu.NONE, MENUITEM_COMANDO_4, } }

v,

1, "Comando 1"); 2, "Comando 2");

1, "Comando 3"); 2, "Comando 4");

@Override public boolean onContextItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case MENUITEM_COMANDO_1: Toast.makeText(this, "Comando 1", Toast.LENGTH_SHORT).show(); return true; case MENUITEM_COMANDO_2: Toast.makeText(this, "Comando 2", Toast.LENGTH_SHORT).show(); return true; case MENUITEM_COMANDO_3: Toast.makeText(this, "Comando 3", Toast.LENGTH_SHORT).show(); return true; case MENUITEM_COMANDO_4: Toast.makeText(this, "Comando 4", Toast.LENGTH_SHORT).show(); return true; } return false; } @Override public void onContextMenuClosed(Menu menu) { Toast.makeText(this, "Men chiuso!", Toast.LENGTH_SHORT).show(); } }

Si tratta di unattivit che dispone di due bottoni, tutti e due collegati ad un men contestuale. Lesempio mostra come registrare i widget per il men contestuale, come creare men distinti per widget differenti e come gestire gli eventi dei men contestuali.

Figura 7 - Esempio di men contestuale collegato a dei bottoni. Pagina 12

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

A proposito di eventi: anche nel caso dei context menu possibile sfruttare la tecnica di gestione alternativa basata sugli oggetti OnMenuItemClickListener. Submenu Aggiungere dei sotto-men ad un options menu o ad un context menu possibile grazie ai seguenti metodi, disponibili per tutti gli oggetti Menu e ContextMenu: public SubMenu addSubMenu(CharSequence title) public SubMenu addSubMenu(int titleRes) public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) Questi metodi assomigliano moltissimo ai metodi add() per laggiunta di un comune elemento, ed infatti funzionano alla stessa maniera. Lunica differenza che lelemento inserito, una volta selezionato, causer lapertura di un sotto-men. Cosa sar mostrato nel sotto-men possiamo stabilirlo manipolando loggetto android.view.SubMenu restituito dai metodi addSubMenu(). Anche in questo caso avremo a disposizione tutti i metodi di Menu, ed avremo a disposizione anche dei metodi come quelli di ContextMenu per la definizione di unintestazione del men. In effetti i submenu assomigliano moltissimo ai context menu, ed infatti come questultimi non supportano icone e scorciatoie sui loro elementi. Sperimentiamo il seguente esempio:
package mieapplicazioni.test; import android.app.Activity; import android.view.Menu; import android.view.SubMenu; public class MenuDemo07Activity extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { SubMenu subMenu1 = menu.addSubMenu("Submenu 1"); subMenu1.add("Comando 1"); subMenu1.add("Comando 2"); SubMenu subMenu2 = menu.addSubMenu("Submenu 2"); subMenu2.add("Comando 3"); subMenu2.add("Comando 4"); return true; } }

Gruppi di elementi Gli elementi di un men, come si visto inizialmente, possono essere riuniti in dei gruppi. Due o pi elementi sono nello stesso gruppo se, quando li si aggiunti, si usato lo stesso valore groupId. Ad esempio:
menu.add(1, MENUITEM_COMANDO_1, 1, "Comando 1"); menu.add(1, MENUITEM_COMANDO_2, 1, "Comando 2"); menu.add(Menu.NONE, MENUITEM_COMANDO_3, 3, "Comando 3");

Pagina 13

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

In questo caso Comando 1 e Comando 2 appartengono entrambi al gruppo con groupId 1, mentre Comando 3 non fa parte di alcun gruppo (il suo groupId Menu.NONE). I gruppi di elementi permettono di velocizzare alcune operazioni. Ad esempio se si intende abilitare o disabilitare tutti gli elementi di un gruppo, sufficiente richiamare il metodo di Menu cos definito: public void setGroupEnabled(int groupId, boolean enabled) Ad esempio:
menu.setGroupEnabled(1, true);

Alla stessa maniera, gli elementi di un gruppo possono essere resi visibili o invisibili chiamando il metodo: public void setGroupVisible(int groupId, boolean visible) Funzionalit pi particolare riservata invece al metodo: public void setGroupCheckable(int group, boolean checkable, boolean exclusive) Questo metodo rende checkable tutti gli elementi di un gruppo. Quando un elemento checkable, si comporta come un interruttore che pu essere acceso (selezionato) o spento (non selezionato). Se il parametro exclusive false, gli elementi si comporteranno come delle checkbox, cio sar possibile selezionarne anche due o pi contemporaneamente. Se exclusive true, invece, gli elementi del gruppo si comporteranno come dei bottoni radio. In pratica, solo un elemento alla volta potr essere selezionato. Si pu selezionare o deselezionare un elemento con il metodo di MenuItem cos definito: public MenuItem setChecked(boolean checked) Se un elemento selezionato oppure no, invece, lo si pu sapere chiamando: public boolean isChecked() Ecco un esempio che dimostra questa funzionalit:
package mieapplicazioni.test; import import import import android.app.Activity; android.view.Menu; android.view.MenuItem; android.view.SubMenu;

public class MenuDemo08Activity extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { SubMenu subMenu1 = menu.addSubMenu("Tipo checkbox"); subMenu1.add(1, Menu.NONE, Menu.NONE, "Opzione 1"); subMenu1.add(1, Menu.NONE, Menu.NONE, "Opzione 2"); subMenu1.add(1, Menu.NONE, Menu.NONE, "Opzione 3"); subMenu1.setGroupCheckable(1, true, false); SubMenu subMenu2 = menu.addSubMenu("Tipo radio"); subMenu2.add(2, Menu.NONE, Menu.NONE, "Opzione 1");

Pagina 14

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men
subMenu2.add(2, Menu.NONE, Menu.NONE, "Opzione 2"); subMenu2.add(2, Menu.NONE, Menu.NONE, "Opzione 3"); subMenu2.setGroupCheckable(2, true, true); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.isChecked()) { item.setChecked(false); } else { item.setChecked(true); } return true; } }

Figura 8 - Gruppi di elementi checkable. A sinistra un gruppo tipo checkbox, a destra uno di tipo radio.

Men in salsa XML I men, proprio come i layout, possono essere definiti via XML anzich via codice Java. Per farlo si usa la speciale cartella res/menu. I file XML al suo interno usano i tag <menu>, <group> e <item> per definire i men in modo dichiarativo. Eclipse, attraverso il plug-in per lo sviluppo Android, dispone di un plug-in per lediting visuale di questi file. Ecco un esempio:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:title="Sotto men 1" android:id="@+id/item01"> <menu> <group android:id="@+id/group01" android:checkableBehavior="all"> <item android:id="@+id/item03" android:title="Opzione 1" /> <item android:id="@+id/item04" android:title="Opzione 2" /> <item android:id="@+id/item05" android:title="Opzione 2" /> </group> </menu> </item> <item android:title="Sotto men 2" android:id="@+id/item01"> <menu> <group android:id="@+id/group01" android:checkableBehavior="single"> <item android:id="@+id/item06" android:title="Opzione 1" /> <item android:id="@+id/item07" android:title="Opzione 2" /> <item android:id="@+id/item08" android:title="Opzione 2" /> </group> </menu> </item> </menu>

Pagina 15

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 7 Men

Questo XML produce un men tipo quello usato nellesempio del paragrafo precedente. Si tratta di un men principale con due comandi, ciascuno dei quali corrisponde ad un submenu. Il primo contiene tre item di tipo checkbox, mentre il secondo ospita tre opzioni radio. I men XML possono essere caricati attraverso la classe android.view.MenuInflater ed il suo metodo inflate(int menuRes, Menu menu). Supponendo di aver salvato il men XML sopra mostrato al percorso /res/menu/menu1.xml, la seguente attivit in grado di richiamarlo e mostrarlo come proprio options menu:
package mieapplicazioni.test; import import import import android.app.Activity; android.view.Menu; android.view.MenuInflater; android.view.MenuItem;

public class MenuDemo09Activity extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = new MenuInflater(this); inflater.inflate(R.menu.menu1, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.isCheckable()) { item.setChecked(!item.isChecked()); } return true; } }

Abilitare e disabilitare gli elementi Gli elementi di un men possono essere disabilitati e successivamente riabilitati servendosi del seguente metodo di MenuItem: public MenuItem setEnabled(boolean enabled) Quando un elemento disabilitato, lutente non pu selezionarlo, come se non ci fosse.

Pagina 16

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android A cura di Carlo Pelliccia

Lezione 8

Notifiche e finestre di dialogo


Andiamo ad aggiungere un ulteriore tassello nel quadro dellinterazione fra unattivit Android e lutente che ne fa uso. Nelle lezioni precedenti abbiamo gi appreso numerose tecniche per dialogare con chi utilizza lapplicazione: tra widget, eventi e men siamo gi in grado di costruire applicazioni interattive. Ci sono comunque altri due strumenti che non possono assolutamente mancare nel nostro armamentario: sono i cosiddetti toast e le finestre di dialogo. I primi servono per segnalare delle notifiche, mentre le seconde possono essere usate sia per emettere un output sia per ricevere un input. Toast Abbiamo gi usato i toast in alcuni degli esempi delle lezioni precedenti. Andiamo ora ad ufficializzare la conoscenza di questo strumento. Un toast un avviso mostrato per qualche istante in sovraimpressione sullo schermo. Si chiama toast perch, in molti sistemi mobili, la sua forma e la sua animazione di comparsa ricorda quella di una fetta di pancarr che salta fuori dal tostapane.

Figura 1 - Ecco come appare un messaggio toast in Android.

Le notifiche toast vanno usate per brevi messaggi testuali. Insomma, informazioni del tipo impostazioni salvate, operazione eseguita e simili. I messaggi toast rimangono sullo schermo per qualche istante e poi il sistema li rimuove automaticamente: non c alcuna interazione con lutente. La classe di riferimento per la gestione dei messaggi toast android.widget.Toast. A disposizione ci sono i seguenti due metodi statici: public static Toast makeText(Context context, CharSequence text, int duration) public static Toast makeText(Context context, int resId, int duration) Entrambi i metodi costruiscono un messaggio toast testuale. I parametri da fornire sono, rispettivamente, il contesto applicativo (ad esempio lattivit stessa), il messaggio da mostrare (come stringa, nel primo caso, o come riferimento a risorsa esterna, nel secondo) e la durata del messaggio. Non possibile specificare quanti secondi, esattamente, il messaggio dovr restare visibile. Si pu soltanto dire se il messaggio deve durare poco o tanto. Per farlo si deve usare come argomento duration una fra le due costanti Toast.LENGTH_SHORT (durata breve) o Toast.LENGTH_LONG (durata lunga). Ecco un esempio:
Toast toast = Toast.makeText(this, "Questo un toast", Toast.LENGTH_LONG);

Una volta creato, il toast pu essere mostrato chiamandone il metodo show():


toast.show();

Altri metodi degli oggetti Toast permettono di impostare ulteriori propriet dellavviso. Si consideri ad esempio il metodo:

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo

public void setGravity(int gravity, int xOffset, int yOffset) Con questo metodo si pu impostare in quale angolo dello schermo far apparire il messaggio, specificando anche il discostamento dai bordi laterali (xOffset) e da quelli verticali (yOffset). Ad esempio:
toast.setGravity(Gravity.TOP | Gravity.LEFT, 10, 10);

Questo avviso sar mostrato in alto a sinistra, scostato di 10 pixel dai bordi. Si possono anche creare dei messaggi toast che, invece di mostrare del semplice testo, facciano uso di immagini o di altri widget al loro interno. In tal caso, invece di passare per i metodi statici makeToast(), si usa il costruttore della classe, che vuole in argomento il contesto dellapplicazione:
Toast toast = new Toast(this);

La durata, in questo caso, la si pu stabilire con setDuration():


toast.setDuration(Toast.LENGTH_LONG);

Il contenuto del toast, adesso, pu essere del testo, come nel caso precedente:
toast.setText("messaggio di testo");

Esiste anche una seconda variante di setText() che permette lutilizzo delle stringhe esterne:
toast.setText(R.string.messaggio_esterno);

Per un toast graficamente pi ricco, invece, si pu usare il metodo setView(View view), che imposta il widget da visualizzare allinterno della notifica in sostituzione del testo. Ad esempio unicona:
ImageView image = new ImageView(this); image.setImageResource(R.drawable.mia_icona); Toast toast = new Toast(this); toast.setGravity(Gravity.CENTER, 0, 0); toast.setDuration(Toast.LENGTH_LONG); toast.setView(image); toast.show();

Un layout XML esterno pu essere caricato, proprio sotto forma di oggetto View, passando attraverso un oggetto android.view.LayoutInflater. Ogni attivit ne mette a disposizione unistanza:
LayoutInflater inflater = getLayoutInflater(); View view = inflater.inflate(R.layout.toast_xml, null); Toast toast = new Toast(this); toast.setView(view); toast.show();

Questo significa che toast di maggiore complessit possono essere creati con la pi agevole sintassi di XML, per essere poi caricati dinamicamente quando occorre mostrarli.

Pagina 2

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo

Figura 2 - Un toast di maggiore complessit, con icona e testo, costruito a partire da una definizione di layout esterna in formato XML.

Toast: non solo dalle attivit I messaggi toast possono essere mostrati non soltanto dalle attivit, ma anche da altri tipi di applicazioni Android, come i servizi.

Finestre di dialogo Le finestre di dialogo sono dei riquadri che possibile aprire sopra lattivit corrente. Quando una finestra di dialogo compare, lattivit da cui dipende viene bloccata, e lutente deve necessariamente interagire con la finestra di dialogo per farvi ritorno. Lesempio tipico la finestra di conferma, che domanda allutente se vuole proseguire con una certa operazione. Lutente, quando tale finestra compare, non pu far altro che scegliere tra lopzione per proseguire e quella per annullare. Finch la scelta non viene espressa, lattivit sottostante rimane bloccata e non pu essere ripresa.

Figura 3 - Una richiesta di conferma allinterno di una finestra di dialogo.

A differenza dei toast, quindi, le finestre di dialogo sono sia bloccanti sia interattive. Per questo motivo la loro gestione risulta lievemente pi complessa. Lastrazione di base cui far riferimento la classe android.app.Dialog, che definisce mediante i suoi metodi cosa una finestra di dialogo pu fare e come pu essere manipolata. Nei prossimi paragrafi la approfondiremo pi nello specifico. Adesso, invece, concentriamoci sul ciclo di vita delle finestre di dialogo, e sulla maniera che dovremo adoperare per richiamarle e mostrarle. La classe Activity fornisce un metodo cos definito: public final void showDialog(int id) Possiamo richiamare questo metodo ogni volta che dobbiamo aprire e mostrare una finestra di dialogo. Il parametro id simboleggia quale specifica finestra di dialogo lattivit deve mostrare. Il valore arbitrario, nel senso che nostro compito creare le finestre di dialogo ed assegnargli degli identificativi. La prassi consiglia di usare delle costanti interne alla classe dellattivit.

Pagina 3

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo

Facciamo il caso che la nostra attivit faccia uso di due finestre di dialogo, una per emettere un avviso di errore ed una per richiedere una conferma. La miglior cosa da fare, in casi come questo, aggiungere due costanti allattivit, con nomi e valori arbitrari ma univoci. Ad esempio:
private static final int DIALOG_ERROR_ID = 1; private static final int DIALOG_CONFIRM_ID = 2;

Quando dovremo mostrare lavviso di errore, dunque, chiameremo:


showDialog(DIALOG_ERROR_ID);

Analogamente, per la richiesta di conferma, dovremo fare:


showDialog(DIALOG_CONFIRM_ID);

Adesso viene da chiedersi: come fa Android a sapere quali finestre corrispondano effettivamente ai due interi indicati? Ed infatti, allo stato attuale delle cose, Android non lo sa: siamo noi a dover svolgere le associazioni. Per farlo dobbiamo ridefinire il metodo di Activity avente firma: protected Dialog onCreateDialog(int id) Android richiama questo metodo ogni volta che deve creare una finestra di dialogo. La finestra che deve essere creata identificata dal parametro id. Ridefinendo il metodo dobbiamo riconoscere lidentificativo fornito, costruire la finestra di dialogo corrispondente e restituirla sotto forma di oggetto android.app.Dialog. Lo schema consigliato il seguente:
@Override protected Dialog onCreateDialog(int id) { Dialog dialog; switch (id) { case DIALOG_ERROR_ID: dialog = createErrorDialog(); break; case DIALOG_CONFIRM_ID: dialog = createConfirmDialog(); break; default: dialog = null; break; } return dialog; }

In breve, si utilizza un costrutto switch per associare degli oggetti Dialog ai loro corrispettivi identificativi numerici. Nel codice di esempio si fa riferimento ai due metodi createErrorDialog() e createConfirmDialog(), che sono dei metodi custom che lo sviluppatore crea a proprio piacimento per generare le differenti finestre di dialogo di cui ha bisogno. Dopo che la specifica finestra di dialogo stata creata, Android prima di mostrarla richiama il seguente metodo di Activity: protected void onPrepareDialog(int id, Dialog dialog)

Pagina 4

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo

I due parametri corrispondono, rispettivamente, allidentificativo della finestra e alloggetto Dialog costruito nel passaggio precedente. Lo sviluppatore pu opzionalmente ridefinire questo metodo per inizializzare la finestra di dialogo con dei comandi specifici. Il modello, questa volta, il seguente:
@Override protected void onPrepareDialog(int id, Dialog dialog) { switch (id) { case DIALOG_ERROR_ID: prepareErrorDialog(dialog); break; case DIALOG_CONFIRM_ID: prepareConfirmDialog(dialog); break; } }

importate sapere che il metodo onCreateDialog() per uno specifico id viene richiamato solo la prima volta che la corrispondente finestra di dialogo deve essere mostrata. La seconda volta che la stessa finestra dovr essere mostrata, il sistema far riuso dellistanza gi esistente. Se bisogna inizializzare listanza in maniera differente dalla volta precedente, quindi, non resta che farlo ridefinendo onPrepareDialog() ed adottando lo schema proposto sopra. Una volta che una finestra di dialogo viene aperta, ci sono due maniere per chiuderla. In primo luogo la pu chiudere lutente servendosi del tasto back del suo dispositivo. Non sempre possibile farlo, dipende dal tipo e dalle impostazioni della specifica finestra di dialogo. Quando possibile farlo si dice che la finestra cancelable (cio cancellabile). Via codice, invece, sempre possibile chiudere e terminare qualsiasi finestra di dialogo. Lo si pu fare invocando il metodo di Activity cos definito: public final void dismissDialog(int id) Ad esempio:
dismissDialog(DIALOG_ERROR_ID);

La finestra dismessa viene chiusa e nascosta. Come spiegato prima, per, un riferimento alloggetto Dialog che la rappresenta viene conservato allinterno dellattivit, in modo che listanza possa essere riusata nel caso in cui la stessa finestra debba essere nuovamente mostrata. In realt possibile far dimenticare del tutto la finestra di dialogo, servendosi al posto di dismissDialog() del metodo: public final void removeDialog(int id) In questo caso listanza della corrispondente finestra di dialogo viene rimossa ed eliminata. Se la finestra dovr essere mostrata di nuovo, per, sar necessario ricrearla passando nuovamente attraverso onCreateDialog(). La prassi, dunque, invocare dismissDialog() per le finestre di dialogo usate frequentemente, mentre removeDialog() va invocato per chiudere quelle mostrate raramente. Ora che abbiamo le idee chiare sul ciclo di vita delle finestre di dialogo, prepariamoci ad andare pi nello specifico, imparando come costruire ed amministrare una finestra di dialogo. Poche volte si usa direttamente la classe Dialog: nella stragrande maggioranza dei casi si fa prima ad usare una delle sue sottoclassi messe a disposizione dalla libreria di Android. Classi come

Pagina 5

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo

android.app.AlertDialog e android.app.ProgressDialog, infatti, coprono il 99% delle esigenze. Andiamo a conoscerle. AlertDialog Il primo tipo di finestra di dialogo che studieremo android.app.AlertDialog. Si tratta della finestra di dialogo utile per mostrare un avviso o per chiedere qualcosa allutente, come una conferma o la selezione di un elemento da una lista. Cominciamo dal pi semplice dei casi: vogliamo notificare un evento e vogliamo essere sicuri che lutente ne prenda atto. Un messaggio toast, in questo caso, non andrebbe bene: potrebbe scomparire prima che lutente lo noti. Useremo allora una finestra di dialogo in grado di bloccare lapplicazione fin quando lutente non noter ed accetter il messaggio. Il codice per farlo molto semplice:
AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Avviso"); builder.setMessage("Attenzione! Questo un avviso!"); builder.setCancelable(true); AlertDialog alert = builder.create();

Come possibile osservare, la finestra di dialogo viene prodotta servendosi di uno speciale oggetto AlertDialog.Builder. A questo oggetto builder si deve dire quali sono le caratteristiche dellAlertDialog desiderata, e per farlo sono a disposizione numerosi metodi. In questo caso abbiamo specificato il titolo, con setTitle(), ed il messaggio, con setMessage(). Con il comando setCancelable(true) abbiamo fatto in modo che lavviso possa essere chiuso con il tasto back del telefono. Il metodo create(), a questo punto, stato invocato per produrre la finestra di dialogo. Una finestra di dialogo cos realizzata, adesso, potrebbe essere restituita dal metodo onCreateDialog(), producendo un risultato come quello in figura.

Figura 4 - Un avviso che pu essere chiuso con il tasto back del telefono.

Facciamo ora il caso di voler produrre un avviso identico al precedente, ma che, invece di costringere lutente ad usare il tasto back del dispositivo, metta a disposizione esso stesso un bottone chiudi. Ecco come fare:
AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Avviso"); builder.setMessage("Attenzione! Questo un avviso!"); builder.setCancelable(false); builder.setPositiveButton("Chiudi", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dismissDialog(ID_FINESTRA); } }); AlertDialog alert = builder.create();

Con setCancelable(false) abbiamo disabilitato il tasto fisico del dispositivo, mentre con setPositiveButton() abbiamo aggiunto il bottone chiudi. Al metodo abbiamo dovuto anche fornire
Pagina 6

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo

un oggetto di tipo android.content.DialogInterface.OnClickListener. Si tratta, come facile intuire, di un gestore di eventi richiamato alla pressione del tasto chiudi. Con una chiamata a dismissDialog() facciamo dunque in modo che la finestra di dialogo venga chiusa quando lutente tocca il bottone.

Figura 5 - Un avviso che pu essere chiuso attraverso il suo stesso bottone chiudi.

Altrettanto semplice, a questo punto, creare una richiesta di conferma:


AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Conferma"); builder.setMessage("Vuoi proseguire con l'operazione?"); builder.setCancelable(false); builder.setPositiveButton("Prosegui", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // Confermato! dismissDialog(ID_FINESTRA); } }); builder.setNegativeButton("Annulla", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // Annullato! dismissDialog(ID_FINESTRA); } }); AlertDialog alert = builder.create();

Alla risposta positiva programmata con setPositiveButton(), abbiamo aggiunto ora una risposta negativa, con setNegativeButton(). Il metodo simile al precedente: anche in questo caso dobbiamo fornire un listener che intercetti la pressione sul bottone e gestisca di conseguenza levento.

Figura 6 - Una richiesta di conferma con due bottoni.

E se volessimo fornire la facolt di scegliere fra pi di due opzioni? Anche questo possibile:
final String[] options = { "Caff", "Gelato", "T", "Birra", "Rag" }; AlertDialog.Builder builder = new AlertDialog.Builder(this);

Pagina 7

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo
builder.setTitle("Scelta multipla"); builder.setItems(options, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String option = options[which]; // gestione dellopzione option dismissDialog(ID_FINESTRA); } }); builder.setCancelable(false); AlertDialog alert = builder.create();

In questo caso non si sono usati n setMessage() n i metodi setPositiveButton() e setNegativeButton(). Si invece fatto ricorso al metodo setItems(). Questo metodo vuole come argomento un array di stringhe. Ciascuna delle stringhe fornite sar unopzione di scelta. Ancora una volta, il secondo argomento da fornire il gestore di eventi. Quando una delle opzioni sar selezionata dallutente, il metodo onClick() del gestore verr automaticamente richiamato. Largomento which, in questo caso, riporter lindice dellopzione selezionata.

Figura 7 - Un AlertDialog con numerose opzioni possibili.

Lelenco di opzioni pu essere anche popolato con dei bottoni radio o con delle caselle di tipo checkbox. Nel primo caso si deve utilizzare, al posto di setItems(), il metodo setSingleChoiceItems(). Questo metodo vuole tre parametri: larray di stringhe con le opzioni, lindice dellopzione inizialmente selezionata e, infine, il solito gestore di eventi. Ad esempio:
final String[] options = { "Caff", "Gelato", "T", "Birra", "Rag" }; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Scelta multipla"); builder.setSingleChoiceItems(options, 2, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String option = options[which]; // gestione della selezione } }); builder.setCancelable(false); builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // gestione conferma dismissDialog(ID_FINESTRA); } });

Pagina 8

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo
builder.setNegativeButton("Annulla", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dismissDialog(ID_FINESTRA); } }); AlertDialog alert = builder.create();

In questo caso si tornati ad aggiungere i tasti di conferma e di cancellazione. Ci ha senso: lutente seleziona nellelenco lopzione desiderata e quindi conferma la sua scelta con lapposito bottone. Visto che i listener, in questo caso, crescono di numero, si deve fare attenzione a mettere in atto una corretta politica di gestione degli eventi.

Figura 8 - Un AlertDialog con le opzioni rese come bottoni radio.

La scelta multipla, infine, possibile usando dei checkbox. Il metodo utile, in questo caso, setMultiChoiceItems(). Il metodo chiede tre parametri. Il primo la lista delle opzioni, cos come la abbiamo gi conosciuta nei due casi precedenti. Il secondo argomento richiesto un array di booleani, i cui elementi corrispondono a ciascuna delle opzioni possibili, indicando se lopzione corrispondente inizialmente selezionata o meno. Il terzo argomento il gestore degli eventi. Questa volta linterfaccia da implementare DialogInterface.OnMultiChoiceClickListener. Il metodo onClick() di questa interfaccia si differenzia da quello di OnClickListener perch prevede un terzo parametro. Si tratta di un booleano chiamato isChecked, che indica se lopzione toccata dallutente stata selezionata o deselezionata. Ecco un esempio:
final String[] options = { "Caff", "Gelato", "T", "Birra", "Rag" }; final boolean[] selections = { true, false, false, false, false }; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Cosa vorresti?"); builder.setMultiChoiceItems(options, selections, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { String option = options[which]; if (isChecked) { // option selezionato! } else { // option deselezionato! } } });

Pagina 9

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo
builder.setCancelable(false); builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // applicare le opzioni selezionate dismissDialog(ID_FINESTRA); } }); builder.setNegativeButton("Annulla", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dismissDialog(ID_FINESTRA); } }); AlertDialog alert = builder.create();

Figura 9 - Un AlertDialog con le opzioni rese come checkbox, consentendo la scelta multipla.

ProgressDialog Pu capitare che sia necessario svolgere delle operazioni non istantanee, che possono cio durare qualche secondo o anche di pi. Quando avviene ci, si deve far capire allutente che c unoperazione in corso, e che bisogna attendere fiduciosi. Se non lo si fa, lutente potrebbe pensare che lapplicazione non gli sta rispondendo perch lenta o, peggio ancora, perch si bloccata. Questo, naturalmente, male. Per fortuna in casi come questo ci si pu servire di una android.app.ProgressDialog. Si tratta di una finestra di dialogo concepita appositamente per mettere in attesa lutente. Lo scopo duplice: da una parte blocca lattivit, in modo che non si possa far altro che attendere, mentre allo stesso tempo comunica allutente che lapplicazione sta lavorando alacremente e che tutto procede come previsto. Opzionalmente si pu mostrare anche il progresso delloperazione. Ad esempio durante un download possibile far vedere la percentuale di completamento raggiunta. Quando la barra di avanzamento non viene mostrata si dice che la ProgressDialog indeterminata. La maniera pi facile per creare una ProgressDialog indeterminata attraverso il suo metodo statico: public static ProgressDialog show( Context context, CharSequence title, CharSequence message)

Pagina 10

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo

Il metodo crea e restituisce una finestra di attesa indeterminata, quindi senza barra di progresso. Richiede come parametri il contesto dellapplicazione (tipicamente lattivit stessa che sta creando la finestra), il titolo da assegnare alla finestra ed il messaggio da mostrare al suo interno.

Figura 10 - Una ProgressDialog indeterminata (cio senza barra di avanzamento).

Le ProgressDialog con barra di avanzamento sono leggermente pi complesse. Loggetto, in questo caso, va costruito manualmente richiamando il costruttore:
ProgressDialog progress = new ProgressDialog(this);

Bisogna poi specificare che la barra che si sta creando non indeterminata:
progress.setIndeterminate(false);

Adesso si deve indicare di usare la barra orizzontale:


progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

Lavanzamento raggiunto deve essere espresso mediante un valore che va da 0 a 100. Se, per qualche motivo, questo range non fosse adatto ad una vostra specifica esigenza, potete cambiarne il limite superiore, invocando il metodo setMax(). Ad esempio:
progress.setMax(1000);

Cos facendo, il valore di progresso raggiunto potr essere espresso nel range che va da 0 a 1000. A questo punto, la finestra di dialogo pu essere restituita e visualizzata. Su un thread parallelo bisogna intanto iniziare a svolgere loperazione di cui lutente attende la conclusione. Di tanto in tanto, mentre si svolge tale operazione, necessario aggiornare la barra di avanzamento della ProgressDialog, per informare lutente circa il punto raggiunto. Per farlo disponibile il metodo setProgress(), che accetta come parametro un valore intero che rappresenta il livello di completamento raggiunto. Ad esempio:
progress.setProgress(50);

Il valore espresso, naturalmente, deve essere compreso nel range di default (da 0 a 100) o in quello esplicitamente modificato in precedenza chiamando setMax(). Ecco un esempio di attivit che, simulando unoperazione di una certa durata, mostra una barra di progresso fatta avanzare gradualmente attraverso un thread secondario:

Pagina 11

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo
package mieapplicazioni.test; import import import import import import import import import android.app.Activity; android.app.Dialog; android.app.ProgressDialog; android.os.Bundle; android.view.Gravity; android.view.View; android.view.View.OnClickListener; android.widget.Button; android.widget.LinearLayout;

public class ProgressDialogDemoActivity extends Activity { private static final int DIALOG_PROGRESS_ID = 1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Button button = new Button(this); button.setText("Fammi attendere!"); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showDialog(DIALOG_PROGRESS_ID); } }); LinearLayout layout = new LinearLayout(this); layout.setGravity(Gravity.CENTER); layout.addView(button); setContentView(layout); } @Override protected Dialog onCreateDialog(int id) { Dialog dialog; switch (id) { case DIALOG_PROGRESS_ID: dialog = createProgressDialog(); break; default: dialog = null; break; } return dialog; } @Override protected void onPrepareDialog(int id, Dialog dialog) { switch (id) { case DIALOG_PROGRESS_ID: final ProgressDialog ref = (ProgressDialog) dialog; ref.setProgress(0); Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i <= ref.getMax(); i++) { try { Thread.sleep(100); } catch (InterruptedException e) { } ref.setProgress(i); } dismissDialog(DIALOG_PROGRESS_ID); } }); thread.start(); break; } } private ProgressDialog createProgressDialog() { ProgressDialog progress = new ProgressDialog(this); progress.setTitle("Attendere");

Pagina 12

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo
progress.setMessage("Operazione in corso..."); progress.setIndeterminate(false); progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progress.setMax(100); progress.setCancelable(false); return progress; } }

Figura 11 - Una ProgressDialog con avanzamento da 0 a 100.

Per le operazioni pi lunghe e complesse si pu addirittura usare una barra di progresso secondaria. Facciamo il caso di unapplicazione che scarica dei file da Internet. Ad un certo punto deve scaricare dieci file. In questo caso si pu usare la barra di avanzamento principale per far vedere quanti file sono stati gi scaricati, e la barra secondaria per mostrare il progresso raggiunto dal singolo file che di volta in volta viene scaricato. Il metodo utile setSecondaryProgress(), che accetta un intero compreso tra 0 ed il valore massimo previsto. Ad esempio:
progress.setSecondaryProgress(30);

Figura 12 - Una ProgressDialog con barra di avanzamento secondaria.

Finestre di dialogo custom Se AlertDialog e ProgressDialog non dovessero andare bene per una vostra specifica esigenza, potete sempre procedere alla costruzione e allutilizzo di una finestra di dialog custom, cio i cui contenuti sono stabiliti in tutto e per tutto da voi. Vediamo insieme come procedere. Ancora una volta, la miglior cosa da farsi realizzare un layout XML. Proviamo con il seguente:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout_root" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"

Pagina 13

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo
android:gravity="center" android:padding="10px"> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10px" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#ffffff" /> </LinearLayout>

Chiamate il file custom_dialog.xml e disponetelo nella cartella res/layout del vostro progetto Android. Questo layout mette insieme unimmagine ed un testo. N limmagine n il testo, per, sono specificati a livello di XML: realizzeremo ora una classe estensione di Dialog che permetter di impostare luno e laltra. Chiamiamola CustomDialog:
package mieapplicazioni.test; import import import import import android.app.Dialog; android.content.Context; android.os.Bundle; android.widget.ImageView; android.widget.TextView;

public class CustomDialog extends Dialog { public CustomDialog(Context context) { super(context); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.custom_dialog); } public void setImage(int resId) { ImageView image = (ImageView) findViewById(R.id.image); image.setImageResource(resId); } public void setMessage(String message) { TextView text = (TextView) findViewById(R.id.text); text.setText(message); } }

Come potete vedere, estendere Dialog non poi tanto diverso da estendere Activity. Allatto di creazione della finestra abbiamo provveduto affinch il layout XML realizzato in precedenza venga caricato e mostrato allinterno della finestra. Abbiamo poi predisposto i metodi setImage() e setMessage(), che impostano limmagine ed il testo visualizzati nel layout. Ora possiamo utilizzare la classe CustomDialog in unattivit. Baster fare qualcosa del tipo:
package mieapplicazioni.test; import android.app.Activity; import android.app.Dialog; public class MyProjectActivity extends Activity { private static final int DIALOG_CUSTOM_ID = 1; @Override protected Dialog onCreateDialog(int id) { Dialog dialog;

Pagina 14

Laboratorio di Ingegneria del Software A.A 2009/2010 Programmazione su Android Lezione 8 Notifiche e finestre di dialogo
switch (id) { case DIALOG_CUSTOM_ID: dialog = new CustomDialog(this); break; default: dialog = null; break; } return dialog; } @Override protected void onPrepareDialog(int id, Dialog dialog) { switch (id) { case DIALOG_CUSTOM_ID: CustomDialog ref = (CustomDialog) dialog; ref.setTitle("Titolo"); ref.setImage(R.drawable.mia_immagine); ref.setMessage("Testo desiderato"); break; } } }

Figura 13 - Una finestra di dialogo completamente custom.

DatePickerDialog e TimePickerDialog Altre due finestre di dialogo previste dalla libreria di Android sono DatePickerDialog e TimePickerDialog, entrambe nel pacchetto android.app. Come il loro nome lascia intuire, servono per far selezionare allutente una data o un orario.

Notifiche nella status bar La status bar, o barra di stato che dir si voglia, la barra posizionata nella parte alta dello schermo, quella che contiene lorologio, per intenderci. La barra di stato di Android viene utilizzata, tra le altre cose, anche per trasmettere delle notifiche allutente. Ad esempio quando arriva un SMS la barra si attiva e mostra unicona che segnala levento allutente. Lutente pu poi srotolare la barra ed esaminare i dettagli della notifica ricevuta. Anche le applicazioni custom possono segnalare delle notifiche allutente utilizzando questo sistema. Pi che le attivit, ad ogni modo, in genere sono i servizi a farlo. Per approfondire: http://developer.android.com/guide/topics/ui/notifiers/notifications.html

Pagina 15

Potrebbero piacerti anche