Tecnologie - Classe Quinta
robertomana.it
Java per Android
Java per Android
Rev Digitale 1.1 del 19/05/2017
Android SDK …………………………………………………………………………………..………
Android SDK Manager: Creazione di un device ……………..…………………………..…………
Creazione di una applicazione tramite IntelliJ Idea …………………………………..…………….
Activity ………………………………………………………..……………………….………………
Layout ………………………………………………………………………………….……………….
Widget e View – Gestione dell ID …..……………………………………………………….………
La gestione degli eventi …..…………………………………………………………………………
Associazione di uno stesso evento a più controlli ..…………………………………….…………
Associazione di una procedura di evento tramite il designer ..……………………………………
L‟evento onTouch ..……………………………………….…………………………….....…………
Generazione di Numeri casuali ……………………….………………………………....…………
Il file strings.xml ..……………….....……………………………………………………...…………
Il file AndroidManifest.xml .…….....……………………………………………...……...…………
Concetto di Context ..………………………………………………………….….……...…………
2
5
6
7
8
9
10
11
11
11
12
12
12
13
Le finestre di popup : i Toast …………………………………………………….…………………..
AlertDialog …………………………………………………………………….……………………..
InputDialog …………………………………………………………………….……………………..
Il widget EditText ..………………………………………………………………………....…………
Il widget ImageView ..………………………………………………………..…….……...…………
Rotazione del video …………………………………………………………….…………………..
Creazione dinamica dei controlli …………………………………………………………………..
Approfondimenti sulla Gestione dell’ID ……………………………………….……………..
Principali Proprietà comuni ai vari widget ……………………………………….………………..
Check Box e Radio Button ………………………………………………………….………………..
14
15
16
16
17
17
18
18
19
20
I tipi di Layout …………………………………………………………………….…………………..
Gestione delle Activity tramite gli Intent ……………………………………………………………..
Passaggio di parametri ad una Activity …………………………………………..………………..
Ciclo di vita di una Activity …………………………………………………………….………………
La gestione dei thread : Il timer …………………………………………………..……………….
Android Device Monitor ……………………………………………………………………………..
Gestione dei File di Testo ……………………………………………………………………………..
Shared Preferences ……………………….…………………………..………………….…………..
21
23
26
28
29
30
31
32
ListView e Adapter ………………………………..………………………………………………….
Spinner ……………………………………………..………………………………………………….
Creazione di un Adapter personalizzato ……………………..…………………………………….
OptionsMenù ………………………………………………………………………..……………….
ContextMenù …………………………………………………………………………….…………….
33
35
36
38
40
Utilizzo di XML in Java ……………………………….…………………………………...………….. 41
Accesso a SQLite ………………………………………….……………………………...………….. 45
Accesso ad un web service HTTP …………………………..…….……………………………….. 48
Parsing di una stringa Json ……………………..…….……………………………………..…….. 53
Services And Notification ………………………..…….……………………………………..……….. 55
Utilizzo del GPS e Accesso alle Google Maps …………..…….………………………………….. 56
pag 1
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Android SDK
Il SO Android usa un kernel Linux dalla versione 2.6 in avanti (dunque con supporto ai multithread).
Sopra c‟è Android Run Time (ART) basato sulle Librerie Java Core che sono una versione ridotta
delle librerie SE. Lo pseudo codice prodotto dai compilatori android viene tradotto a run time in un
eseguibile vero e proprio mediante un compilatore Just In Time che legge lo pseudocodice e crea un
eseguibile vero e proprio.
Android è stato originariamente sviluppato da una piccola azienda, la Android Inc, ma nel 2005 Google
ne acquistò i diritti e decise di rilasciare Android in modalità open source, il che significa che chiunque
voglia utilizzare tale sistema può farlo semplicemente scaricando l‟intero codice sorgente. Tuttavia i
produttori di hardware possono aggiungere le proprie estensioni al sistema e personalizzare Android
per differenziare i propri prodotti dagli altri
Le applicazioni per Android sono distribuite mediante files .APK (Android Package) che sono files
compressi in un formato simile al JAR. I file .apk possono essere aperti e ispezionati utilizzando
applicativi comuni come 7-Zip, Winzip Winrar o X-Plore (in smartphone Android o Symbian).
La presentazione ufficiale della prima versione del "robottino verde" avvenne il 5 novembre 2007 da
parte della neonata OHA (Open Handset Alliance), un consorzio di aziende del settore Hi Tech che
include Google, produttori di smartphone come HTC e Samsung, operatori di telefonia mobile come
Sprint Nextel e T-Mobile, e produttori di microprocessori come Qualcomm e Texas Instruments. Oggi
copre il 78 % del mercato nella vendita di smartphone / tablet:
Uno dei fattori principali che determinano il successo (o meno) di una piattaforma per dispositivi mobili
è la varietà di applicazioni che essa mette a disposizione degli utenti. A partire dalla fine del 2008,
Google ha messo a disposizione degli utenti l‟Android Market (oggi Google Play), un‟applicazione online attraverso la quale gli utenti possono installare applicazioni aggiuntive sui propri dispositivi. Su
Google Play sono disponibili sia applicazioni gratuite che a pagamento.
pag 2
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Ogni aggiornamento o release, similmente a quanto accade per molte versioni di Linux, segue un
ordine alfabetico e una precisa convenzione per i nomi, che in questo caso sono quelli di dolci:
versione 1.5
versione 1.6
versione 2.0
versione 2.2
versione 2.3
versione 3.0
versione 4.0
versione 4.1
versione 4.2
versione 4.3
versione 4.4
versione 5.0
versione 5.1
versione 6.0
Cupcake (30 aprile 2009 – API Level 3)
Donut (30 aprile 2009 – API Level 4)
Eclair (26 ottobre 2009 – API Level 5)
Froyo (20 maggio 2010 – API Level 8)
Gingerbread (6 dicembre 2010 – API Level 9)
Honeycomb (22 febbraio 2011 – API Level 11)
Ice Cream Sandwich (19 ottobre 2011 – API Level 14)
Jelly Bean (9 luglio 2012 – API Level 16)
Jelly Bean (13 novembre 2012 – API Level 17)
Jelly Bean (24 luglio 2013 – API Level 18)
Kit Kat n seguito ad un accordo con la Nestlé (31 ottobre 2013 – API Level 19)
Lollipop lanciata il 3 novembre 2014 (API Level 21)
Lollipop lanciata il 9 marzo 2015 (API Level 22)
Marshmallow lanciata il 5 ottobre 2015 (API Level 23
Ambiente di sviluppo
Il 12 novembre 2007 l'OHA ha rilasciato il software development kit SDK che include: gli strumenti di
sviluppo, le librerie, un emulatore del dispositivo, la documentazione (in inglese), alcuni progetti di
esempio, tutorial e altro. E‟ disponibile per:
 qualsiasi piattaforma Linux,
 qualsiasi piattaforma x86 compatibile che usi un sistema operativo Windows (da XP in avanti)
 Mac OS X, dalla versione 10.4.8,
L'IDE ufficialmente supportato per lo sviluppo di applicazioni per Android è Eclipse, per il quale è fornito
un plug-in ufficiale denominato ADT (Android Development Tools), che contiene tutte le librerie
necessarie allo sviluppo.
Versioni diverse dell‟SDK possono convivere sulla stessa macchina, ed anzi è necessario installarne
più di una, in quanto i device più vecchi ovviamente supportano soltanto fino ad una certa versione di
API e non oltre. Nelle ultime versioni sono disponibili API per Google Maps, Facebook, mixPanel
(statistiche)
Compilzione
Java Source
Compilatore javac
Class files
dx legge i class files e le risorse e genera i
.dex files (Dalvik EXecutable, dove Dalvik era la vecchia Android Virtual
Machine oggi sostituita da ART)
jar (compattatore) legge i files dex e genera un file finale
.apk che è il file tipico da installare su un sistema Android
Application Framework
Package Manager
Window Manager consente di gestire più applicazioni.
Activity Manager consente di gestire la visualizzazione delle applicazioni gestite a stack (quando si
torna indietro vedo la finestra precedente)
View Manager gestisce la Graphics User Interface
Content Provider gestisce lo scambio i dati tra le applicazioni
Notification Manager gestisce la barra di notifica in alto (es: è arrivata una nuova mail)
pag 3
Tecnologie - Classe Quinta
robertomana.it
Java per Android
L‟APK viene invito direttamente all‟emulatore o al dispositivo vero e proprio.
Un file APK (Android Package) è un archivio che contiene le seguenti cartelle:
 RES (risorse: costanti, form, immagini)
 META-INF contenente i certificati (a chiave pubblica) che garantiscono riguardo alla bontà della
app che si sta scaricando.
ed i seguenti files:
 AndroidManifest.xml ( file xml che descrive l‟applicazione)
 class.dex che è lo pseudo eseguibile vero e proprio che verrà eseguito dall‟Android Run Time
A run time viene generato un processo diverso per ogni applicazione da eseguire.
Installazione dell’SDK
Occorre innanzitutto installare la Java JDK versione 6 o superiori
C:/programmi/java/jdk1.7.0
Occorre poi installare Android SDK scaricabile da :
http://developer.android.com/sdk/index.html
(86 MBytes)
installing the sdk
stand alone
download the sdk now
Al termine del download lanciare l‟installer.
L‟installer propone come default il percorso
C:/utente/appData/local/Android
Viene poi aggiunto un link di lancio nella cartella
C:\Users\myUser\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
In realtà non si tratta di una vera e propria installazione, ma di una semplice cartella che può
essere trascinata ovunque sul disco.
L‟installazione base inserisce soltanto alcune librerie base. Android SDK Manager permette però di
gestire le varie versioni del SDK installate sul proprio computer. Dopo averlo avviato infatti esso mostra
una lista di componenti indicando se ognuno di essi sia installato o meno.
In qualsiasi momento è possibile selezionare altre versioni di API e procedere all‟installazione.
Selezionare ad es tutte le voci di API 20 e poi clickare sul pulsante Install N packages.
Esecuzione di Android SDK
Nelle installazioni più vecchie per lanciare l‟SDK manager occorre andare nella cartella TOOLS ed
eseguire android.bat
Nelle installazioni più recenti nella cartella principale vengono posizionate le due applicazioni :
Android SDK Manager
Android AVD Manager
pag 4
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Installazione scuola
Android SDK è installato nella cartella:
C:\Anadroid\sdk
L‟installazione comprende soltanto la versione 4.3 (latest of Jelly Bean) API Level 18
Android SDK Manager – Creazione di un device
Per prima cosa, occorre creare un emulatore di dispositivo, cioè un AVD (Android Virtual Device)
Menù Tools / Menage AVDs / Create
Come dispositivo di prova va benissimo Nexus S
Come target viene proposto Android 4.3 (unico installato).
Tutti i valori di default proposti sono OK.
Il Nexus S ha una risoluzione di 480 x 800 pixel
ARM è un tipo di CPU. Su Nexus 5 sono montate solo CPU ARM.
Su altri device possono esistere versioni diverse on CPU diverse.
Per provarlo selezionarlo e fare Start
La finestra che viene proposta al momento del lancio fa si che l‟emulatore venga aperto all‟interno di
una finestra di dimensioni ridotte più facilmente trascinabile e riposizionabile sul video.
Il tempo di apertura è abbastanza lungo, per cui è consigliabile avviarlo subito e poi lasciarlo avviato.
Dopo aver creato l‟emulatore, SDK Manager può anche essere chiuso.
L‟emulatore
 simula perfettamente il comportamento di un dispositivo reale, quindi se per esempio clicchiamo
sul pulsante Home in alto a destra vedremo la schermata iniziale del dispositivo
 riproduce tutte le caratteristiche hardware e software del dispositivo mobile, tranne la possibilità
di effettuare chiamate telefoniche. In esso sono disponibili una grande varietà di pulsanti di
navigazione e controllo, utilizzabili tramite il mouse, per generare gli eventi che le applicazioni
devono gestire.
Riferimento Ufficiale: http://developer.android.com/reference
Il menù VIEW mostra la sintassi di tutti i controlli disponibili.
Quando una classe è evidenziata in rosso significa che manca l’import corrispondente.
Digitando ALT + INVIO intelliJ aggiunge automaticamente in alto l’import necessario.
Digitando CTRL+ALT + SPAZIO su un metodo compare la sua firma
Se una Activity implementa una certa interfaccia che richiede l‟implementazione di un certo metodo è
sufficiente fare ALT + INVIO sul nome dell‟interfaccia e poi scegliere Implementa Metodi
pag 5
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Creazione di applicazioni tramite IntelliJ Idea
Una applicazione per Android è articolata nelle seguenti sottocartelle:
src - sorgenti java
res - risorse:
 layout sono le form,
 values sono stringhe e costanti,
 drawable sono le immagini
 animazioni
 palette dei colori
gen – autogenerati (R.java)
bin – compilati (APK e DEX)
AndroidManifest è un file xml che descrive l‟applicazione (c‟è scritto ad esempio la versione di API
in uso)
Una applicazione può utilizzare quattro tipi di componenti :
Activity - è il codice che gestisce un layout (form)
Service - sono eseguiti in background e non hanno interfaccia grafica
Broadcast Receiver
Content Provider (fornitore di contenuti)
I servizi non sono in grado di inviare nulla a monitor
I servizi al massimo possono scrivere sul notification Manager
Le activity NON possono accedere al notification Manager
Una applicazione più avere più activity e più services
Una app non può lanciare un‟altra app, ma può lanciare una activity di un‟altra app
Sul nome del package (packege è sinonimo di namespace) ci deve essere almeno un puntino.
IntelliJ Idea definisce automaticamente il package come
package="com.example.appName"
Il primo esercizio
New project / Android Application Module
Mediante il pulsante new definire il percorso relativo a:
JDK
Program files / JDK
android SDK
c: / Android / SDK
Selezionare quindi la versione di android SDK da utilizzare nel progetto. Se c‟è una sola versione
installata non si pone il problema. Il sistema si memorizza una entry contenente i due percorsi
impostati. Ogni volta che si fa new viene creata una nuova entry.
Invece di fare new si può scegliere fra una delle entry esistenti.
Project Name : Viene proposto il nome della cartella, ma in realtà non c‟è relazione tra le 2 cose.
pag 6
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Seconda finestra
Propone come Application Name lo
stesso nome impostato come Project Name
Occorre quindi
 Impostare il check di creazione di
una Activity Base di esempio,
assegnandogli un nome (default
MyActivity, con iniziale Maiuscola)
 scegliere l‟emulatore da utilizzare
fra quelli precedentemente creati
(oppure collegare il telefono e
scegliere USB device)
 Clickando sui 3 puntini si apre
Android SDK Manager che,
consente, mediante il tasto NEW,
di creare un nuovo device.
 Purtroppo, una volta creato il
device, intelliJ non lo vede subito.
Occorre chiudere e riaprire IntelliJ
Le Activity
Avendo impostato il check precedente, dentro la cartella src viene automaticamente creata una
activity avente come nome di default MyActivity.
Un'activity è costituita da :
 un file xml relativo alla definizione dell’interfaccia (Layout)
 una classe che serve a definire il comportamento dell’interfaccia.
Facendo doppio click si apre una finestra contenente il codice relativo alla classe dell‟activity:
public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
MyActivity estende (cioè eredita da) Activity.
Definita una classe come Activity, è obbligatorio ridefinire il metodo onCreate in quanto è il metodo
che permette l'avvio vero e proprio dell'activity. All'interno di questo metodo occorre inserire tutto il
codice riguardante la struttura della activity.
 savedInstanceState è lo stato dell‟activity salvato da qualche parte su disco.
Quando si lancia una activity, per prima cosa andiamo a recuperare lo stato (che potrebbe anche
essere vuoto).
 setContentView(R.layout.main) è un metodo di Activity che consente di associare
all‟Activity il Layout contenuto nel file main.xml della cartella res/layout del progetto corrente. Ogni
Activity di solito ha assegnato un Layout.
pag 7
Tecnologie - Classe Quinta
robertomana.it
Java per Android
La classe R è una classe base autogenerata dal frame work che serve a gestire tutte le risorse
memorizzate dentro res (layout, etc). All‟interno della classe R il compilatore memorizza un puntatore
esadecimale per ogni elemento contenuto all‟interno del Layout.
Pur appartenendo alla stessa App, ogni Activity sostanzialmente vive di vita propria.
Volendo è possibile terminare una Activity utilizzando il metodo finish();
Decisamente più complicato da una Activity andare a terminare un‟altra Activity
Note
 Prima dell‟onCreate possono essere dichiarate delle Property, ma non è consentito eseguire delle
istruzioni (come ad esempio findViewById() )
 Per visualizzare il Project Explorer -> Menù View / Tool Windows / Project
 Il nome delle risorse (layout, immagini, etc.) deve essere scritto SOLO in minuscolo.
Layout
res / layout / main.xml
main.xml sostanzialmente rappresenta la form principale del progetto.
Con doppio click si accede al designer costituito da due finestre :
 Design contiene il layout grafico della form completabile tramite trascinamento
 Text contiene il codice xml relativo alla creazione dei controlli posizionati sulla form.
Si può indifferentemente scrivere codice oppure trascinare gli oggetto sul designer.
All‟interno della form occorre impostare un oggetto layout. I Layouts molto simili a quelli usati in Java
all‟interno dei Panels. Per default viene impostato un Linear Layout che consente di posizionare i
controlli soltanto uno per riga (uno sotto l‟altro). Il Linear Layout all‟inizio è orientato verticalmente ed
ha altezza e larghezza tali da estendere il layout per tutta la grandezza dello schermo del dispositivo
sia in orizzontale che in verticale.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World, MyActivity" />
</LinearLayout>
All‟interno del contesto Layout si possono posizionare dei Widget che sono i controlli di interazione
con l‟utente. Provare ad utilizzare i vari tipi di Label (Plain Text View, Large Text, etc.).
Nelle prime due linee viene impostata la larghezza e l'altezza della Casella di Testo.
Il valore wrap_content ha l'effetto di estendere l'elemento per una larghezza ed un'altezza tale da
contenere la stringa che inseriremo. L‟ultima riga imposta il contenuto.
pag 8
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Lancio dell’applicazione
Pulsantino play in alto a destra
Prima di lanciare l‟applicazione, occorre verificare che l‟emulatore sia avviato.
Anche senza aprire SDK Manager, per avviare l‟emulatore è sufficiente utilizzare il comando
Run / Edit Configuration (raggiungibile anche dal combo Box posizionato davanti al tasto Play)
Nella parte inferiore della finestra è possibile selezionare quale emulatore utilizzare.
Clickando sui puntini viene aperto (con notevole ritardo !) SDK Manager.
Log
Log.i(“title”, “message”);
Log.d(“title”, “message”);
// messaggio di informazione (solo in modalità BIN)
// messaggio di debug (solo in modalità DEBUG)
Widget e View
Ogni elemento appartenente al package android.widget ha come superclasse la classe View che è
un astrazione di ciò che vedrà l'utente sullo schermo. Per creare un componente personalizzato è
sufficiente creare una classe che estenda la classe View o una sua sotto-classe.
Accesso ai controlli : La proprietà numerica id
Ogni widget deve avere come identificativo un nome che necessariamente deve iniziare con
ilprefisso @+id/ Se lo si omette le associazioni non vengono eseguite correttamente.
Questo prefisso viene impostato in automatico nel momento in cui si trascina un widget sul layout.
Es: @+id/button1
Per accedere ai widget dall‟interno dell‟Activity si utilizza il metodo findViewById (View = Controllo)
final Button btn =(Button) findViewById(R.id.button1);
final TextView lbl = (TextView) findViewById(R.id.lblHello);
R.id.button1 è in realtà una costante numerica di tipo int definita dal compilatore all‟interno del file R
Due widget anche appartenenti a Layout differenti NON possono avere lo stesso nome.
I nomi devono cioè essere univoci a livello dell‟intera applicazione.
findViewById Questo metodo è un metodo disponibile all‟interno di :
 un oggetto di tipo xmlLayout (il cui puntatore può essere recuperato a partire dal nome statico
(ID) del layout facedo uso di un oggetto denominato Inflater)
 una Activity che, mediante il metodo setContentView, ha associato un xmlLayout
In entrambi i casi, pur essendo l‟ID dei controlli univoco a livello di applicazione, findViewById consente
di acceddere soltanto ai controlli posizionati sul xmlLayout al quale si riferisce.
final utilizzato davanti al nome di una variabile significa sostanzialmente che la variabile non può
essere riassegnata (nel caso di un riferimento significa che il riferimento deve rimanere costante, cioè
non può essere riassegnato). All‟interno del metodo onCreate devono essere dichiarate final tutte
quelle variabili per le quali si intende gestire degli eventi o che sono utilizzate all‟interno degli eventi.
L‟evento onCreate, oltre alle inizializzazioni, serve a definire delle procedura di evento che verranno
eseguite in corrispondenza dell‟evento stesso. Quando la procedura termina le variabili locali vengono
rimosse, per cui non sono più accessibili al momento dell‟evento. Dichiarandola final, la variabile viene
memorizzata all‟interno di uno string pool che rimane allocato anche al termine della procedura.
pag 9
Tecnologie - Classe Quinta
robertomana.it
Java per Android
La Gestione degli eventi
Gli eventi sono gestiti tramite oggetti (listener) e non tramite semplici procedure. Per associare un
oggetto di evento (listener) ad un controllo occorre accedere al controlo tramite il solido findViewById e
poi utilizar il metodo
setOnEventNameListener (listenerObject);
EventName è il tipo di evento che si intende gestire (es Click)
listener deve essere l‟istanza di una opportuna clase di ascolto (es OnClickListener)
il metodo .set è sempre seguito dal nome della classe di ascolto (es setOnClickListener()
La classe listener di ascolto può esere istanziata:
1. in loco, cioè internamente al metodo set() stesso
2. esternamente, passando al metodo set() il nome della classe.
Soluazione 1
btn.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
((Button) v).setText(“Pressed”);
// oppure
btn.setText(“pressed”);
}
});
Soluazione 2
View.OnClickListener myListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
((Button) v).setText(“Pressed”);
// btn.setText(“pressed”); // in questo caso btn non è definito
}
});
La classe View.OnClickListener è una classe astratta e non può essere direttamente istanziata
senza prima ridefinirne i metodi astratti. L'unico metodo dichiarato all‟interno della classe è il metodo
onClick che occorre ridefine al momento dell'istanza del listener.
Il metodo onClick riceve come unico parametro la View (il controllo) che ha generato l‟evento.
Consente di definire il comportamento voluto quando il listener cattura l'evento a cui è associato.
Dove posizionare il codice


Il metodo set() deve necessariamente essere scritto all‟interno di un qualche metodo,
tipicamente all‟interno del metodo di gestione dell‟evento onCreate( ).
La classe di ascolto, se definita esternamente rispetto al metodo set(), può essere scritta
o all‟interno del metodo onCreate (ma prima dell‟utilizzo del metodo set()),
o all‟esterno del metodo onCreate (indifferentemente prima o dopo)
pag 10
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Associazione di uno stesso evento a più controlli
La soluzione2 precedente ha il vantaggio di poter associare lo stesso listener di ascolto a più controlli.
All‟interno del listener si può utilizzare uno switch per individuare il controllo premuto:
View.OnClickListener myListener
public void onClick(View v)
switch(v.getId()){
case R.id.btn1:
txt.setText("E'
break;
case R.id.btn2:
txt.setText("E'
break;
}
}
};
= new View.OnClickListener() {
{
stato premuto il pulsante 1");
stato premuto il pulsante 2");
btn1.setOnClickListener(myListener);
btn2.setOnClickListener(myListener);
Associazione di una procedura di evento tramite il designer
Senza istanziare ogni volta il Listener, è sufficiente definire, fuori dal metodo onCreate() una
generica procedura con firma adeguata e poi assegnarla all‟evento direttamente tramite Designer
cercando nella finestra delle Properties l‟evento onClick ed assegnandogli tramite menù a tendina il
metodo desiderato.
public void on_click(View v) {
}
Attenzione che :
 Il metodo deve essere dichiarato public.
 Questa tecnica funziona esclusivamente per l‟evento onClick, per il quale è stata definita una
property aggiuntiva che consente di memorizzare il puntatore alla classe di gestione dell‟evento
L’evento onTouch
Dal momento che i device non hanno il mouse, l‟evento click non è l‟evento migliore da utilizzare.
Al posto dell‟evento onClick è possibile utilizzare l‟evento onTouch che presenta la seguente sintassi:
public boolean onTouch(View v, MotionEvent event) {
if( event.getAction() == MotionEvent.ACTION_DOWN)
((Button) v).setText(“Pressed”);
else if( event.getAction() == MotionEvent.ACTION_UP)
((Button) v).setText(“Premi”);
return true;
});




Il primo parametro rappresenta il controllo che ha generato l‟evento
Il secondo parametro rappresenta l‟azione specifica legata al Touch (ACTION_DOWN,
ACTION_UP, ACTION_MOVE, etc.)
Se il metodo onTouch restituisce true significa che lo stesso Listener dovrà occuparsi anche di
gestire i successivi eventi ACTION_MOVE e ACTION_UP ammesso che esista un handler
Se il metodo onTouch restituisce false significa che l‟evento viene consumato ed il Listener non
sentirà più i successivi ACTION_MOVE e ACTION_UP
pag 11
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Generazione di numeri casuali in Java
Random rnd = new Random();
rnd.nextInt(B-A+1) + A;
nextInt usato senza parametri genera unnumero compreso tra 0 e MAX_INT
Il file strings.xml
Il file strings.xml, posizionato nella cartella res/values, consente di definire delle stringhe costanti che
consentono di parametrizzare l‟applicazione nel senso che l‟applicazione può usare queste stringhe in
qualsiasi segmento di codice andandole a leggere all‟interno del file di risorsa.
Esempio di file strings.xml
<resources>
<string name="app_name">app_02</string>
<string name="titolo">Home Page</string>
</resources>
La stringa app_name è usata in AndroidManifest.xml e consente di cambiare rapidamente il nome
visualizzato sulla barra superiore della app
Utilizzo della stringa titolo all’interno del layout
<TextView
android:text = "@string/titolo"
/>
Utilizzo della stringa titolo all’interno del codice della Activity
Per leggere da codice le stringhe memorizzate all‟interno del file strings.xml occorre utilizzare il metodo
this.getString()
txt.setText("Hello " + this.getString(R.string.titolo));
Il file AndroidManifest.xml
Il file Manifest.xml è un file in cui devono essere registrati tutti i componenti appartenenti al progetto. E‟
l‟equivalente del file di Progetto di C#. Ogni activity deve essere registrata nel file Manifest.
<manifest package="com.example.app_02"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="18"/>
<application android:label="@string/app_name" android:icon="@drawable/ico">
<activity android:label="Pagina 1" android:name="MyActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
il file manifest contiene informazioni dettagliate riguardo all‟applicazione. Definisce :
 il nome del package,
 il numero ed il nome della versione, espresso in formato x.x.x (es 1.0.1).
Questo è ciò che consenti gli aggiornamenti automatici da parte di Google Play.
 la versione del SDK minima per l‟utilizzo dell‟applicazione (minSdkVersion)
pag 12
Tecnologie - Classe Quinta
robertomana.it
Java per Android
<application>
 icon = icona da utilizzare per rappresentare l‟applicazione sul dispositivo. Questa icona è
utilizzata per rappresentare sul desktopo tutte le Activity che compongono la app.
 label = Label dell‟applicazione. Nome identificativo della app impostato in fase di creazione.
Questo nome viene però utilizzato per identificare la app soltanto all‟interno del menù setting /
app. Sul desktop ed anche nella barra del titolo della Activity viene utilizzata la Label della
Activity. Quando si chiede a IntelliJ all‟avvio di creare una nuova Activity, lui imposta la stessa
Label (nome app impostato in fase di creazione) sia per la App che per l‟Activity.
<activity>
 Per ogni Activity sono memorizzati il name e la label, che rappresenta il nome visualizzato
come intestazione dell‟Activity e che identifica l‟Activity sul Desktop. L‟icona è quella della app.
 All‟interno della definizione di ciascuna Activity c‟è un elemento denominato intent-filter
che serve a definire le caratteristiche dell‟Activity :
- I valori MAIN LAUNCHER indicano che si tratta di una Activity che può essere lanciata
direttamente da SO. Tutte le Activity di questo tipo verranno visualizzate sul desktop del
dispositivo.
- Se invece l‟activity presenta il valore category = DEFAULT significa che si tratta di una
Activity secondaria richiamabile soltanto da un‟altra Activity. In questo caso all‟interno di
action è possibile specificare un nome che potrà essere utilzzato per avviare l‟activity. Nel
caso di Activity di tipo DEFAULT la sezione <intent-filter> può anche essere omessa.
Il concetto di Context
La classe Context è la classe base da cui, mediante più passaggi, ereditano le classi Activity.
Rappresenta una specie di interfaccia che consente all’Activity corrente di accedere a risorse e
informazioni specifiche relative all’applicazione.
Context context = getApplicationContext();
Mediante il context è possibile accedere ad un ampio insieme di caratteristiche e servizi a
disposizione dell‟applicazione. Il Context corrente viene utilizzato per :

accedere alle stringhe costanti memorizzate in strings.xml
String s = context.getString(R.string.titolo);

accedere alle eventuali variabili condivise dell‟applicazione::
SharedPreferences myPref = context.getSharedPreferences(name, mode);

quando si creano nuovi oggetti, per fare in modo che il nuovo oggetto possa anche lui accedere
a risorse e informazioni relative all‟applicazione, occorre passare all‟oggetto il context corrente:
TextView lbl = new TextView(context);
Visto che la classe Activity deriva dalla classe Context all‟interno del codice della classe Activity il
this rappresenta il Context dell‟applicazione.
Se però siamo all‟interno di un Listener di evento, this rappresenta il listener stesso.
Per accedere al Context, a seconda dei casi, si può utilizzare una delle seguenti sintassi:
this
MyActivity.this;
// istanza corrente
// Va bene quando mi trovo all‟interno di un listener di evento.
MyActivity. this è una proprietà che punta all‟istanza corrente
v.getContext();
// v è il puntatore ricevuto come parametro dal gestore di evento
getApplicationContext();
// disponibile in ogni situazione. Talvolta non funziona
pag 13
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Le finestre di pop up : i Toast
Le finestre di pop up sono le cosiddette shallow window, cioè le finestre che appaiono / scompaiono
con una durata temporanea paragonabile ad una notifica. Scopo di questo widget è quello di notificare
all'utente un messaggio del tipo "salvataggio effettuato" oppure "operazione eseguita" in seguito a
qualche operazione compiuta dall'utente stesso.
Rispetto alle Alert Dialog il Toast è molto più semplice, non ha pulsanti di interazione e, dopo un breve
tempo, scompare da solo senza che l‟utente debba eseguire un click.
Il costruttore della classe Toast si aspetta come unico parametro il Context di riferimento.
Toast t=new Toast(MainActivity.this);
t.makeText(getApplicationContext(), "Ok!!",
t.show();
Toast.LENGTH_SHORT);
Nelle versioni più recenti la precedente sintassi non è più supportata ed è stata sostituita dall‟utilizzo del
metodo statico Toast.makeText che presenta 3 parametri che sono :
1. Il Context dell‟applicazione nel quale applicare il toast. Dato che il codice è inserito all'interno
del listener non possiamo passare come contesto this in quanto passeremmo come argomento
il listener stesso al quale non è applicabile un toast. Occorre pertanto utilizzare
getApplicationContext()oppure MyActivity.this
2. Il messaggio da visualizzare
3. La durata della visualizzazione (LONG oppure SHORT)
Toast t =Toast.makeText(getApplicationContext(),”ok”, Toast.LENGTH_SHORT)
t.show();
E‟ anche possibile aggiungere il testo in un secondo momento, dopo aver istanziato l‟oggetto:
Toast t =Toast.makeText(this, ””, Toast.LENGTH_SHORT)
t.setText(“myMessage”);
t.setGravity(Gravity.CENTER, 0, 0);
t.show();
Il metodo setGravity() definisce la posizione dello schermo in cui visualizzare il pop up.
Il valore 0,0 rappresenta l‟offset di visualizzazione rispetto al valore impostato da Gravity
Utilizzo del toast per la visualizzazione di una immagine
Occorre per prima cosa caricare l‟immagine da utilizzare.
ImageView img = new ImageView(this);
img.setImageResource(R.drawable.ic_launcher);
Dopo di che al posto del metodo t.makeText() occorre utilizzare il metodo t.setView()
Toast t = new Toast(getApplicationContext());
t.setView(img);
t.show();
Un toast non può visualizzare contemporaneamente Testo e Immagine.
O si visualizza il testo, oppure si visualizza l‟immagine.
pag 14
Tecnologie - Classe Quinta
robertomana.it
Java per Android
AlertDialog
Widget utilizzato per mostrare un messaggio informativo all'utente, tipicamente un errore, un
comportamento non desiderato oppure una richiesta di conferma da parte dell'utente per una qualche
operazione (è dunque in grado di ricevere e gestire degli input da parte dell'utente).
Per esempio un'applicazione che richiede una connessione ad Internet per compiere alcune
operazioni, potrebbe mostrare un'AlertDialog all'utente nel caso in cui il dispositivo non si riesca a
collegarsi ad Internet.
AlertDialog.Builder dlg = new AlertDialog.Builder(this);
dlg.setTitle("AlertDialog");
dlg.setMessage("E' stato premuto Pulsante 3");
dlg.setIcon(R.drawable. ic_launcher);
dlg.show();
// oppure
// AlertDialog myAlert = dlg.create();
// myAlert.show();
Il factory method create() istanzia un nuovo oggetto AlertDialog inizializzato con le
caratteristiche impostate dal builder.
Non essendoci pulsanti di dialogo, per chiudere la AlertDialog occorre clickare nell‟area dell‟Activity
Aggiunta dei pulsanti di dialogo
Aggiungiamo all‟applicazione un Text View in cui visualizzare il pulsante utilizzato dall‟utente per
chiudere la AlertDialog.
dlg.setCancelable(false);
dlg.setPositiveButton("Si", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
lbl.setText("cliccato il tasto SI");
}
});
dlg.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
lbl.setText("cliccato il tasto NO");
}
});
setCancelable(false) ha l'effetto di disabilitare il tasto back del telefono e dunque l'utente dovrà
necessariamente cliccare su uno dei due bottoni per chiudere l'AlertDialog.
setPositiveButton ha come primo parametro il testo da visualizzare nel pulsante e come secondo
parametro un oggetto Listener di tipo DialogInterface.OnClickListener e non di tipo
View.OnClickListener. Anche nel caso del DialogInterface.OnClickListener occorre
comunque ridefinire il metodo OnClick ed inserire il codice relativo al comportamento che
vogliamo dare al bottone.
Parametri del metodo onClick dell‟ DialogInterface.OnClickListener :
1.
2.
un oggetto di tipo AlertDialog che indica quale dialog ha generato l'attivazione del listener
un id che identifica il bottone premuto.
Questi due parametri consentono di realizzare un unico Listener per tutte le AlertDialog
dell‟applicazione con i relativi pulsanti. Ciò si realizza semplicemente eseguendo un primo switch sul
parametro dialog (identificando quale AlertDialog ha attivato il listener) con all‟interno un altro switch
per capire quale bottone, relativo alla AlertDialog presa in esame, è stato cliccato.
pag 15
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Input Dialog
Per trasformare una AlertDialog in InputDialog è sufficiente aggiungere una casella di input
all‟interno della Dialog stessa.
AlertDialog.Builder dlg = new AlertDialog.Builder(this);
dlg.setTitle("InputDialog");
dlg.setMessage("Inserire il nome:");
dlg.setIcon(R.drawable. ic_launcher);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
dlg.setView(input);
dlg.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
Toast.show(input.getText().toString());
}
});
dlg.show()
Attenzione che se il programma necessita di aprire più finestre di dialogo consecutive utilizzando un
codice del tipo :
apriFinestra3();
apriFinestra2();
apriFinestra1();
le tre finestre verranno aperte consecutivamente una sopra l‟altra, per cui occorrerà richiamarle in
ordine inverso rispetto alla sequenza desiderata di apertura. L‟evento click di ogni pulante
memorizzarà il valore inserito all‟interno di una aapposita variabile globale. Il click di finestra3
richiamrerà una funzione elabora() che andrà ad elaborare i tre valori inseriti.
La casella di testo : il widget EditText
La proprietà inputType consente di definire il comportamento della Casella di Testo :
inputType
inputType
inputType
inputType
=
=
=
=
"none"
"text"
"number"
"password"
hint = "Inserisci il tuo testo"
qualsiasi carattere
solo caratteri testuali
solo numeri. In corrispondenza del click si apre il tasterino numerico
visualizza asterischi
suggerimento iniziale
Nelle versioni più recenti di IntelliJ sono stati aggiunti molti TextFields specifici con inputType già
settato. Quando l'utente clicca su un oggetto di tipo EditText appare automaticamente una tastiera
virtuale sullo schermo del dispositivo che permette l'immissione del testo. La chiusura della tastiera
quando l'utente termina l'immissione del testo non è invece implementata direttamente nella tastiera,
ma occorre aggiungere un bottone particolare che consenta di rimuovere la tastiera dallo schermo.
Per arricchire la tastiera con il tasto done occorre inserire la seguente riga:
txt.setImeOptions(EditorInfo.IME_ACTION_DONE);
Il metodo setImeOptions ha l'effetto di settare un IME nella tastiera.
pag 16
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Un IME (input method) implementa un particolare modello di interazione che l'utente può utilizzare.
Il tipo di IME da aggiungere alla tastiera deve essere passato come argomento al metodo
setImeOptions. I principali tipi IME (mutuamente esclusivi) sono:
IME_ACTION_DONE
IME_ACTION_GO
IME_ACTION_NEXT
IME_ACTION_SEND
Nel caso di campi URL invia la richiesta HTTP al server
Passa al campo successivo
Invia il testo ad un servizio specifico
La gestione delle immagini : il widget ImageView
Consente di visualizzare un'immagine memorizzata all‟interno della cartella res la quale, oltre alla
sottocartella Layout, contiene le seguenti cartelle drawable per la memorizzazione delle immagini:



drawable-hdpi
drawable-mdpi
drawable-ldpi
// high definition
// medium definition
// low definition
Poiché Android può essere installato su dispositivi diversi aventi risoluzione diversa, per avere una
ottimizzazione dell'immagine è opportuno creare versioni delle immagini a grandezze differenti (grandi,
medie, piccole) ed inserirle nelle cartelle sopra indicate. A seconda della risoluzione del dispositivo
viene automaticamente scelta la cartella appropriata. Se l‟immagine è caricata in una sola cartella,
viene comunque caricata quella. Nel caso di immagini di dimensioni superiori alla risoluzione dello
schermo, Android effettua un resize automatico in modo da renderla completamente visibile.
Il valore layout_width/height="fill_parent" sul tag ImageView ingrandisce il controllo fino a
quando l‟immagine non satura completamente una delle 2 dimensioni.
 Aggiungere una immagine nella cartella hd
 Così come per i Layout, anche per le res i nomi dei files devono essere scritto in minuscolo.
 Impostazione dell‟immagine sul Layout Grafico. Trascinare un oggetto ImageView :
android:src="@drawable/medusa"
// senza estensione !!
 Impostazione dell‟immagine da codice :
final ImageView img =(ImageView) findViewById(R.id.img1);
img.setImageResource(R.drawable.desert); // senza estensione !!
 la sintassi precedente però non consente concatenamenti, In alternative si può scrivere :
int id = getResources().getIdentifier(“img”+i, "drawable", "package");
img.setImageResource(id);
Rotazione del video
if(this.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
else this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Attenzione che la rotazione del video provoca la terminazione dell‟Activity e successivo riavvio con
rigenerazione dell‟evento onCreate. Questo di per sé non è un gravissimo problema e non inficia il
buon funzionamento della rotazione, ma il riavvio dell‟applicazione può comunque creare fastidi. Ad
esempio se il contenitore img del layout all‟avvio caricava staticamente una immagine, questa viene
ricaricata ad ogni rotazione.
Per evitare che in corrispondenza della rotazione dell‟immagine venga riavviata l‟applicazione,
occorre aggiungere la seguente riga:all‟interno del Manifest:
<activity android:name="MyActivity" android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize">
pag 17
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Dall‟API Level 13 in avanti la rotazione del video provoca anche un effetto screenSize che pertanto
deve essere inserito dentro configChanges. Nelle versioni precedenti deve essere omesso perché
non riconosciuto. Questa riga fa sì che, in caso di rotazione, l‟applicazione non si riavvii ma che generi
semplicemente l‟evento onConfigurationChanged che può essere gestito nel modo seguente:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Toast t = Toast.makeText(this, "changed", Toast.LENGTH_SHORT);
t.show();
}
Creazione Dinamica dei controlli
Il layout grafico di una Activity può essere creato dinamicamente tramite codice anche senza l‟utilizzo
di un file .xml.
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setGravity(Gravity.BOTTOM);
final Button btn1 = new Button (this);
btn1.setText("Bottone 1");
btn1.setId(1);
final Button btn2 = new Button (this);
btn2.setText("Bottone 2");
btn2.setId(2);
layout.addView(btn1);
layout.addView(btn2);
setContentView(layout);
Essendo il codice scritto direttamente all‟interno del metodo onCreate( ) della classe Activity, in questo
caso this rappresenta il Context corrente.
Note sulla gestione dell’Id
L‟Id è in realtà un numero intero utilizzato per identificare ciascun controllo (View). L‟Id può essere
assegnato staticamente all‟interno del file XML oppure dinamicamente da codice.
Assegnazione statica dell’ID all’interno del file XML
Occorre definire per ogni controllo un attributo del tipo:
android:id=”@+id/button1”
Il valore dell‟attributo id deve essere univoco sull‟intera applicazione.
Il compilatore trasforma questa stringa alfanumerica in un intero positivo e univoco.
Riferimento al controllo
Per accedere da codice al controllo occorre utilizzare la seguente sintassi:
Button btn =(Button) findViewById(R.id.button1);
dove R.id.button1 sostanzialmente è la costante numerica identificativa del controllo.
pag 18
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Lettura dell’id del controllo
Il metodo getId() restituisce la costante numerica R.id.button1
if( v.getId() == R.id.button1)
Mentre findViewById restituisce un riferimento al controllo,
v.getId() restituisce l‟ID numerico del controllo al quale viene applicato
Assegnazione dinamica dell’ID tramite codice
L‟assegnazione può essere eseguita semplicemente mediate il metodo .setId(int) dove il
parametro deve essere un numero >= 0 non necessariamente univoco e che può anche essere in
conflitto con un id statico. L‟univocità dell‟ID è importante soltanto dal punto di vista logico-funzionale.
L‟accesso all‟ID di un controllo può essere eseguito al solito tramite v.getId()
lbl.setText(Integer.toString(v.getId()));
Dalla versione 14 di IntelliJIdea, non è più consentito assegnare direttamente un valore numerico al
metodo setId() e nemmeno è consentito confrontare direttamente il valore restituito da v.getId()
con un valore numerico, ma in entrambi i casi occorre passare attraverso una variabile:
int id = 0;
// id DEVE partire da 0 !
btn1.setId(id++);
int id = v.getId();
if(id==1) .......
Riferiemnto al controllo creato dinamicamente
L‟accesso al controllo sul layout può essere eseguito semplicemente facendo:
Button btn =(Button) findViewById(id);
Come è possible consentire id non univoci ?
Il fatto che l‟ID non debba essere necessariamente essere univoco si spiega con il fatto che, quando si
ricerca di un controllo appartenente al Layout tramite findViewById, esso esegue una semplice
ricerca sequenziale restituendo il primo controllo avente l‟ID indicato. Poiché nella lista dei controlli
quelli creati staticamente a Design Time si trovano sicuramente prima di quelli creati dinamicamente,
findViewById, in caso di conflitto, restituirà sempre il controllo statico (che in certi casi potrebbe
essere un problema). Se si usa la crazione dinamica è MEGLIO creare tutto dinamicamente !
L‟API 17 ha introdotto il metodo View.generateViewId() che consente di generare ID univoci.
Principali Proprietà Comuni ai vari widget
http://developer.android.com/reference/android/widget/package-summary.html
Elenco principali Property
text = “testo del controllo”
textSize = "30dp"
// dp = „density pixel‟ è sostanzialmente „quasi‟ sinonimo di pixel
tag = Campo nascosto di tipo stringa molto comodo per salvare valori di appoggio (id record)
textColor = "#ffff00"
// colore del testo
background = "#0000ff" // colore di sfondo
background = "@android:color/holo_blue_dark"
pag 19
Tecnologie - Classe Quinta
robertomana.it
Java per Android
I valori fillparent e matchparent sono equivalenti. Il primo è deprecato a favore del secondo.
layout_width="185dp”
// larghezza del controllo
layout_height="wrap_content"
// altezza del controllo adattata al contenuto
layout_margin=”10dp”
// margini esterni.
layout_gravity
// ancoraggio dell‟oggetto rispetto al Layout.
I principali valori di layout_gravity sono:
 TOP
In cima al contenitore
 CENTER
Al centro del contenitore, rispetto sia all‟asse X che all‟asse Y
 BOTTOM
Al fondo del contenitore
 LEFT
A sinistra,
RIGHT A destra
 FILL
Riempie l‟intera area del contenitore
I precedentii valori di gravity possono essere combinati tra loro tramite una OR bit a bit.
Es Gravity.LEFT | Gravity.BOTTOM
gravity
// ancoraggio del testo rispetto all‟oggetto
Esempio: txtNome.setGravity(Gravity.CENTER_VERTICAL);
enabled = true / false
// Disabilita il controllo
editable = true / false
// Usato nei TextView
visibility = View.VISIBLE / View.INVISIBLE
padding = "10dp" // non va a modificare width e height. Da codice : .setPadding(10, 10, 10, 10);
// i singoli valori possono anche essere negativi. Viene aggiunto come offset rispetto alla pos corrente
ems=”10”
// consente di impostare le dimensioni del controllo in sol colpo (width textSize etc)
Accesso da codice
Per l‟accesso da codice alle Property occorre utilizzare i metodi get e set che spesso utilizzano valori
in formato differente rispetto alle Property precedenti.
.getText();
restituisce il testo del widget. Spesso occorre fare .toString()
.setText(“testo”);
setta il testo del widget
.bringToFront()
porta il widget corrente in primo piano.
.setBackgroundColor(Color.holo_blue_dark);
// oppure
.setBackgroundColor(Color.rgb(214,214,214); // oppure
.setBackgroundColor(0xFF00FF00); // verde. Il primo FF è l‟opacity. FF=solido, 00=transparent
setPadding(6,6,6,6); I valori di Padding e Margin partono da LEFT / TOP / RIGHT / BOTTOM
layout.getChildAt(i); restituisce il puntatore all‟oggetto i-esimo figlio del layout indicato (base 0)
Immagine di sfondo di un pulsante
btn.setBackgroundResource(R.drawable.myimg);
btn.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.btn_
default));
// Depracato ed inoltre imposta il pulsante come dimensioni fisse. Meglio usare 2° img
pag 20
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Check Box e Radio Button
Non esiste il concetto di value, ma si utilizza sempre la Property Text che è quella visualizzata a fianco
del Check Box / Radio Button. Esempi di utilizzo:
if (myOpt.isChecked()) {
myOpt.setText(“checked”);
myOpt.setChecked(false); }
int id_elemSelez = optGroup.getRadioButtonCheckedId();
RadioButton opt = (RadioButton) findViewById(id);
RadioButton opt = (RadioButton) optGroup.getChildAt(2);
// 3° rdb
Sono mutuamente esclusivi soltanto i Radio Button inseriti all‟interno di un medesimo RadioGroup.
Non sono mutuamente esclusivi i Radio Button inseriti direttamente all‟interno di un Layout o altro.
Layout
LinearLayout
E‟ il layout di default. Dispone gli elementi uno di seguito all'altro.
La proprietà orientation (vertical o horizontal=defaut) consente di impostare il layout:
 verticale - gli elementi vengono disposti uno sotto l'altro (uno per riga)
 orizzontale - gli elementi vengono disposti uno dopo l'altro da sinistra verso destra (tutti sulla
stessa riga). In questo caso i vari elementi dovranno avere width="wrap_content" e NON
width="fill_parent" altrimenti ne vedremo solo uno.
In entrambi i casi gli elementi vengono aggiunti alla finestra finchè c'è spazio . Se si richiede
l'inserimento di un ulteriore elemento, android cerca di ridimensionarlo (peraltro in modo molto poco
leggibile) per inserirlo comunque all'interno dello schermo. Questo comportamento è limitato ad una
soglia oltre la quale, semplicemente, gli elementi aggiuntivi non vengono più visualizzati.
La proprietà layoutDirection consente visualizzare i controlli interni da sinistra verso destra
(LAYOUT_DIRECTION_LTR) oppure da destra verso sinistra (LAYOUT_DIRECTION_RTL)
La proprietà gravity del LinearLayout consente di ancorare i wdget interni nelle varie posizioni.
TableLayout
Permette di organizzare i contenuti come se si stesse lavorando con una tabella. L‟organizzazione
avviene tramite l‟oggetto TableRow che identifica una riga della tabella All'interno di questo oggetto
dovremo inserire tutti gli elementi che vogliamo mostrare in quella specifica riga. Es di matrice 4 x 4
TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
for (int i = 0; i < 4; i++) {
TableRow tableRow = new TableRow(this);
tableRow.setGravity(Gravity.CENTER);
for (int j = 0; j < 4; j++) {
Button btn = new Button(this);
btn.setId(i * 4 + j); btn.setText("");
btn.setPadding(10, 10, 10, 10);
btn.setOnClickListener(myListener);
tableRow.addView(btn);
}
tableLayout.addView(tableRow);
}
pag 21
Tecnologie - Classe Quinta
robertomana.it
Java per Android
FrameLayout
La particolarità del Frame Layout è quella di consentire la sovrapposizione dei controlli, che vengono
posizionati su piani crescenti in base alla posizione del controllo all‟interno del Layout.
In pratica mentre il Linear Layout consente di disporre consecutivamente i controlli in verticale oppure
in orizzontale, il Frame Layout consente di impilarli lungo l‟asse z. Alla base è come se ci fosse un
unico controllo. Per ogni controllo si può specificare tramite layout_gravity il tipo di ancoraggio
(con i soliti 9 valori: top, bottom, left, right, fill, top|left, top|right, bottom|left, bottom|right).
Utilizzato spesso per realizzare le Transitions di una immagine sopra un‟altra immagine.
RelativeLayout
Consente di posizionare liberamente gli elementi indicando dimensione e posizione.
Non è però una buona strategia perché vincola alla risoluzione del monitor
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="39dp"
android:layout_marginTop="94dp"/>
Invece che su Top e Left, il controllo può essere allineato, ad esempio, rispetto a ParentRight e
ParentBottom, specificando poi i valori di marginRight e marginBottom.
Le proprietà di tipo layout
.setWidth(50);
setHeight(50);
// Non sembrano riconosciuti
.setGravity(Gravity.LEFT | Gravity.BOTTOM), oltre al valore di gravity, presenta due altri parametri
che rappresentano offsetX e offetY rispetto alla posizione definita dal primo parametro.
Tutte le proprietà dei singoli controlli che interagiscono con il layout (cioè width, height, margin,
gravity del controllo rispetto al layout, etc.) e che, nel designer, iniziano con la parola layout, devono
essere definite tramite un oggetto LayoutPrameters ed assegnate al controlo tramite il metodo
.setLayoutParams();
L’oggetto LayoutPrameters deve essere del tipo specifico a seconda del layout a cui il
controllo appartiene. I valori di width e height possono essere passati direttamente al costruttore.
Per alcuni tipi di layout (es TableRow) è anche disponibile un costruttore privo i parametri.
L‟oggetto layoutParams può essere istanziato ex-novo oppure può essere ricavato dal layout
corrente tramite il metodo . .getLayoutParams(); Esempio :
TableRow.LayoutParams params = new TableRow.LayoutParams(300,
TableRow.LayoutParams.WRAP_CONTENT);
params.setMargins(6,6,6,6);
params.gravity= Gravity.CENTER_VERTICAL;
params.gravity= Gravity.CENTER_HORIZONTAL;
params.width(50);
// sovrascrive il 300 passato al costruttore
btn.setLayoutParams(params);
// oppure, nel caso interessino solo width e height, ancora più semplicemnte si può fare:
btn.setLayoutParams(new TableRow.LayoutParams (300, 400);
pag 22
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Scroll di un Layout
Per rendere un Linear Layout srollable è sufficiente racchiuderlo all‟interno di un oggetto ScrollView
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:orientation="vertical"
</LinearLayout
</ScrollView
Rimozione dei controlli da un Layout
layout.removeAllViews();
Gestione delle Activity tramite gli Intent
Per creare una nuova Activity src / package / tasto destro / New Android Component / Activity
Creando la nuova Activity in questo modo, questa viene automaticamente dichiarata nel Manifest
senza però la sezione di <intent-filter>.
Attenzione che la nuova Activity deve essere posizionata nella cartella interna e NON direttamente
dentro src. Eventualmente spostarla con trascinamento.
Per ciascuna Activity si può creare un apposito layout di riferimento. In realtà però una Activity1
potrebbe utilizzare due diversi Layout senza necessità di dover istanziare una seconda Activity
Il fatto di creare due Activity separate, una per ogni layout, consente di suddividere il codice relativo a
ciascun layout.
Registrazione dell’Activity all’interno del File Manifest
Ogni nuova Activity deve necessariamente essere registrata all‟interno del file Manifest.
Per registrare l‟Activity all‟interno del file Manifest ci sono due possibilità :
pag 23
Tecnologie - Classe Quinta
robertomana.it
Java per Android
caso 1
Utilizzando i valori action=MAIN e category= LAUNCHER
<activity android:name="Activity2" android:label="Pagina 2">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
In questo caso l‟Activity potrà essere eseguita direttamente dal sistema operativo e verrà visualizzata
sul desktop dello smartphone. Nel caso di più Activity MAIN, l‟Activity da lanciare all‟avvio dovrà
essere specificata all‟interno della finestra EDIT CONFIGURATION.
caso 2
Utilizzando action= nome simbolico e category=DEFAULT
<intent-filter>
<action android:name="android.intent.action.secondaActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
In questo secondo caso l‟Activity sarà una activity secondariaa che potrà solo essere richiamata da
un‟altra activity attraverso l‟utilizzo di un intent. Se si utilizzano gli intent espliciti, la sezione <intentfilter> può anche essere omessa.
Il concetto di Intent
Un Intent rappresenta l‟intento di eseguire una certa azione, o meglio una richiesta al Sistema
Operativo di avviare una certa azione. E‟ una forma di messaggistica gestita dal sistema operativo
con cui un Componente (Activity, Service, Broadcast Receiver o ContentProvider)
può richiedere in modo asincrono l‟esecuzione di un‟azione da parte di un‟altro componente.
Un Intent rappresenta un messaggio che una Activity può inviare al SO per:

avviare un‟altra Activity - startActivity(intent)

avviare un Service - startService(intent)

inviare un broadcast - sendBroadcast(intent)
L‟APP o il sistema operativo sono perennemente in ascolto per servire le richieste di tipo Intent.
Per avviare l‟Activity esistono due sintassi leggermente diverse entrambe applicabili per avviare sia
Activity LAUNCHER sia Activity DEFAULT.
Intent Espliciti
Un Intent si dice Esplicito quando passo esplicitamente al costruttore il nome del Componente da
avviare. Per istanziare ed avviare un Intent esplicito si procede nel modo seguente:


si passa al costruttore il nome del file contenente l‟Activity da avviare
si esegue l‟Intent mediante il metodo appropriato. Se il Componente da avviare è una Activity
occorre utilizzare il metodo startActivity che avvierà l‟Activity in un processo separato.
public void onClick(View v) {
Intent intent = new Intent(this, Activity2.class);
this.startActivity(intent); // metodo del context
pag 24
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Intent Impliciti
In questo caso non si passa nulla al costruttore creando un Intent anonimo.
Successivamente, mediante setAction(), si assegna il nome del Componente da avviare.
Intent intent = new Intent();
intent.setAction("android.intent.action.secondaActivity");
startActivity(intent);
Nel caso di Activity DEFAULT il nome da utilizzare è quello definito nella action all‟interno del Manifest
Nel caso di Activity LAUNCHER, il nome da utilizzare è il name della Activity da avviare (sempre
definito all‟interno del Manifest).
Se nel manifest non è stata definita la sezione <intent-filter>, gli attributi ACTION e CATEGORY
possono anche essere definiti via codice:
Intent intent = new Intent(this, Activity2.class);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setAction(Intent.ACTION_MAIN);
startActivity(intent);
Ulteriore sintassi per la definizione dell’intent:
Intent intent = new Intent("android.intent.action.secondaActivity ");
startActivity(intent);
Chiusura di una Activity
La stessa cosa la si potrebbe fare sul pulsante di Activity2 per lanciare Activity1. Così facendo però
Activity1 viene reistanziate e una seconda istanza viene caricata in memoria in cima allo stack.
Per far ricomparire la precedente istanza di Activity1 è sufficiente chiudere la finestra Activity2 e si
ripresenta automaticamente l'ultima activity presente sullo stack, cioè Activity1
public void onClick(View v) {
Activity2.this.finish();
finish();
this.finish();
// ok
// ok
// nok per la classe annidata
Il metodo finish rimuove fisicamente l‟istanza dalla memoria generando l‟evento onDestroy.
Viceversa il pulsante Back dello smartphone non rimuove l‟istanza dalla memoria, ma la contrassegna
come “Stopped” e genera il solo evento onStop.
Il Sistema Operativo la rimuoverà dalla memoria soltanto in caso di necessità.
Come riportare una Activity in primo piano
Per riportare una Activity in primo piano è sufficiente settare un apposito flag prima di eseguire
startActivity. In questo modo l‟Activity non viene re istanziata ma solo riportata in primo piano.
Intent intent = new Intent(Activity2.this, Activity1.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
Attenzione che, in questo caso, in Activity2 non viene generato l‟evento onCreate, ma viene generato
semplicemente l‟evento onResume() che è un evento privo di parametri e che, allo stesso modo di
onCreate(), come prima istruzione DEVE richiamare l‟evento onResume( ) della superclasse
pag 25
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Passaggio di parametri ad una Activity
Sia nel caso di Explicit Intent, sia nel caso di Intent Anonimo, prima di richiamare il metodo
startActiity(intent), si possono aggiungere dentro intent eventuali parametri da passare
alla seconda Activity, facendo uso del metodo
intent.putExtra("nomeParametro", valore);
intent.putExtra("parametro1","Ciao");
intent.putExtra("parametro2", (float) 77.77); // default double
intent.putExtra("parametro3", new int[]{23, 12, 54});
intent.putStringArrayListExtra("parametro4", myList);
// ArrayList
this.startActivity(intent);
Lettura dei parametri in Activity2
Activity2 può accedere ai parametri ricevuti nel modo seguente:
Intent intent = this.getIntent();
if(intent!=null && intent.hasExtra("parametro1"))
parametro1 = intent.getStringExtra("parametro1");
if(intent!=null && intent.hasExtra("parametro2"))
parametro2 = intent.getFloatExtra("parametro2", 0); (0=defaultValue)
parametro3 = intent.getIntArrayExtra("parametro3");
myList = intent.getStringArrayListExtra ("parametro4");
In alternativa al passaggio dei passaggio dei parametri, questi potrebbero essere dichiarati statici
dentro Activity1 diventando accessibili da Activity2 semplicemente come Activity1.parametro1
Restituzione di un risultato
Se Activity1 intende ricevere una risposta da Activity2, in fase di chiamata non deve utilizzare il metodo
startActivity(), ma startActivityForResult() passandogli come secondo parametro un
codice identificativo della richiesta :
final int CODICE_RICHIESTA = 13;
this.startActivityForResult(intent, CODICE_RICHIESTA);
Activity2, una volta letti i parametri ed eseguite le eventuali azioni necessarie, potrà presentare
all‟utente due pulsanti OK e CANCEL che provvederanno ad inviare all‟utente due risultati differenti.
public void btnOk_Click(View v) {
Intent intent = new Intent();
intent.putExtra("risultato", "xx");
this.setResult(RESULT_OK, intent);
this.finish();
}
public void btnAnnulla_Click(View v) {
this.setResult(RESULT_CANCELED);
this.finish();
}
pag 26
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Activity1 infine potrà andare a leggere il risultato ricevuto gestendo il seguente evento relativo all‟Activity
onActivityResult(int requestCode, int resultCode, Intent intent)
dove:
 Il primo parametro rappresenta il codice relativo alla richiesta inviata da Activity1
 Il secondo parametro rappresenta il codice restituito da Activity2
 Il terzo parametro rappresenta gli eventuali dati inviati da Activity2 ad Activity1
public void onActivityResult(int reqCode, int resultCode, Intent intent){
TextView txt = (TextView) findViewById(R.id.txtRisultato);
if(reqCode == CODICE_RICHIESTA){
String pulsantePremuto = "";
if(resultCode == RESULT_OK)
pulsantePremuto = "RESULT_OK";
else if(resultCode == RESULT_CANCELED)
pulsantePremuto = "RESULT_CANCELED";
String par = null;
if(intent!=null && intent.hasExtra("risultato"))
par = intent.getStringExtra("risultato");
txt.setText(pulsantePremuto + “ – “ + par);
}
}
Attenzione che, essendo resultCode una define numerica, la seguente riga
txt.setText(Integer.toString(resultCode));
produrrebbe a video il valore numerico di resultCode convertito in stringa.
Terminazione dell’intera applicazione
La chiusura di una app di solito è demandata al SO, e l‟uso di un pulsante Exit è sconsigliato.
Una semplice possibilità per chiudere la app è quella di far ritornare alla Activity secondaria un certo
risultato prima di fare finish. Su quel risultato anche la Activity primaria farà il suo finish.
public void btnOk_Click(View v) {
Intent intent = new Intent();
intent.putExtra("termina",true);
this.setResult(RESULT_OK,intent);
this.finish();
fa sì che venga ritornato il valore Exit alla Activity principale, la quale potrà eseguire il seguente codice:
Boolean chiudi=intent.getBooleanExtra("termina",false);
if(chiudi)
this.finish();
pag 27
Tecnologie - Classe Quinta
robertomana.it
Java per Android
I principali eventi di una activity (ciclo di vita)
onCreate : evento richiamato al momento del caricamento in memoria
onDestroy : Un'activity viene distrutta quando viene terminata esplicitamente mediante il metodo
finish oppure “quando è stata chiusa dal pulsante Back del dispositivo e in seguito il Sistema
Operativo l‟ha rimossa dalla memoria per fare spazio al lancio di una nuova applicazione”.
onPause : questo evento si verifica quando una nuova activity viene eseguita e l‟activity corrente viene
messa in uno stato di pausa (equivalente allo stato di ready del controllore dei processi)
onResume : richiamata quando che l‟Actiity ritorna in primo piano
Questi eventi sono utilizzati per attivare/terminare servizi che devono eseguiti quando l'activity è in uso
onStop : Quando si usa il pulsante Back del dispositivo, l‟Activity è come se venisse chiusa (con
cancellazione dei suoi dati), però in realtà non viene deallocata dalla memoria fino a quando c‟è
memoria disponibile. Questo per evitare di doverla ogni volta ricaricare. Cioè l‟Activity non compare
tra le activity “attive” ma viene comuqnue mantenuta in memeoria. Nel momento in dovesse essere
riavviata non viene più generato l‟evento onCreate ma l‟evento onRestart seguito da onStart
Quando non c‟è più ram disponbile, il so chiude automaticamente l‟applicazione in fondo allo stack .
Viceversa il metodo finish() forza la de-allocazione dalla memoria.
pag 28
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Gestione dei thread
Per realizzare un Timer si possono utilizzare 2 diverse soluzioni.
Prima soluzione: Utilizzo di un thread generico
Occorre istanziare due oggetti:
 un oggetto Runnable dove Runnable è una classe Astratta la cui istanza deve implementare il
metodo Run, che è il metodo che consente di eseguire una procedura su un thread separato
 un oggetto Handler che è un oggetto contenuto nel namespace osHandler che consente di
controllare il thread (avvio / arresto).
 La procedura invocata all‟interno di un thread separato può comunque accedere ai controlli
dell‟interfaccia grafica.
private Handler threadHandler;
final long intervallo = 4000;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
threadHandler = new Handler();
}
public void btnAvvia_Click(View v){
// Avvio del thread che verrà avviato fra 4 secondi
// Si aspetta come primo parametro una ISTANZA della classe Runnable
threadHandler.postDelayed(aggiornaGrafica, intervallo);
}
Terminate le istruzioni da eseguire il thread si arresterebbe, ma si può inserire un comando di riavvio.
private Runnable aggiornaGrafica = new Runnable() {
@Override
public void run() {
Toast t =Toast.makeText(MainActivity.this, "tick", toast.LENGTH_SHORT)
t.show();
threadHandler.postDelayed(this, intervallo);
}
};
public void btnArresta_Click(View v){
// Arresto del thread
threadHandler.removeCallbacks(aggiornaGrafica);
}
L‟istanza aggiornaGrafica può essere generata anche dentro onCreate, però in tal caso occorre
portare dentro onCreate anche i Listener di ascolto Avvia_Click e Arresta_Click (e, a questo punto,
anche le 2 variabili esterne).
Seconda soluzione: Utilizzo dell’oggetto Timer
Molto più complessa della precedente. Anche in questo caso occorre istanziare due oggetti:
 un oggetto Timer che consente di eseguire una certa procedura a intervalli regolari
 un oggetto TimerTask che contiene la procedura da eseguire
pag 29
Tecnologie - Classe Quinta
robertomana.it
Java per Android
MyTimerTask myTask = new MyTimerTask();
Timer myTimer = new Timer();
// Avvio del timer
myTimer.schedule(myTask, 1500, 3000);
// Ritardo del 1° avvio, interval
// Arresto del timer
myTask.cancel();
class MyTimerTask extends TimerTask {
public void run() { txtOutput.setText(Integer.toString(nCounter)); }
}
Android Device Monitor
DDMS = Dalvik Device Monitor Service è un software che funge da terminale grafico verso l‟emulatore
dello smartphone. E‟ come se ci fosse un computer connesso tramite USB allo smartphone.
IntelliJ / Tools / Android / Android Device Monitor
Selezionare il device connesso nell’elenco di sinistra.
Finestra File Explorer
Consente di accedere al file system dello smatphone.
Le cartelle dati a cui l‟utente ha accesso in scrittura sono sostanzialmente due:
Data (memoria interna)
SdCard (memory card esterna)
Le app caricate all‟interno dello smartphone sono posizionate nella cartella Data / App
Dentro Data si può creare una cartella Music in cui salvare ad esempio files.mp3.
Per creare una nuova cartella si usa il pulsante + in alto destra.
Per caricare i files è sufficiente il trascinamento oppure il pulsante in alto a destra prima del
+
Finestra Emulator Control
Consente ad esempio di impostare un valore simulato delle attuali coordinate GPS.
Gestione dei Text File
L’oggetto File
Restituisce un oggetto File associato al file avente il path indicato. Occorre sempre specificare il path
completo, altrimenti viene assunto come path quello di root generando un errore di protezione.
I files vengono salvati nella cartella data / data / path dove
String path = this.getFilesDir().getPath().toString() + "/myFile.txt";
pag 30
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Scrittura e Lettura del file
public void btnSalvaClick(View v) {
try{
// true = append;
false=overwrite (default);
FileWriter writer = new FileWriter(path, true)
writer.write("Lorem Ipsum\r\n");
writer.flush();
writer.close();
toast.setText("File Salvato Correttamente");
toast.show();
}
catch (Exception ex) {
toast.setText(ex.getMessage());
toast.show();
}
public void btnLeggiClick(View v) {
try{
FileReader reader = new FileReader(path);
BufferedReader br = new BufferedReader(reader);
String s, testo="";
// readline elimina il \r\n finale
while((s=br.readLine()) != null)
testo += s + "\r\n";
br.close();
reader.close();
toast.setText(testo);
toast.show();
}
catch (Exception ex) {
// pokemon exception ! gotta catch „em all
toast.setText(ex.getMessage());
toast.show();
}
Per vedere se un file esiste ed è effettivamente un file e non una directory:
File myFile = new File(path);
if(myFile.isFile())
FileReader reader = new FileReader(myFile);
else
myFile.createNewFile();
Per leggere il contenuto di una cartella :
File dir = new File(path);
String [] vect = dir.list();
External Storage
E‟ anche possibile salvare i dati su una scheda SD esterna. A tal file occorre aggiungere all‟interno del
file Manifest (direttamente nel tag principale <manifest>) la seguente riga:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Il path da indicare per l‟accesso al file sarà il seguente :
String filePath = “/sdcard/filename.txt";
Essendo la SD rimuovibile, se si effettua il salvataggio di dati sulla SD è opportuno controllare che i
dati salvati non siano indispensabili per il corretto funzionamento dell'applicazione.
pag 31
Tecnologie - Classe Quinta
robertomana.it
Java per Android
L’oggetto FileOutputStream
In Java, in alternativa agli oggetti FileWriter e FileReader, raccomandati nel caso di stream
testuali, esiste anche un altro oggetto raccomandato nel caso di stream binari che vengono letti byte a
byte senza attenzione all‟interpretazione dei dati.
Nota: A differenza del caso precedente, questa volta nomeFile non è il path completo, ma il semplice
filename.
FileOutputStream fs = openFileOutput(nomeFile, Context.MODE_PRIVATE);
fs.write(myVar.getBytes());
fs.close();
Qualsiasi variabile viene salvata su file in forma binaria byte per byte. Questa tecnica potrebbe essere
utilizzata per creare i vecchi files dati, ma con estrema difficoltà. Molto più semplice convertire in testo
ed usare files testuali gestiti tramite i due oggetti precedenti.
Lettura
File f = getBaseContext().getFileStreamPath(nomeFile);
String ris = "";
if (f.exists()) {
try {
FileInputStream fs = openFileInput(nomeFile);
// Legge tanti bytes quanto è grande tmp
byte[] tmp = new byte[1];
while (fs.read (tmp) != -1)
ris = ris + new String(tmp,"UTF-8");
fs.close();
}
Diritti di accesso
Di default questo tipo di salvataggio è un salvataggio privato. Il file può essere letto e scritto
esclusivamente dall'applicazione che lo ha creato e non da altre applicazioni. Per potere accedere al
file anche da altre applicazioni su può utilizzare una delle seguenti costanti:
Context.MODE_WORLD_READABLE: viene consentito l'accesso in lettura a tutte le applicazioni
Context.MODE_WORLD_WRITEABLE: viene consentito l'accesso in scrittura a tutte le applicazioni
Shared Preferences
Le SharedPreferences vengono salvate all‟interno di un‟area di memoria comune in cui ogni App può
salvare delle variabili in formato chiave/valore, variabili che la App può leggere in qualsiasi momento.
Shared nel senso che sono condivise tra tutte le Activity. Ottime per salvare le preferenze dall‟utente.
Le SharedPreferences vengono salvate in un file xml in the app data folder, i.e.
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml
Il metodo getSharedPreferences ritorna la Shared Preference indicata dal primo parametro.
Se non esiste la crea. Il secondo parametro è lo stesso di prima ed indica i diritti di accesso.
Si usa sempre MODE_PRIVATE , i valori _READABLE e _WRITEABLE sono stati deprecati.
pag 32
Tecnologie - Classe Quinta
robertomana.it
Java per Android
public void btnSalva_click(View v){
SharedPreferences myPrefs =getSharedPreferences("Shared1",MODE_PRIVATE)
SharedPreferences.Editor editor = myPrefs.edit();
editor.putString("nome", "pippo");
editor.putString("cognome", "pluto");
editor.putInt("eta", 18);
editor.remove("cognome");
editor.commit(); // Salva su file
public void btnLeggi_click(View v){
SharedPreferences myPrefs=getSharedPreferences("Shared1",MODE_PRIVATE)
String nome = myPrefs.getString("nome", "Nessun valore");
String cognome = myPrefs.getString("cognome", "Nessun valore");
int eta = myPrefs.getInt("eta", 0);
TextView lbl = (TextView) findViewById(R.id.lblStato);
String s = "nome=" +nome + "\n";
s += "cognome=" +cognome + "\r\n";
s += "eta=" + Integer.toString(eta) + "\r\n";
lbl.setText(s);
L‟oggetto Editor consente di modificare le singole variabili di un gruppo di SharedPreferences.
List<String>
e
ArrayList<String>
List è una interfaccia,e dunque non può essere istanziata direttamente, ma può ad esempio essere
restituita da un metodo che esegue il cast da ArrayList. ArrayList viceversa è una classe che
implementa l‟interfaccia List e dunque può essere istanziata. Ci sono anche altre classi che
implementano l‟interfaccia List in modo diverso da ArrayList, ad es la classe LinkedList.
// Creazione di un ArrayList di Stringhe
ArrayList<String> list = new ArrayList<String>();
// <String> è il default per cui può anche essere omesso
// Popolamento dell’ArrayList
list.add("Fossano");
list.add(pos, "Saluzzo"); // int pos = 0;
String[] vect = new String[] { "Cuneo", "Alba", "Genola", "Bra" };
list.addAll(Arrays.asList(vect));
list.addAll(list2);
// Scansione dell’ArrayList
for (int i = 0; i<list.size(); i++)
s += list.get(i) + "\r\n";
// Cancellazione elementi
list.clear();
list.remove(pos);
pag 33
Tecnologie - Classe Quinta
robertomana.it
Java per Android
ArrayAdapter
Per il caricamento dei dati di un ArrayList in un Listiew, occorre utilizzare un Adapter che si occupi di
associare il dato corretto in relazione al layout grafico definito per la visualizzzione delle singola righe.
Si chiama Adapter in quanto funziona proprio come adattatore tra due elementi: i dati e il layout.
Esistono numerose varianti di questi "adattatori" a seconda della natura dei dati da mostrare. Uno dei
più usati è l'ArrayAdapter, che permette di gestire dei dati memorizzati sotto forma di array.
Al costruttore dell'ArrayAdapter occorre passare:
1. Il contesto corrente
2. il nome del layout da utilizzare per la visualizzazione delle singole righe
3. il nome dell'ArrayList che funge da sorgente dati. Invece di un ArrayList, è possibile passare
direttamente un vettore di stringhe. L‟ArrayList però, rispetto al vettore statico di stringhe,
è estensibile, cioè in qualunque momento è possibile aggiungere nuovi elementi.
// Istanza dell’Adapter e associazione con la sorgente dati
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, list);
android.R.layout.simple_list_item_1 è un layout predefinito che permette di gestire un solo
elemento di testo. E‟ possibile creare layout personalizzati contenenti la rappresentazione di ogni
singola riga oppure, nel caso più semplice, un solo TextView eventualmente personalizzato. Può però
contenere più TextView combinati in modo da mostrare più campi / img contemporaneamente.
lstView lstCitta = (lstView) findViewById(R.id.lstCitta);
// Link del ListView all’Adapter
lstView.setAdapter(adapter);
lstCitta.setOnItemClickListener(listener);
adapter.notifyDataSetChanged(); // refresh
ListView
In Android le liste vengono usate per rappresentare dei dati una riga sotto l'altra con la possibilità di
effettuare il classico scrolling per posizionarsi in fondo e in cima. E possibile rendere i singoli
elementi della lista sensibili al tocco in modo da accedere al dettaglio di una notizia o di un prodotto.
Una volta trascinato sul Layout il controlli ListView si possono definire le seguenti proprietà:
android:choiceMode="singleChoice"
// Seleziono un solo element alla volta
android:listSelector=blue"
// Colore dell’elemento selezionato
android:fastScrollAlwaysVisible="true" // Barra scorrimento sempre visibile
Se non si specifica il listSelector, non viene usato alcun evidenziatore ma semplicemente, al
momento del click, l‟elemento selezionato si ricolora per un attimo di blu e poi ritora subito grigio.
Accesso ai dati della lista
lstCitta.getItemAtPosition(i).toString();
lstCitta.getCount();
// restituisce il numero di elementi contenuti nel ListBox
pag 34
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Scrolling del List View
lstCitta.setSelection(2);
// La lista viene visualizzata a partire dall‟elemento indicato.
Per eseguire lo scrolling della ListView è sufficiente clickare su una voce e poi trascinare.
Per eseguire lo scrolling da codice : lstCitta.setSelection(index); La lista verrà visualizzata
a partire dall‟elemento avente l‟indice index (ovviamente a base 0).
La direttiva di layout android:scrollbars= "vertical" è già definita di default.
All‟atto del trascinamento compare automaticamente sulla destra una piccola scroll bar,
Evento onItemClick
Il metodo onItemClick riceve come parametri
parent
riferimento all‟Adapter che ha scatenato l‟evento
v
riferimento al controllo associato all‟adapter (ListView)
position
posizione dell‟elemento su cui si è verificato il clcik
id
id del controllo associato all‟adapter
ListView.OnItemClickListener listener = new ListView.OnItemClickListener()
AdapterView.OnItemClickListener lis =new AdapterView.OnItemClickListener()
{
public void onItemClick(ListView<?> parent, View v, int pos, long id) {
String item = (String) parent.getItemAtPosition(pos);
show(item);
}
};
Spinner
Implementa il tipico Combo Box. Il funzionamento è praticamente identico rispetto al ListView.
Occorre definire una sorgente dati di tipo ArrayList ed un apposito adapter.
ArrayList<String> list = new ArrayList<String>();
ArrayAdapter<String> adapter = new ArrayAdapter<String>( this,
android.R.layout.simple_spinner_item, list);
cmbElenco.setAdapter(adapter);
adapter.notifyDataSetChanged();
cmbElenco.setOnItemSelectedListener(new Spinner.OnItemSelectedListener() {
public void onItemSelected(Spinner<?> parent, View v, int pos, long id)
{
String item = (String) parent.getItemAtPosition(pos)
show(item);
}
public void onNothingSelected(AdapterView<?> arg0) {
}
});
pag 35
Tecnologie - Classe Quinta
robertomana.it
Java per Android
GridView
Le GridView operano in modo simile rispetto alle ListView. Per la GridView è possibile ripetere lo
stesso identico esercizio precedente. I dati del vettore verranno spalmati attraverso le varie colonne.
Dal layout l‟unica cosa da definire è il numero di colonne: android:numColumns="3"
E‟ anche possibile definire android:numColumns="auto_fit" specidiando però columnWidth
final GridView grid = (GridView) findViewById(R.id.grid);
grid.setAdapter(adapter);
Anche la procedura di risposta al click è la stessa identica di cui sopra. La posizione restituita è una
posizione vettoriale che si incrementa partendo da 0 e scorrendo le righe. I vari campi dei dati da
visualizzare devono però essere caricati sequenzialmente all‟interno del vettore.
Creazione di un Adapter personalizzato
Per la visualizzazione dei dettagli, è possibile utilizzare un TableLayout costruito dinamicamente,
oppure è possibile creare un adapter personalizzato basato su un apposito Layout personalizzato in
base alla struttura del record da visualizzare.
La soluzione basata su TableLayout è più semplice da implementare, mentre la soluzione basata su
adapter personalizzato è più strutturata in quanto demanda tutta la gestione dei dati all‟interno della
classe di definizione dell‟adapter.
L‟Adapter personalizzato può ereditare dalla classe BaseAdapter (Adapter base, occorre però eseguire
una implementazione abbastanza complessa) oppure, più semplicemente, può ereditare dalla classe
ArrayAdapter<Type> nel qual caso deve implementare soltanto il metodo
getView(int position, View layout, ViewGroup parent)
dove I tre parametri rappresentano rispettivamente:
 la posizione (all‟interno della Sorgente Dati) dell‟elemento che si vuole visualizzare sul layout
 un riferimento al layout personalizzato per la visualizzazione della riga (che il primo giro è null)
 un riferimento al layout genitore (layout principale)
Il metodo ritorna un riferimento al layout in modo che nei giri successivi il riferimento possa essere
passato come secondo parametro al metodo stesso, evitando così di re-istanziarlo ogni volta.
Questo metodo viene richiamato automaticamente dal Run Time per caricare le righe all‟interno del
Layout. Cioè nel momento in cui si istanzia un Adapter associandolo ad una certa sorgente dati, il Run
Time provvede automaticamente a scorrere una ad una tutte le righe della sorgente dati e, in
corrispondenza di ogni riga, provvede a richiamare il metodo getView() dell‟Adapter per aggiungere
la riga all‟interno dell‟Adapter stesso.
public class MyAdapter extends ArrayAdapter<Studente> {
private final Context context;
private final List<Studente> list;
public MyAdapter(Context context, List<Studente> list) {
super(context, R.layout.rowlayout, list);
this.context = context;
this.list = list;
}
pag 36
Tecnologie - Classe Quinta
robertomana.it
Java per Android
public View getView(int position, View layout, ViewGroup parent) {
View v = null;
if (layout == null) {
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.rowlayout, parent, false);
}
else
v = layout;
TextView txtId =(TextView) v.findViewById(R.id.txtId);
TextView txtNome = (TextView) v.findViewById(R.id.txtNome);
TextView txtCitta = (TextView) v.findViewById(R.id.txtCitta);
Studente studente = list.get(position);
txtId.setText(Integer.toString(studente.getId()));
txtNome.setText(studente.getNome());
txtCitta.setText(studente.getCitta());
return v;
}
}
L'inflating è il meccanismo che permette di istanziare un'oggetto a partire da una risorsa XML.
La IF serve ad evitare che l‟oggetto relativo al rowlayout venga re istanziato ad ogni giro
Activity Principale
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.main);
ListView lstStudenti = (ListView) findViewById(R.id.listView);
// 1) Creazione di un ArrayList
ArrayList<Studente> list = new ArrayList<Studente>();
// 2) Popolazione dell’ArrayList
list.add (new Studente(1, "pippo", "cuneo"));
list.add (new Studente(2, "pluto", "genola"));
list.add (new Studente(3, "minnie", "alba"));
// 3) Istanza dell’adapter personalizzato
MyAdapter adapter = new MyAdapter(this, list);
// 4) Link del List View all'Adapter
lstStudenti.setAdapter(adapter);
Il Listener è lo stesso del ListView base, però questa volta getItemAtPosition(pos) restituisce un
Object (lo studente corrente) invece che una semplice String. pos rappresenta il numero di riga.
lstStudenti.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int pos, long id)
final Object item = lstStudenti.getItemAtPosition(pos);
Studente studente = (Studente) item;
Toast t = Toast.makeText(MyActivity.this, "", Toast.LENGTH_LONG);
t.setText("posiz=" + pos + " nome=" + studente.getName());
t.show();
}
pag 37
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Menù
Ogni applicazione può possedere un menù attraverso il quale l'utente possa scegliere tra le diverse
operazioni da eseguire. Ci sono due tipi di menù: OptionsMenu e ContextMenu.
OptionsMenu
Questi menù vengono visualizati tramite la pressione del tasto Menù del dispositivo (pulsantino in basso
a sinistra), Vengono visualizzati nella parte bassa dello schermo e scompaiono automaticamente dopo il
click su una delle voci oppure dopo un altro click sullo stesso pulsante Menù.
Sono associati ad una singola Activity ed espongono le operazioni più importanti che un utente può
eseguire relativamente a quella Activity. Sono detti anche icon menu in quanto di solito viene inserita in
ciascuna voce una icona che sintetizza l‟operazione effettuata in corrispondenza del click.
Nelle versioni API più vecchie questi menù avevano un numero massimo di voci visualizzabili pari a
sei. In caso di ulteriori voci semplicemente la sesta voce del menù veniva automaticamente mutata in
una voce che, se cliccata, apriva un expanded menu contenente le altre voci.
API Level 13
In Android 4 le voci vengono visualizzate in verticale una sotto l‟altra (con allineamento a fondo pagina)
In Android 5 l‟apertura del Menù viene eseguita mediante un pulsantino “con tre puntini” in alto a destra
Evento OnCreateOptionMenu
In corrispondenza del click sul pulsante Menù viene automaticamente generato l‟evento
OnCreateOptionMenu a cui viene passato come parametro un riferimento menu che punta alle voci
di menu da visualizzare. Di default il menu è vuoto e si può utilizzare questo evento per creare un
menù personalizzato. Il metodo restituisce un valore booleano: true avrà l'effetto di notificare all'activity
di mostrare il menu, mentre false avrà l'effetto contrario (il menù non verrà visualizzato).
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, 1, 1, "Voce 1");
menu.add(Menu.NONE, 2, 2, "Voce 2");
MenuItem menu3 = menu.add(Menu.NONE, 3, 3, "Voce 3");
menu3.setIcon(R.drawable.icon);
// Non sembra supportato
menu3.setCheckable(true);
SubMenu menuOpzioni = menu.addSubMenu(Menu.NONE, 8, 8,"Opzioni");
menuOpzioni.add(GROUP1, OPZIONE1, 1, "Opzione 1");
menuOpzioni.add(GROUP1, OPZIONE2, 2, "Opzione 2");
pag 38
Tecnologie - Classe Quinta
robertomana.it
Java per Android
// setGroupCheckable(int group, boolean checkable, boolean exclusive
menu8.setGroupCheckable(GROUP1,true,true);
return true;
}
Parametri del metodo add
menu.add(int groupId, int itemId, int order, CharSequence title)




groupId è un parametro che indica il gruppo di appartenenza della voce del menu
itemId indica l'id da assegnare alla voce del menù
order specifica la posizione della voce all'interno del menù (menu.NONE = ordine naturale).
title specifica la stringa di testo che verrà mostrata all'utente.
Il gruppo in realtà non ha alcuna funzione visuale, ma serve semplicemente a creare un gruppo logico
al quale si possono applicare i metodi setGroupCheckable, setGroupEnabled, setGroupVisible
Valore Restituito: Il metodo .add restituisce un riferimento all‟item appena aggiunto
MenuItem v1 = menu.add(Menu.NONE, 1, 1, "voce1");
v1.setIcon(R.drawable.icon);
Proprietà dell’oggetto menuItem
getTitle (restitusice il testo dell‟item),
setIcon, setEnabled, setVisible, setCheckable, setChecked,
Evento onOptionsItemSelected di risposta al click
In corrispondenza del click su una voce del Menù viene automaticamente generato l‟evento
onOptionsItemSelected che riceve come parametro un riferimento all‟item che ha generato
l‟evento.
Toast toast = null;
switch(item.getItemId()){
case 1:
toast = Toast.makeText(this, "premuto item1", Toast.LENGTH_SHORT);
break;
case 2:
toast = Toast.makeText(this, "premuto item2", Toast.LENGTH_SHORT);
break;
case OPZIONI:
break;
case 3:
case OPZIONE1:
case OPZIONE2:
if (item.isChecked()) item.setChecked(false);
else item.setChecked(true);
break;
default:
toast = Toast.makeText(this, "altro item", Toast.LENGTH_SHORT);
break;
}
if (toast!=null)
toast.show();
return true;
pag 39
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Creazione statica di un OptionsMenu
Invece di aggiungere dinamicamente le voci, un OptionsMenu può anche essere creato staticamente
aggiungendo all‟interno della cartella delle risorse un nuovo file menu.xml.
Click destro su res / new / Android resource file / tipo=menu
Indipendentemente dal nome del file verrà creato un nuovo oggetto XML denominato menù. A
differenza del layout, all‟oggetto menu non è associato un designer, per cui occorre lavorare
direttamene sul file xml impostando gli stessi attributi dell‟esempio precedente cioè id e title.
<group android:id = "@+id/gruppo01">
<item android:id = "@+id/item01" android:title="voce1"> </item>
<item android:id = "@+id/item02" android:title="voce2"> </item>
</group>
<item android:id = "@+id/item04" android:title="voce4"> </item>
Evento OnCreateOptionMenu
In questo evento, generato quando l‟utente clicka sul pulsante Menu, occorre semplicemente
„collegare‟ il parametro menu ricevuto dall‟evento con il menu creato staticamente all‟interno del file
xml. A tal fine occorre utilizzare l‟oggetto MenuInflater.
public boolean onCreateOptionsMenu (Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
Evento onOptionsItemSelected di risposta al click
Stesso di prima
ContextMenu
Il ContextMenu è simile al menù associato al tasto destro del mouse su un controllo Windows. Il
ContextMenu associato ad un elemento compare in corrispondenza del click prolungato sull'elemento
Per associare due diversi ContextMenu ad un Button btn ed un TextView lbl si può scrivere :
public void onCreate(Bundle savedInstanceState) {
this.registerForContextMenu(btn);
this.registerForContextMenu(lbl);
}
this.registerForContextMenu fa si che, in corrispondenza del click sul controllo, venga
automaticamente generato l‟evento onCreateContextMenu nel quale viene creato il menu.
public void onCreateContextMenu(ContextMenu menu, View v, menuInfo) {
if(v.getId() == R.id.btn1) {
menu.setHeaderTitle("ContextMenu del button");
menu.add(Menu.NONE, 1, 1, "Voce bottone 1");
menu.add(Menu.NONE, 2, 2, "Voce bottone 2");
}
else if(v.getId() == 2) {
// textView
menu.setHeaderTitle("ContextMenu della TextView");
menu.add(Menu.NONE, 3, 1, "Voce TextView 1");
menu.add(Menu.NONE, 4, 2, "Voce TextView 2");
}
Per poter associare un comportamento specifico alla pressione di uno dei tasti del menù si opera come
per gli OptionMenu, utilizzando il metodo onContextItemSelected(MenuItem item).
pag 40
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Utilizzo di XML in Java
Parsing di una stringa xml
String xml = ………………………;
DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = builder.newDocumentBuilder();
Document xmlDoc = docBuilder.parse(“myFile.xml”);
Document xmlDoc = docBuilder.parse(new InputSource(new StringReader(xml)));
Il metodo docBuilder.parse() si aspetta come parametro :
 un File XML
 oppure un InputStream
 oppure un oggetto InputSource che è un stream testuale „specializzato‟, cioè in grado di
gestire anche i caratteri speciali. Partendo da una Stringa, occorre convertirla in un oggetto
StringReader (che è semplicemente uno stream testuale) e poi in InputSource.
Restituisce il DOM corrispondente al parametro ricevuto.
Sintatticamente accetta come parametro una semplice stringa, però questa stringa deve essere un
URI, cioè sostanzialmente il path di un documento
Serializzazione di un XML Document
XMLSerializer ser = new XMLSerializer();
ser.setCharOutputStream(new FileWriter("myFile.xml"));
ser.serialize(xmlDoc);
// oppure
XMLSerializer ser = new XMLSerializer();
StringWriter sw = new StringWriter();
ser.setCharOutputStream(sw);
ser.serialize(xmlDoc);
String xml = sw.toString();
// oppure
StringWriter writer = new StringWriter();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.transform(new DOMSource(xmlDoc), new StreamResult(writer));
String xml=writer.toString();
I vari tipi di Nodo
L‟oggetto Node è la struttura più generale da cui ereditano le seguenti classi:
Document: Estende Node per rappresentare (la radice di) un documento. Un Node non può essere
creato se non viene creato un Document
Element: Estende Node per rappresentare un elemento
Attr: Estende Node per rappresentare un attributo
Text: Estende Node per rappresentare una sezione #PCDATA (nodo testuale o foglia).
pag 41
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Questi vari tipi di Node sono definiti mediante le seguenti costanti:
1 = Node.ELEMENT_NODE = nodo vero e proprio
2 = Node.ATTRIBUTE_NODE = attibuto
3 = Node.TEXT_NODE = nodo testuale, cioè foglia dell‟albero (valore di un nodo ELEMENT)
8 = Node.COMMENT_NODE = commento
9 = Node.DOCUMENT_NODE = l‟intero xmlDoc subito dopo il parsing
Accesso alla radice dell’albero
Node
Node
Node
Node
root
root
root
root
=
=
=
=
xmlDoc.getDocumentElement(); (Restituisce un Element)
xmlDoc.getChildNodes().item(0);
xmlDoc.getElementsByTagName("bookstore")[0];
xmlDoc.getElementById(String elementId);
Metodi per la navigazione dell’albero
Restituiscono come risultato sempre un generic oggetto di tipo Node di cui occorrerà eventualmente
eseguire il cast
.getChildNodes();
.getFirstChild();
.getLastChild();
.getNextSibling();
.getPreviousSibling();
.getParentNode();
.getOwnerDocument()
collezione (NodeList) dei figli del nodo corrente
primo figlio del nodo corrente
ultimo figlio del nodo corrente
prossimo fratello del nodo corrente
fratello precedente
padre del nodo corrente
restituisce l‟oggetto Document contenente il nodo
Esempio
Node root = xml.getDocumentElement();
root.normalize();
if (xml.hasChildNodes()) {
Node node = root.getFirstChild();
while (node != null) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
list.add(node.getTextContent());
node = node.getNextSibling();
}
}
// oppure accedo direttamente al vettore dei nomi
// NodeList nodi = doc.getElementsByTagName("nome");
}
Il metodo .normalize() serve ad eliminare eventuali empty node terminali (whitespaces).
Principali Metodi dell’oggetto NodeList
int getLength()
Node item(int index)
(restituita da .getChildNodes())
numero dei nodi
accede all‟elemento iesimo (a base 0)
pag 42
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Altri Metodi dell’oggetto Node
boolean hasChildNodes()
verifica la presenza di nodi figli
short getNodeType()
restituisce il tipo del nodo (Element ,Text, Attr)
String getNodeName()
restituisce il nome di un TAG di tipo Element
setNodeName(String)
imposta il nome di un TAG di tipo Element
String getNodeValue()
restituisce il contenuto di un nodo di tipo Text (foglia)
setNodeValue(String)
imposta il contenuto di un nodo di tipo Text (foglia)
String getTextContent()
restituisce il contenuto testuale (foglia) di un Element Node
setTextContent (String)
imposta il contenuto testuale di un Element Node
(in pratica consente di associare direttamente una foglia ad un nodo, come InnerText di C#)
Principali Metodi di un nodo di tipo Text
void setData(String data) imposta il contenuto del nodo
String getData() restituisce il contenuto del nodo
Principali Metodi di un nodo di tipo Element
String getTagName() restituisce il nome dell’elemento
NodeList getElementsByTagName(String name) restituisce i sotto-nodi con un certo tag
Principali Metodi per la gestione degli attributi di un oggetto Node
boolean hasAttributes()
verifica la presenza di attributi
boolean hasAttribute(String name)
verifica la presenza di un attributo
String getAttribute(String name)
restituisce il valore di un attributo
void setAttribute(String name, String value) imposta il valore di un attributo
Attr attr getAttributeNode(String name) restituisce un certo attributo come Object
void setAttributeNode(Attr newAttr)
aggiunge un nuovo attributo
String attr.getName() restituisce il nome dell‟attributo
String attr.getValue() restituisce il valore dell‟attributo
void attr.setValue(String value) imposta il valore dell‟attributo
Element attr.getOwnerElement() restituisce l‟elemento che contiene l‟attributo
boolean attr.getSpecified() verifica se l‟attributo è stato specificato esplicitamente, oppure
assume il suo valore di default
NamedNodeMap attrList getAttributes() restituisce la lista degli attributi del nodo
int attrList.getLength(); Numero di attributi
Node attrList.item(i)
Attributo con indice i (a base 0)
Node attrList.getNamedItem(attributeName);
Node attrList.removeNamedItem(attributeName);
Void attrList.setNamedItem(Node node); aggiunge, o sostituisce se presente, un nodo
all‟insieme
pag 43
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Metodi dell’oggetto Document per la creazione di nuovi nodi
Element createElement(String tagName) crea un elemento con un tag
Text createTextNode(String data) crea una sezione #PCDATA (foglia) specificando il contenuto
Attr createAttribute(String name) crea un attributo con un dato nome
Esempi:
Element node = xmlDoc.createElement("nodeName");
root.appendChild(node);
node.setTextContent("valore");
// oppure
Text foglia = xmlDoc.createTextNode("salve mondo");
node.appendChild(foglia);
// Nodo di tipo foglia
Metodi per l’aggiunta / rimozione dei nodi
parentNode.appendChild(Node) Appende in coda un nuovo nodo
parentNode.insertBefore(newNode, childNode) aggiunge newNode davanti a childNode
parentNode.removeChild(childNode) Elimina childNode, lui e tutti gli eventuali nipoti, di
qualunque ordine e grado
Esempio di creazione di un nuovo albero
<A id="aa">
<B/>
<C>text</C>
</A>
Document doc = docBuilder.newDocument();
Element elemA = doc.createElement("A");
elemA.setAttribute("id","aa");
doc.appendChild(elemA);
Element elemB = doc.createElement("B");
elemA.appendChild(elemB);
Element elemC = doc.createElement("C");
Text text = doc.createText("text");
elemC.appendChild(text);
elemA.appendChild(elemC);
pag 44
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Accesso a SQLite
Esistono diversi modi per creare un database, ma quello più utilizzato è quello di estendere la classe
SQLiteOpenHelper e poi effettuare un overriding del metodo onCreate per poter creare le tabelle che
rappresenteranno la struttura del database.
public class SqliteHelper extends SQLiteOpenHelper {
private static Context context = null;
private static final String[] ALL_TABLES = { "studenti", "insegnanti" };
private static final String CREA_TABELLA_STUDENTI = "CREATE TABLE if not
exists studenti (id integer not null primary key, nome TEXT, email TEXT);";
private static final String CREA_TABELLA_INSEGNANTI ="CREATE TABLE if not
exists insegnanti (id integer not null primary key, nome TEXT, email TEXT);";
// costruttore
SqliteHelper(Context context, String dbName) {
// L'ultimo parametro rappresenta la versione iniziale del database
super(context, dbName, null, 1);
this.context = context;
}
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(CREA_TABELLA_STUDENTI);
db.execSQL(CREA_TABELLA_INSEGNANTI);
showToast("CREATING....");
}
catch (Exception ex) { showToast (ex.getMessage());
}
}
public void onUpgrade(SQLiteDatabase db, int oldVers, int newVers) {
// ricrea tutte le tabelle presenti nel database
for (String s : ALL_TABLES) db.execSQL("DROP TABLE IF EXISTS " + s);
onCreate(db);
}
public class Sqlite {
public static void init(Context context) {
// Se il db non esiste viene creato generando l'eveto onCreate
if (sqliteHelper == null)
sqliteHelper = new SqliteHelper(context, DATABASE_NAME );
}
/* Open database for insert,update,delete in syncronized manner */
private static synchronized SQLiteDatabase open() {
return sqliteHelper.getWritableDatabase();
}
La parola chiave syncronized, utilizzabile anche all‟interno di una classe davanti ad un blocco di
istruzioni, fa si che il blocco di istruzioni racchiuse venga eseguito da un solo thread alla volta. In
pratica realizza un specie di semaforo. Nel caso del database fa sì che il db venga aperto in modo
sincrono, nel senso che un solo thread alla volta potrà accedere al database. Eventuali Thread
successivi verranno accodati al semaforo (un po‟ come il db.serialize() di Node.js). Es:
public void increment() {
synchronized (this) { count++;
}
}
pag 45
Tecnologie - Classe Quinta
robertomana.it
Java per Android
// 1) aggiungi nuovo studente : il metodo db.insert()
public static void aggiungiStudente(Studente studente) {
final SQLiteDatabase db = open();
String name = sqlEscape(studente.getNome()); Let's suppose you have a table named
foo where all columns either allow NULL
String email = sqlEscape(studente.getEmail());values or have defaults.
ContentValues record = new ContentValues();
In some SQL implementations, this would
record.put(STUDENTE_NOME, name);
be valid SQL: INSERT INTO foo;
record.put(STUDENTE_EMAIL, email);
That's not valid in SQLite. You have to
db.insert(TABELLA_STUDENTI, null, record);
have at least one column specified.
db.close();
Il secondo parametro serve a specificare
}
il nome della colonna a cui assegnare
NULL per impedire che l’operazione vada
// 2) Query di ricerca : il metodo db.query()
in errore.
public static Studente ricercaStudente (int id) {
final SQLiteDatabase db = open();
// 1=tableName, 2=arrayOfTableColumns, 3=whereClause,
4=arrayOfWhereArgs, 5=groupBy, 6=having, 7=orderBy, 8=limit;
Cursor cursor = db.query(
TABELLA_STUDENTI,
// 1
new String[] {STUDENTE_ID, STUDENTE_NOME, STUDENTE_EMAIL }, // 2
STUDENTE_ID + "=?", // 3
new String[] { String.valueOf(id) },
// 4
null, null, null, null); // 5, 6, 7, 8
Studente studente = null;
if (cursor != null) {
cursor.moveToFirst();
studente = new Studente(cursor.getString(0),
cursor.getString(1), cursor.getString(2));
}
db.close();
return studente;
}
// 3) Query di ricerca : il metodo db.rawQuery() // Query grezza
public static List<Studente> ricercaListaStudenti() {
List<Studente> list = new ArrayList<Studente>();
String sql = "SELECT * FROM " + TABELLA_STUDENTI;
final SQLiteDatabase db = open();
Cursor cursor = db.rawQuery(sql, null); // 2°= String[] parameters
if (cursor !=null) {
cursor.moveToFirst();
do {
Studente studente = new Studente();
studente.setId(Integer.parseInt(cursor.getString(0)));
studente.setNome(cursor.getString(1));
studente.setEmail(cursor.getString(2));
list.add(studente);
} while (cursor.moveToNext());
}
db.close(); return list;
Unsafe: String whereClause = "column1='" + value + "'"; // SQL injection
Safe: String whereClause = "column1=?";
pag 46
Tecnologie - Classe Quinta
robertomana.it
Java per Android
// 4) modifica studente avente id indicato : db.update()
public static int aggiornaStudente(Studente studente) {
final SQLiteDatabase db = open();
ContentValues values = new ContentValues();
values.put(STUDENTE_NOME, studente.getNome());
values.put(STUDENTE_EMAIL, studente.getEmail());
int ris = db.update(TABELLA_STUDENTI,
values,
STUDENTE_ID + " = ?",
new String[]{String.valueOf(studente.getId())});
db.close(); return ris;
}
// 5) Cancellazione di uno studente avente id indicato : db.delete()
public static void cancellaStudente(int id) {
final SQLiteDatabase db = open();
db.delete(TABELLA_STUDENTI,
STUDENTE_ID + " = ?",
new String[] { String.valueOf(id) });
db.close();
pag 47
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Accesso ad un web service HTTP
Uno dei metodi più comuni utilizzati dalle applicazioni per comunicare con il mondo esterno è quello di
accedere tramite HTTP ai web service dedicati dai quali possono scaricare svariati tipi di contenuti
Affinchè la app possa inviare richieste HTTP all‟esterno occorre registrare i permessi all‟interno del file
Manifest, creando una nuova sezione direttamente all‟interno del tag<manifest>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
L’oggetto Runnable
L‟oggetto Runnable è l‟oggetto base di java per la realizzazione dei threading. Una procedura
eseguita all‟interno di un oggetto Runnable può comunicare con il thread principale solo attraverso uno
scambio di Messages.
La classe astratta AsynkTask
L‟oggetto AsynkTask è un wrapper di Android che utilizza un oggetto Runnable per eseguire una
certa procedura in un thread secondario e gestisce la comunicazione fra il thread principale ed il thread
secondario senza costringere il programmatore ad entrare nei dettagli di livello più basso (Messages).
La classe AsynkTask è stata pensata appositamente come classe di supporto per l‟esecuzione di
thread di breve durata che al termine restituisco un risultato al chiamante.
Metodi della classe AsyncTask
String doInBackground(String... args) unico metodo effettivamente eseguito all’interno
di un thread separato. E‟ di tipo protected, quindi non visibile all‟esterno della classe e può essere
richiamato tramite il metodo pubblico execute(). E‟ l‟unico metodo di tipo MUST OVERRIDE. Gli altri
tre metodi sono metodi di evento richiamati rispettivamente in corrispondenza dell‟avvio del thread,
a tempo durante l‟esecuzione del thread, ed in corrispondenza della terminazione del thread.
I tre puntini indicano che il parametro args è una ellisse, cioè sequenza di parametri tutti dello
stesso tipo che il chiamato tratta come un vettore. Si aspetta un parametro del tipo indicato da
TParams e restituisce un risultato di tipo indicato da TResult che viene passato automaticamente
all‟evento onPostExecute
onPreExeute() richiamato nel momento in cui il thread viene avviato. Per eventuali inizializzazioni
onProgressUpdate(View v) utilizzato per riportare sulla UI lo stato di avanzamento del thread. Si
aspetta un parametro dei tipo indicato da TProgress che potrebbe essere ad esempio una progress
bar di visualizzazione dello stato di avanzamento (sulla base ad es del n. di bytes ricevuti)
onPostExecute(String result) richiamato al termine dell‟esecuzione del thread. Il parametro
result viene iniettato da AsynkTask al termine del metodo doInBackground (cioè in
corrispondenza del ricevimento dei dati dal server). Scopo di questo evento è quello di elaborare il
risultato scrivendolo all‟interno di un Object ricevuto dall‟Activity principale.
Dichiarazione di una classe che eredita da AsyncTask
La classe astratta AsynkTask è una Typed Class definita da tre tipizzazioni:
AsyncTask<TParams, TProgress, TResult>
pag 48
Tecnologie - Classe Quinta
robertomana.it
Java per Android
TParams rappresenta il tipo delle variabili che il chiamante può passare al metodo execute (che a sua
volta richiama doInBackground). Le variabili devono essere tutte dello stesso tipo e vengono
passate attraverso una ellissi. TParams seve appunto a tipizzare questi parametri.
Se non si vogliono passare parametri TParams sarà Void.
TProgress rappresenta un object utilizzabile dal metodo onProgressUpdate per aggiornare il
thread principale (UI) sullo stato di avanzamento del thread. Normalmente impostato a null
TResult rappresenta il tipo del risultato restituito dal metodo doInBackground che viene
automaticamente passato all‟evento onPostExecute()
Esempi di Typed Class in C#
 La classe List <T> che ha una sola tipizzazione
Esempio: List <String> myList = new List <String>();
 La classe Dictionary <TKey, TValue> che ha due tipizzazioni; la chiave e il valore
Dictionary <int, clsLibri> myDic = new Dictionary <int, clsLibri>();
Esempio di classe che eredita da AysncTask:
public class InviaRichiestaHttp extends AsyncTask<String,Void,String> { }
cioè il metodo doInBackground dovrà ricevere come parametro un ellisse (sequenza) di stringhe e
dovrà restituire una stringa che verrà automaticamente passata a onPostExecute.
MainActivity: Istanza della classe ed invo della richiesta
L‟activity principale dovrà istanziare la classe InviaRichiestaHttp e richiamare il metodo
execute che provvede ad attivare il metodo doInBackground della classe AsynkTask.
Se l‟unico metodo utilizzato è execute() non serve nemmeno salvare il riferimento all‟interno di una
variabile
new InviaRichiestaHttp(myParam).execute(“get”, "/cercaStudente",
"nomeStudente=pippo");
Passaggio dei parametri al costruttore
Poiché nè il metodo execute() né l‟evento onPostExecute() non restituisce alcun risultato, il
chiamante dovrà provvedere a passare alla classe InviaRichiestaHttp un oggetto (tipicamente un
controllo della UI) all‟interno del quale il metodo onPostExecute possa andare a scrivere il risultato
ricevuto dal server. Questo oggetto non può essere passato direttamente
 Né al metodo execute() in quanto questo si aspetto un vettore di oggetti tutti dello stesso tipo
(tipicamente stringhe di impostazione della richiesta).
 Né all‟evento onPostExecute che ha una firma fissa con un unico parametro di tipo TResult
La tecnica allora utilizzata è quella di passare tutti gli eventuali parametri aggiuntivi al costruttore della
classe InviaRichiestaHttp che provvederà a salvarli all‟interno di appositi campi privati. In questo
modo si può passare anche il context, indispensabile se il thread deve effettuare un Toast.
E‟ anche possibile passare tutti i parametri al cotruttore senza più passarli ad execute. In tal caso nella
dichiarazione della classe tipizzata InviaHttpRequest come primo parametro si può impostare Void e
doInBackground ricevera una ellissi vuota (Void … args).
pag 49
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Nota sull’indirizzo IP
Se il server è sulla macchina stessa, come indirizzo ip del server occorre utilizzare l‟indirizzo della
scheda fisica e NON l‟indirizzo della scheda virtuale 127.0.0.1 e nemmeno localhost che è un alias di
127.0.0.1. Entrambi nel caso dell‟emulatore puntano all‟emulatore stesso che ha un suo indirizzo IP
diverso da quello della macchina fisica.
Oggetti per l’invio di una richieste HTTP
All‟interno della classe AsyncTask occorre poi utilizzare gli oggetti necesari per gestire l‟invio di una
richiesta HTTP al server e la ricezione della relativa risposta. Questi oggetti hanno tutti la caratteristica
di esporre metodi sincroni, cioè che non restituiscono il controllo fino a quando non ricevono la
risposta dal server, per cui devono essere utilizzati all‟interno di AsyncTask oppure in fase di
inizializzazione (splash screen).
Soluzione 1 : La classe DefaultHttpClient
public class InviaRichiestaHttp extends AsyncTask <String,Void,String> {
private
private
private
private
View v;
Context context;
String url = "http://10.0.202.123:8080";
String risorsa;
// costruttore
public InviaRichiestaHttp (View v, Context context)
this.v = v;
this.context = context;
}
{
protected String doInBackground(String ... args) {
String
String
String
String
url +=
ris = "";
metodo = args[0];
risorsa = args[1];
parametro = args[2];
risorsa;
DefaultHttpClient client = new DefaultHttpClient();
HttpResponse response = null;
try{
if(metodo == "get"){
if(parametro!="")
url +="?" + parametro;
HttpGet request = new HttpGet(url);
response = client.execute(request);
// sincrono
}
else if(metodo == "post")
{
HttpPost request = new HttpPost(url);
if(parametro!="") {
ArrayList<BasicNameValuePair> listaParametri =
new ArrayList<BasicNameValuePair>();
listaParametri.add(
new BasicNameValuePair("nomeStudente", parametro));
request.setEntity(new UrlEncodedFormEntity(listaParametri));
}
response = client.execute(request);
}
pag 50
Tecnologie - Classe Quinta
robertomana.it
Java per Android
// lettura risposta
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode()==200) {
InputStream is = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(
new InputStreamReader(is, "iso-8859-1"), 8);
ris =reader.readLine();
reader.close();
}
else
ris = “pagina non accessibile”;
}
catch(Exception ex){
toast(ex.getMessage());
return “”;
}
catch (Exception ex)
{
ris = "Exception: " + ex.getMessage();
}
return ris;
}
@Override
protected void onPostExecute(String responseText){
TextView txtRisultato = (TextView) v;
txtRisultato.setText(responseText);
}
URI e URL sono quasi sinonimi. URI è più generico in quanto è applicabile anche alla definizione delle risorse
tramite namespace. L’oggetto HttpGet non ha un metodo setURL ma ha solo un metodo setURI, per cui
occorre passare attraverso setURI. In alternativa si può passare la URL direttamente al costruttore.
Nota
Il codice precedente suppone che lo stream di risposta sia costituito da una sola riga.
Nel caso in cui lo stream di risposta sia costituito da più righe, si può utilizzare un ciclo while:
StringBuffer sb = new StringBuffer("");
String line="";
InputStream is = response.getEntity().getContent();
BufferedReader reader = new BufferedReader (new InputStreamReader(is));
while ((line = reader.readLine()) != null)
sb.append(line);
reader.close();
return sb.toString();
Invece dello StringBuffer si sarebbe potuta usare una semplice String, ma così è più elegante
pag 51
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Soluzione 2 : La classe URLConnection (più semplice)
Questa seconda soluzione ha il pregio di non distinguere sostanzialmente fra richieste GET e POST,
entrambe inviate tramite il comando conn.connet();. In caso di parametri post è sufficiente
aggiungerli alla connessione dopo averla aperta. Il meotdo conn.setDoOutput(true) abilita la
scrittura di dati sull connessione e trasforma la chiamata da GET a POST
protected String doInBackground(String ... args) {
String ris = "";
try{
if(metodo == "get")
url +="?nomeStudente=" + nome;
URL _url = new URL(url); // parsing
URLConnection conn = _url.openConnection();
if(metodo == "post")
{
// Aggiunta Parametri Post
conn.setDoOutput(true);
String data = URLEncoder.encode("nomeStudente", "UTF-8")
+ "=" + URLEncoder.encode(nome, "UTF-8");
// apre stream di output e scrive i parametri post
OutputStreamWriter wr = new OutputStreamWriter
(conn.getOutputStream());
wr.write(data);
wr.flush();
wr.close();
}
// invio della richiesta
conn.connect();
// lettura risposta
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
while((line = reader.readLine()) != null)
ris += line + "\n";
reader.close();
return ris;
}
Notare come le istruzioni di elaborazione della risposta si siano leggermente modificate rispetto alla
solizione precedente in quanto ora ricevono un oggetto URLConnection e non più un oggetto
HttpResponse.
Spostamento del metodo onPostExecute all’interno della classe principale
Sfruttando il fatto che in JAVA al momento dell‟istanza di una classe è possiile definire nuovi metodi
della classe stessa oppure redifinire metodi esistenti, il codice del metodo onPostExecute può essere
scritto all‟interno della MainActivity al momento dell‟istanza della classe InviaRichiestaHttp.
In questo modo si evita di dover passare al costruttore i controlli da aggiornare ed anche il context.
L‟unico parametro da passare al costruttore sarà la risorsa da richiedere e gli eventuali parametri.
Al metodo execute() non si passa nulla.
pag 52
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Parsing di uno stream jSon
Il metodo onPostExecure riceve solitamente una stringa JSON che dovrà provvedere a parsificare
andando poi a visualizzare le varie informazioni sull‟interfaccia grafica.
Gli oggetti fondamentali per parsificare una stringa JSON sono
JSONArray nel caso in cui la stringa ricevuta faccia riferimento ad un vettore enumerativo. Esempio
[ {“nome”:”pippo”, eta:16}, {“nome”:”pluto”, eta:17}, {} ]
JSONObject nel caso in cui la stringa ricevuta faccia riferimento ad un oggetto. Esempio
{studenti: [ {“nome”:”pippo”, eta:16}, {“nome”:”pluto”, eta:17}, {} ] }
JSONArray json = new JSONArray(responseText);
JSONObject json = new JSONObject(responseText);
Da un JSONObjet, tramite la chiave, si possono poi estrarre i vari campi utilizzando i seguenti metodi :
.getString(String key) Restituisce il valore stringa corrispondente alla chiave indicata
.getInt(String key) Restituisce il valore intero corrispondente alla chiave indicata
.getBoolean(String key) Restituisce il valore boolean corrispondente alla chiave indicata
.getJSONArray String key) Restituisce il vettore corrispondente alla chiave indicata
.getJSONObject String key) Restituisce l‟oggetto json corrispondente alla chiave indicata
Da un JSONArray enumerativo si può estrarre un singolo object nel modo seguente:
JSONObject studente = vect.getJSONObject(i);
Nel caso di un vettore enumerativo valgono tutti i metodi precedenti, con l‟unica differenza che, anziché
passare la chiave, occorre passare l‟indice numerico dell‟elemento all‟interno del vettore enumerativo:
String s = vect.getString(i)
Integer n = vect.getInt(i)
Boolean b = vect.getBoolean(i)
JSONObject o = vect.getJSONObject(i)
JSONArray v = vect.getJSONArray(i)
Esempio 1
Supponendo che il server invii la seguente stringa:
{"citta" : ["Cune", "Genola", "Bra"]}
Il parsing potrà essere eseguito nel modo seguente:
protected void onPostExecute(String result){
try{
JSONObject json = new JSONObject(responseText);
JSONArray citta = json.getJSONArray("citta");
for (int i = 0; i< citta.length(); i++)
listaCitta.add(citta.getString(i));
adapter.notifyDataSetChanged();
}
Notare adapter.notifyDataSetChanged() per il refresh del ListView,
pag 53
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Gestione del Layout all‟interno dell‟Activity principale
public void onCreate(Bundle savedInstanceState) {
final
final
final
final
ListView lstCitta= (ListView) findViewById(R.id.lstCitta);
TextView txtError = (TextView) findViewById(R.id.txtError);
ArrayList<String> listaCitta = new ArrayList<String>();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, listaCitta);
lstCitta.setAdapter(adapter);
InviaRichiestaHttp http = new InviaRichiestaHttp(listCitta, txtErrore, adapter)
http.execute("get", "/elencoCitta");
Esempio 2
Supponendo che il server invii la seguente stringa:
{"studenti" : [\
{"nome":"pippo", "residenza":"fossano", "eta":16}, \
{"nome":"pluto", "residenza":"fossano", "eta":18}, \
{"nome":"minnie", "residenza":"genola", "eta":17} \
]}
Il parsing potrà essere eseguito nel modo seguente:
protected void onPostExecute(String result){
try{
JSONObject jObject = new JSONObject(result);
JSONArray studenti = jObject.getJSONArray("studenti");
for (int i = 0; i< studenti.length(); i++){
JSONObject studente = studenti.getJSONObject(i);
int id = studente.getInt("id");
String nome = studente.getString("nome");
String residenza = studente.getString("residenza");
listStudenti.add (new Studente(id, nome, residenza));
}
adapterStudenti.notifyDataSetChanged();
}
catch (JSONException ex) {
txtErrore.setText("Exception: " + ex.getMessage());
}
}
Impostazione del proxy sull’emulatore della scuola
Settings
More
Mobile Networks
Access Point Names
T-Mobile US
Proxy: 10.0.5.1
Port: 3128
A casa occorre invece impostare l‟indirizzo IP del router nella voce Server della finestra precedente.
pag 54
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Services and Notification
Un Service è simile ad una Activity ma non ha un layout (demone, in windows service).
Le notification vengono visualizzate sulla barra n alto ogni volta che arriva una notifica
Le notifiche sono pending intents. Quando faccio TAP sulla notifica parte la intent relativa.
Solo i Services possono costruire notifiche, le Activity no. Lo scopo è segnalare qualcosa quando non
si ha a disposizione il monitor. Il servizio, per segnalare qualcosa all‟utente, l‟unica cosa che può fare è
aggiungere qualcosa alla barra delle notifiche.
Se si desidera una sola istanza è consigliabile la classe intentService.
Altrimenti è consigliata la classe Service.
Creazione di una notifica
Istanziare un Intent per il richiamo dell‟Activity di risposta alla notifica
Istanziare un PendingIntent che congloba al suo interno l‟Intent precedente
Istanziare una un oggetto Notification che congloba al suo interno l‟oggetto PendintIntent e che
mediante il metodo notify() avvisa il NotificationManager del SO
public class MyService extends IntentService {
public MyService(){
super ("Mio Servizio");
// La superclasse deve assegnare un nome al servizio
// Nome con cui il sistema riconosce il servizio
}
// Evento richiamato in corrispondenza dell'avvio del servizio
protected void onHandleIntent (Intent intent) {
// passo 1 : accesso al NotificationManager
// s è la stringa che identifica il Notification Manager
String s = Context.NOTIFICATION_SERVICE;
NotificationManager mng = (NotificationManager)getSystemService(s);
// passo 2 : creo un oggetto Notification che contenga gli estremi della notifica
// da visualizzare sulla barra delle Notifiche
int icon = R.drawable.ic_launcher;
CharSequence tickerText = "Breve testo di notifica";
long tempo = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, tempo);
// L'oggetto Notification è stato deprecato nelle ultime versioni
// e sostituido da un Builder decisamente più complicato.
// NotificationCompat.Builder myBuilder = new NotificationCompat.Builder(this){}
// passo 3 : definisco cosa fare quando l'utente selezionerà (con un tap) la notifica
Context context = getApplicationContext();
CharSequence title = "My notification";
CharSequence text = "Testo completo della notifica";
// definisco l'Activity che dovrà essere eseguite sul touch
Intent intent2 = new Intent(this, ActivityNotification.class);
PendingIntent intent1 = PendingIntent.getActivity(this, 0, intent2, 0);
// Associo azione da eseguire in corrispondenza del tap sulla notifica
notification.setLatestEventInfo(context, title, text, intent2);
pag 55
Tecnologie - Classe Quinta
robertomana.it
Java per Android
// passo 4 : attivo la notifica
String id = this.getString(R.string.HELLO_ID);
mng.notify(Integer.parseInt(id), notification);
}
Avvio del servizio
Il servizio precedente può essere avviato in corrispondenza dell‟arrivo di dati oppure, semplicemente,
posizionando un pulsante su una Activity principale :
public void btnClick(View v){
Intent intent = new Intent(MyActivity.this, MyService.class);
startService(intent);
}
Activity di risposta alla notifica
Mostra all‟utente i dati relativi alla notifica e presenta le seguenti istruzioni di accettazione della notifica
(eseguite ad esempio tramite un semplice pulsante):
public void btnClick(View v){
String s = Context.NOTIFICATION_SERVICE;
NotificationManager mng = (NotificationManager) getSystemService(s);
String id = this.getString(R.string.HELLO_ID);
mng.cancel(Integer.parseInt(id));
}
Utilizzo del GPS e accesso alle Google Maps
Simulazione delle coordinate GPS
L‟emulatore non può ovviamente leggere le coordinate GPS, che possono comunque essere impostate
(a terminale acceso !) tramite Telnet oppure tramite DDMS / Emulator Control
telnet localhost 5555
(porta di ascolto del dispositivo)
geo fix 44.552304, 7.762786
Dall‟interno di DDMS occorre usare la virgola e non il puntino. Attenzione che le coordinate GPS
vengono azzerate ogni volta che si scarica il programma nell‟emulatore.
Android Manifest
Nell‟android manifest occorre richiedere i diritti sia per lavorare con il GPS sia per accedere ad Internet
(da scriversi direttamente all‟interno del tag Manifest).
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
Activity Principale
Facciamo in modo che l‟Activity principale, oltre ad ereditare da Activity, implementi anche l‟interfaccia
LocationListener la quale richiede l‟implementazione di quattro metodi astratti implementabili al
solito con ALT + INVIO.
pag 56
Tecnologie - Classe Quinta
robertomana.it
Java per Android
Per avviare il sistema di localizzazione occorre innanzitutto istanziare un oggetto LocationManager
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
Il metodo lm.isProviderEnabled verifica se è abilitata la lettura della Posizione Corrente.
Nel caso in cui il GPS non sia attivato, occorre richiamare un‟apposita Activity di sistema che provvede
a richiedere all‟utente se acconsente all‟utilizzo del sistema di localizzazione:
Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(i);
In caso di sistema di localizzazione attivo si può avviare la lettura delle coordinate correnti utilizzando il
metodo
lm.requestLocationUpdates(provider, 1000, 10, this);
I parametri hanno il seguente significato:
1. Provider Il nome del provider da utilizzare per le registrazioni (indirizzo IP o GPS)
2. minTime intervallo minimo di lettura delle coordinate (in msec)
3. minDistance distanza minima in metri. L‟evento onLocationChanged viene generato ad ogni
minTime in cui la distanza percorsa (rispetto all‟evento precedente) risulta > minDIstance
4. LocationListener è il puntatore all‟istanza di LocationListener all‟interno della quale si trova
l‟evento onLocationChanged che dovrà essere eseguito in corrispondenza del trigger. (this)
if(!(lm.isProviderEnabled(LocationManager.GPS_PROVIDER))) {
Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(i);
}
else{
Criteria criteria = new Criteria();
String provider = lm.getBestProvider(criteria, false);
// String provider = LocationManager.GPS_PROVIDER; // NETWORK_PROVIDER
lm.requestLocationUpdates(provider, 1000, 5, this);
}
L‟oggetto Location corrente viene restituito all‟interno dell‟evento onLocationChanged richiamato
nell‟esempio ogni secondo, ma solo se le coodinate sono cambiate per un valore superiore ai 5 m
public void onLocationChanged(Location loc){
Double lat = loc.getLatitude();
Double lng = loc.getLongitude();
Toast.makeText(this, "lat, lng",
Toast.LENGTH_LONG).show();
}
Visualizzazione del percorso sulle Google Maps
Per la visualizzazione di un percorso sulle google maps si può utilizzare il seguente codice, passando
al servizio google soltanto il punto di partenza ed il punto di arrivo
String coord1 = list.get(0);
String coord2 = list.get(list.size() - 1);
String addr = "http:/maps.google.com/maps?saddr=" + coord1
+ "&daddr=" + coord2; // saddr=44.168292,7.289257&daddr=44.568292,7.98925
Uri uri = Uri.parse(addr);
Intent mapIntent=new Intent(Intent.ACTION_VIEW, uri);
if(mapIntent.resolveActivity(getPackageManager())!=null)
startActivity(mapIntent);
Per quanto concerne le Mapper Statiche di Google possono essere usate anche senza registrazione.
Altrimenti occorre registrarsi al servizio di google e richiedere una key per l‟utilizzo delle Google Maps
pag 57