Sei sulla pagina 1di 105

2}

Corso su Android - Dalla


teoria alla pratica

1. Configurazione e guida allimplementazione del primo progetto

ndroid il pi popolare tra i sistemi operativi

codice nativo C++. Le applicazioni sviluppate

per dispositivi mobili basato su una versione

possono essere distribuite gratuitamente o ven-

modificata di Linux. Android open source

dute sul Play Store, dove possibile controllare

sotto licenza Apache v2.0, il che significa che

come distribuire il prodotto. Se lapplicazione

chiunque pu scaricare il codice sorgente, mo-

aumenta di popolarit, Google Play fa salire di

dificarlo e ridistriburlo. I dispositivi in cui viene

posizione nella graduatoria per dare pi visibili-

installato crescono rapidamente e ogni giorno

t allapplicazione.

milioni di persone acquistano un telefono con

Android sfrutta le caratteristiche hardware dei

sistema operativo Android.

dispositivi che permettono alle applicazioni di

Sviluppare applicazioni java based su Android

adattarsi graficamente, infatti possibile svilup-

semplice e veloce grazie all SDK messo a

pare una sola applicazione che vada bene per

disposizione per gli sviluppatori. Inoltre viene

smartphone e tablet creando un layout grafico

messo a disposizione un NDK (Native Deve-

ad hoc.

lopment Kit) per lo sviluppo di funzionalit in

Molte le funzionalit che si possono utilizza-

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


re in quanto presente il supporto per GPS,

All ultimo livello si trovano le applicazioni cu-

Bluetooth, accelerometro, wireless e USB

stom scritte da sviluppatori.

che permettono la creazione di applicazioni


variegate.

Configurazione ambiente
Nuova installazione

Architettura

E possibile installare lSDK Android sui sistemi

Il Sistema operativo Android uno stack di

operativi pi utilizzati (Windows, Mac, Linux).

componenti software diviso in 5 sezioni e 4 li-

LSDK Android fornisce le API e i tools neces-

velli come si vede dal diagramma architetturale

sari per buildare, testare ed effettuare il debug

sottostante.

delle applicazioni Android.


Per una nuova installazione consigliamo di scaricare lADT Bundle presente a questo indirizzo.
LADT Bundle include:
lambiente di sviluppo Eclipse con il plugin
ADT(Android Developer Tools) incluso;
Android SDK Tools;
Android Platform-tools;
la pi recente piattaforma Android;
la pi recente immagine di sistema Android
per lemulatore;

Android costruito su un kernel Linux che forni-

Per chi ha gi un IDE (Eclipse, NetBeans)

sce funzionalit per la gestione della memoria,

Se hai gi unambiente di sviluppo e vuoi svilup-

gestione dei permessi, fornisce molti driver ed il

pare applicazioni Android devi scaricare lSDK

supporto alle shared library.

Tools presente a questo indirizzo http://develo-

Sopra il kernel sono presenti un set di librerie

per.android.com/sdk/index.html alla voce USE

quali per esempio SQLite che permette lacces-

AN EXISTING IDE. Al termine dellinstallazione

so al database sqlite3, librerie per lutilizzo di

si deve selezionare la versione della piattafor-

OpenGL e cos via.

ma Android da sviluppare e limmagine di siste-

Android implementa una sua virtual machine

ma per il simulatore.

che permette la portabilit delle applicazioni, fa

E inoltre necessario aggiungere il plugin ADT al

girare file ottimizzati in formato dex, converte i

tuo ambiente di sviluppo.

file .java in .dex a runtime.

Il plugin pu essere installato in 2 modi possibili:

Inoltre fa in modo che ogni applicazione giri in

1. Scaricando il plugin direttamente dallIDE

un proprio processo .
Fornisce molti servizi ad alto livello che possono
essere utilizzate dalle applicazioni scritte dagli
utenti.

(consigliato)
2. Scaricando il pacchetto .zip sul computer e
poi caricarndo sullIDE

{3

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


1) Download dellADT Plugin con Eclipse

bleshooting ADT Installation il pacchetto .zip

Aprire Eclipse e selezionare Help --> Install

1. Aprire Eclipse e selezionare Help --> Install

New Software
1. Click su Add in alto a destra della finistra che
si apre.
2. Nella sezione Add Repository della finestra
che appare inserire ADT Plugins per Name

New Software
2. Click su Add in alto a destra della finistra che
si apre.
3. Nella sezione Add Repository cliccare su
Archive.

e la seguente url per Location: https://dl-ssl.

4. Selezionare il file scaricato e cliccare su OK.

google.com/android/eclipse/

5. Inserire ADT Plugin per il nome e cliccare

3. Click su OK.

su OK.

Se si riscontrano problemi nellutilizzo del pro-

6. Nel box Available Software, selezionare

tocollo https si pu utilizare il protocollo http. La

il checkbox Developer Tools e cliccare su

url per Location diventerebbe: http://dl-ssl.goo-

Next.

gle.com/android/eclipse/
4. Nel box Available Software, selezionare
il checkbox Developer Tools e cliccare su
Next
5. Nella prossima finestra sar presente la li-

7. Nella prossima finestra sar presente la lista


dei tool che saranno scaricati e cliccare su
Next.
8. Leggere ed accettare le condizioni di licenza
e cliccare su Finish.

sta dei tool che saranno scaricati; cliccare

Quando linstallazione stata completata riav-

su Next

viare Eclipse.

6. Leggere ed accettare le condizioni di licenza


e cliccare su Finish

9. Quando linstallazione stata completata riavviare Eclipse.

Se dovessero comparire dei warning di sicurezza o validit del software basta cliccare su OK.

Configurare un emulatore

7. Quando linstallazione stata completata ri-

Lemulatore Android fornito nellsdk e permet-

avviare Eclipse.

te di sviluppare la maggior parte delle applica-

Al termine del riavvio si deve specificare nelle

zioni. Unico difetto lestrema lentezza, consi-

preferenze di Eclipse la posizione della directo-

gliato per chi ha un pc (o mac) con almeno 4 GB

ry dellSDK Android scaricato precedentemen-

di RAM.

te:

E possibile configurare lemulatore cliccando

1. Nella finestra Welcome to Android Deve-

sullicona:

lopment che appare selezionare Use existing SDKs.


2. Selezionare la posizione della cartella dellSDK Android
3. Click su Next

Nota: Se non si riesce a vedere licona nella


barra, verificare che la propria vista eclipse sia
settata su Java. Per verificarlo basta andare su
Windows -->Perspective-->Java
Cliccando sullicona apparir la finestra con la

2) Installazione da file locale


Scaricare dal seguente link nella sezione Trou-

lista degli emulatori:

{4

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


mette a disposizione un tool che rende semplice
installare lapplicazione sul dispositivo tramite
Eclipse o da linea di comando tramite ADB.
Vediamo come abilitare il debug su un dispositivo:
1. Nel file AndroidManifest.xml, aggiungere
lattributo android: debuggable=true nellelemento <application> se si usa eclipse quePer aggiungere un nuovo emulatore cliccare su
New
Ora bisogna inserire le caratteristiche dellemulatore.
In particolare:
AVD Name: il nome dellemulatore
Device: il tipo di device da emulare (definito
da tipo di schermo,dimensioni e densit )
Target: la versione di Android installata
CPU/ABI: il tipo di processore
Memory Option: la memoria RAM da assegnare allemulatore
Internal Memory: la memoria interna dellemulatore
SD: la memoria della SD card
E inoltre possibile emulare la fotocamera se il
pc/mac ha una webcam

sto passaggio non serve, viene impostato in


automatico nel momento del deploy. Per se
si mette, prima di distribuire lapplicazione
va tolto o messo a false perch lapplicazione NON deve essere debuggabile)
2. Abilitare il debug USB sul dispositivo. Possiamo trovare il settaggio sotto i path:
per telefoni con versione Android minore e uguale alla 3.0 sotto Impostazioni->Applicazioni-->Sviluppo
per telefoni con versione Android maggiore e
uguale alla 4.0 sotto Impostazioni-->Opzioni
sviluppatori
SetUp del sistema operativo per riconoscere il
dispositivo:
Mac: non necessario effettuare nessuna
operazione.
Windows: bisogna installare i driver del dispositivo, di seguito riportiamo i passi da effettuare per Widows 7:
Connetere il dispositivo via USB al PC
Premere il tasto destro su risorse del
computer e selezionare Gestione
Selezionare Gestione dispositivi nel pannello di sinistra

Cliccare su OK.

Configurare un telefono
Quando si implementa unapplicazione consigliato effettuare il deploy ed il test su un dispositivo fisico prima di distribuirla agli utenti. LSDK

Nel pannello di destra espandere la voce


Altri dispositivi
Premere il tasto destro sul nome del device e selezionare Aggiornamento software driver
Selezionare la voce Cerca il software del

{5

{6

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


driver nel computer and click next
Cliccare su sfoglia e selezionare il path
<sdk>\extras\google\usb_driver\
Cliccare next ed installare il driver
Linux: Se si sviluppa su Ubuntu bisogna aggiungere un file di regole udev che contiene
la configurazione per ogni tipo di device che
si vuole utilizzare per deployare le applicazioni. Nel file delle regole ogni costruttore
identificato da un identificativo univoco indicato com la propriet ATTR{idVendor} quindi:
Loggarsi come root e creare il file /etc/
udev/rules.d/51-android.rules.
re

il

formato

Usa-

SUBSYSTEM==usb,

ATTR{idVendor}==0bb4,
MODE=0666,

GROUP=plugdev.

Dove 0bb4 rappresenta il vendor id per


lHTC
Eseuire chmod a+r /etc/udev/rules.d/51android.rules
Per verificare se il dispositivo connesso al pc
si pu procedure in due modi:
da Eclipse: passare alla visualizzazione
DDMS e se il dispositivo connesso si vede
il nome nella finestra Devices
da command line: navigare fino al path
<sdk>\platform-tools e lanciare adb devicesandroid.rules

Company
Acer
Asus
Dell
Foxconn
Fujitsu
Fujitsu Toshiba
Garmin-Asus
Google
Haier
Hisense
HTC
Huawei
K-Touch
KT Tech
Kyocera
Lenovo
LG
Motorola
MTK
NEC
Nook
Nvidia
OTGV
Pantech
Pegatron
Philips
PMC - Sierra
Qualcomm
SK Telesys
Samsung
Sharp
Sony
Sony Ericsson
Teleepoch
Toshiba
ZTE

USB Vendor ID
0502
0b05
413c
0489
04c5
04c5
091e
18d1
201E
109b
0bb4
12d1
24e3
2116
0482
17ef
1004
22b8
0e8d
0409
2080
0955
2257
10a9
1d4d
0471
04da
05c6
1f53
04e8
04dd
054c
0fce
2340
0930
19d2

se il dispositivo presente verr restituito il


nome.

HelloWorld

Di seguito per comodit vengono riportati i ven-

Andiamo a vedere come creare il nostro primo

dor ID :

progetto Android con Eclipse.


1. avviare lIDE Eclipse
2. dal menu File --> New --> Other

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


3. selezionare la cartella Android --> Android
Application Project e cliccare Next

progetto esistente
7. Cliccare su Next.

4. compilare i campi come di seguito. Nota-

8. Questa schermata compare se stata sele-

re che inserendo il nome dellapplicazione

zionata nella schermata precedente la voce

nella sezione Application Name verranno in

Create custom launcher icon. E possibile

automatico prepopolati anche gli altri campi

selezionare una propria immagine creata in

che possibile comunque modificare

precedenza oppure lasciando tutto inaltera-

In questa schermata inoltre possibile sceglie-

to verr creata licona di default di Android.


9. Cliccare su Next.

re:
la versione minima dellSDK Android dalla
quale girer lapplicazione.

la versione dellSDK Android per cui destinata lapplicazione

la versione con cui sar compilata lapplicazione


Questi settaggi possono essere modificati anche in seguito nel file AndroidManifest.xml
10. Questa schermata compare se stata selezionata nella schermata precedente la voce
Create Activity. E possibile scegliere il tipo
di Activity da creare. Il tool ci mette a disposizione alcuni tipi di activity.
11. Cliccare su Next.

5. Al termine cliccare su Next


6. Nella schermata successiva possibile effettuare alcune scelte tra cui:
a.

includere unicona diversa da quella di

default (la scelta non definitiva, pu essere


modificata o inserita in un secondo momento)
b.

creare unactivity di default. In questo

caso il tool che crea una struttura di base(un


file java e un file xml)
c.

il percorso dove salvare il proprio proget-

to. Di default il workspace scelto alla partenza


di Eclipse
d.

e possibile aggiungere il progetto ad un

12. Scegliere il nome dellactivity e dellxml associato.


13. Cliccare su Finish.
14. LIDE creer la struttura del progetto e visualizzer lactivity creata.

{7

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

Run Configuration
E possibile eseguire lapplicazione su un dispositivo fisico oppure utilizzando lemulatore
messo a disposizione dallSDK di Android. In
entrambi i casi possibile scegliere tra due diverse modalit:
Modalit personalizzata
1. Modalit veloce:
1. Cliccare con il pulsante destro sul nome
del progetto
2. Scegliere Run as --> Android Application
2. Modalit personalizzata:
1. Cliccare con il pulsante destro sul nome
del progetto
2. Scegliere Run as --> Run Configurations
3. Cliccare con il pulsante destro su Android Application
4. Scegliere New
5. Cliccare sul pulsante Browse
6. Scegliere il proprio progetto e cliccare su
OK
7. Scegliere il nome della configurazione e
cliccare su Run

Struttura del progetto

Espandendo la root del progetto nella sezione


Project Explorer si possono vedere delle cartelle, il plugin non le crea tutte alcune devono
essere aggiunte in un secondo momento a seconda dellesigenza:
src: contiene i file sorgenti java
gen: contiene il file R.java e le interfacce create dal file AIDL. Questo contiene gli id di
tutti gli oggetti dellapplicazione. Viene crea-

{8

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


to a runtime nel momento della compilazio-

tutto possiamo dire che Android distingue tra:

ne e quindi inserito nel file apk.

Screen size: la dimensione fisica dello scher-

assets:di default vuota. I file che vengono


messi qui sono inclusi nellapk e possono

mo misurata sulla diagonale e definisce gli


schermi in 4 categorie
Small con schermi di dimensione mi-

essere letti utilizzando lAssetManager op-

nima di 426dp x 320dp

pure tramite url dallapplicazione

normal con schermi di dimensione mi-

bin: cartella di output dove vengono messi i

nima di 470dp x 320dp

file compilati e il file apk

large con schermi di dimensione mini-

libs: contiene le librerie dellutente

ma di 640dp x 480dp

res: composta da sottocartelle e contiene

extra large con schermi di dimensione

le risorse applicative (NB: i nomi delle risor-

minima di 960dp x 720dp

se allinterno della cartella devono essere


scritte con caratteri minuscoli altrimenti si
avranno errori in compilazione)
anim: contiene i file relativi alle animazioni

Screen density: la quantit di pixel in


una data area dello schermo definita come
dpi(dot per inch) definita in 5 categorie low,
medium, high, extra high e extra extra high

drawable:contiene i file bitmap


layout:contiene i file xml che descrivono le interfacce utente
menu:contiene i file xml relativi ai
menu applicativi
values:contiene semplici valori come
string, stili, colori, dimensioni e array

Quindi possibile adattare la grafica inserendo


apposite immagini nelle cartelle con una determinata size o con una determinata density;
sar poi il Sistema Operativo in fase di installazione ad adattare la grafica necessaria.
Per creare le cartelle con le varie densit basta

xml:contiene file xml che possono

appendere in suffisso (qualifier) opportuno alle

essere letti a runtime chiamando Re-

cartelle, di seguito riportiamo quelle per drawa-

sources.getXML()

ble:

Nel progetto possiamo distinguere pi cartelle

drawable-hdpi

con il nome drawable, questo perch chi svilup-

drawable-lhdpi

pa unapp deve fornire linterfaccia grafica per i

drawable-mhdpi

vari dispositivi.

drawable-xhdpi

Infatti Android adatta la grafica a seconda

drawable-xxhdpi

del dispositivo su cui viene installata. Cos

Per recuperare le risorse che meglio si adatta-

possibile fare la stessa applicazione per i vari

no agli schermi dei dispositivi, Android usa la

modelli di smartphone e tablet modificando so-

seguente logica: se non esiste un qualifier per

lamente il layout grafico e distribuendo lo stesso

lo schermo del telefono, allora sceglie la risor-

file apk. Infatti possibile fornire varie risorse

sa definita per uno schermo pi piccolo. Se

che verranno adattate a runtime dal sistema

non fosse disponibile una risorsa piu piccola od

operativo.

uguale a quella necessaria al telefono, lappli-

Vediamo come android adatta i layout. Prima di

cazione verr chiusa forzatamente dal sistema

{9

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca


operativo.

#ARGB

Android mette a disposizione altri due qualifica-

#RRGGBB

tor per distinguere lorientamento del telefono:

#AARRGGBB

port: indica una risorsa nel caso il dispositi-

La definizione la seguente:

vo in posizione vericale
land: indica una risorsa nel caso il dispositivo in posizione orizzontale
E inoltre possibile definire altri qualifier sul lay-

<?xml version=1.0 encoding=utf-8?>


<resources>
<color
name=color_name>hex_color</color>

out a seconda dellarea dello schermo.

</resources>

Se per esempio il tuo layout richiede che la di-

3) dimens.xml: definisce una dimensione defi-

mensione pi piccola dellarea dello schermo

nita da un numero seguito da una unit di misu-

debba essere almeno 600 dp, allora si pu usa-

ra. Per esempio 10dp, 10sp, 10pt, 10px, 10mm,

re questo qualifier layout-sw600dp. Il sistema

10in dove:

user questa risorsa solo quando la pi piccola

dp: Density indipendent Pixel, un nume-

dimensione disponibile dello schermo almeno

ro astratto basato sulla densit fisica dello

600dp (in altezza o larghezza).

schermo. Questa unit relativa a 160dpi

Allinterno della cartella res vi unaltra cartella,

(dots per inch) dove 1dp uguale ad 1px,

quella values contentente i valori:

quindi quando lapplicazione gira su uno

arrays.xml

schermo con pi alta densit, il numero di

colors.xml

pixel usato aumentato di un fattore di scala

dimens.xml

relativo allo schermo, cos se lapplicazione

strings.xml

gira su un dispositivo con bassa densit, il

styles.xml

numero di pixel viene diminuito con un fat-

Andiamo a spiegare cosa rappresentano questi

tore di scala appropriato. Utilizzando questa

file:

unit di misura si ha una consistenza degli

1) arrays.xml: permette di creare TypedArray di

elementi grafici su dispositivi diversi;

item. Larray pu essere di tipi diversi, sta allo

sp: Scale Indipendent Pixel. come dp ma

sviluppatore definire il tipo di dati presenti.

viene scalato anche per le preferenze del

<?xml version=1.0 encoding=utf-8?>

font dellutente.

<resources>
<array name=country>

pt: Points rappresenta 1/72 di un inch basato sulla dimensione fisica dello schermo

<item>Albania</item>

px: Pixel, corrisponde al pixel dello scher-

<item>Algeria</item>

mo. Questa non raccomandata perch

</array>

ogni device potrebbe avere un numero di pi-

</resources>

xel per inch diverso.

2) color.xml: permette di specificare colori in


RBG e canale alpha. I colori devono esere preceduti dal simbolo # e possono essere:
#RGB

mm: millimeters, basato sulla dimensione fisica dello schermo


in: Inches basato sulla dimensione fisica
dello schermo

{10

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Le dimensioni hanno il seguente formato:

Esempio di caricamento di un layout dal codice:

<?xml version=1.0 encoding=utf-8?>

setContentView(R.layout.activity_main);

<resources>

Esempio di caricamento di una stringa da xml:

<dimen name=dimension_name>dimension</

<TextView

dimen>

android:layout_width=wrap_content

</resources>

android:layout_height=wrap_content

4) strings.xml: contiene stringhe che possono

android:text=@string/hello_world />

essere referenziate allinterno dellapplicazione


<?xml version=1.0 encoding=utf-8?>

Localizzazione

<resources>

Come sappiamo Android gira su telefoni in pi

<string name=string_name>text_string</

nazioni e quindi aventi lingue diverse. Per poter

string>

adattare a piu lingue la nostra applicazione ba-

</resources>

sta creare delle cartelle con un qualifier adatto

5) styles.xml: definisce gli stili dellapplicazione

(it, en, fr.). Per esempio se vogliamo adatta-

<?xml version=1.0 encoding=utf-8?>

re tutte le stringhe sia per la lingua italiana che

<resources>

per quella inglese possiamo creare la cartella

<style name=style_name parent=@[package:]style/style_to_inherit>


<item name=[package:]style_property_name>style_value</item>
</style>
</resources>

values-en al cui interno andremo a mettere il


file string.xml con le definizioni scritte in lingua
inglese. Potremmo creare anche una cartella
values-it e mettere un file string.xml con le definizioni in italiano.
E da notare che se il Sistema Operativo non
trova il qualifier per cui settata la lingua del

Accedere alle risorse

telefono, considera la cartella di default values,

Una volta definite le risorse possibile referen-

quindi nel caso precedente nella cartella values

ziarle nel progetto utilizzano il resource ID che

possiamo mettere la lingua che vogliamo appa-

viene generato in fase di compilazione e messo

ia di default (inglese) e creare solamente la car-

nel file R.java.

tella values-it. Cosi se un dispositivo in lingua

Questultima conterr tutti i resouce ID delle ri-

francese usa lapplicazione vedr tutti i testi in

sorse che i trovano sotto la cartella res. Per ogni

lingua inglese.

tipo di risorsa vi una sottoclasse da cui si ac-

Di seguito un esempio per aggiungere la lingua

cede allid, per esempio nel progetto HelloWorld

italiana e impostare la lingua inglese di default

troviamo R.layout.activity_main che rappresen-

su dispositivi con lingue diverse:

ta il resource ID per recuperare il layout.

Cliccare con il tasto destro sulla cartella res e

Possiamo dire che ci sono due modi per acce-

scegliere new-->folder

dere alle risorse:

Inserire values-it in Folder name e premere Fi-

dal codice: come abbiamo visto sopra

nish

R.string.hello
dallinterno dellxml: @string/hello

Cliccare con il pulsante destro del mouse sulla


cartella appena creata e scegliere new

{11

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


-->Android XML file

5. Premere su Next

1. Cliccare con il tasto destro sulla cartella res


e scegliere new-->folder
2. Inserire values-it in Folder name e premere
Finish
3. Cliccare con il pulsante destro del mouse
sulla cartella appena creata e scegliere new
-->Android XML file

6. Premere su Finish
Nota: notare come nella sezione Chosen Qualifier venga in automatico impostata la lingua it.
In questo modo avremo per ogni cartella values(qualifier) un file strings che dovr contenere
tutti gli elementi tradotti nelle lingua corrispondente.
4. Impostare a Value la voce Resource Type e
inserire strings alla voce File:

La prima puntata si conclude qui


Nel prossimo articolo vedremo quali sono le caratteristiche di un Activity e come questa venga
gestita dal sistema operativo. Vedremo inoltre
lutilizzo del Manifest con le descrizioni dei vari
tag.
Stay tuned.

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:
http://it.emcelettronica.com/corso-su-android-configurazione-guida-allimplementazione-del-primo-progetto

{12

{13

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

2. Lavorare con le Activity

niziamo questo secondo articolo con un po di

Il file AndroidManifest.xml deve iniziare con il

teoria necessaria per capire come sviluppare

tag univoco <manifest> che include tutti gli altri

applicazioni efficienti. Di seguito andremo ad

tag. Al suo interno vi sono alcuni attributi impor-

analizzare alcuni componenti fondamentali par-

tanti:

tendo da un file molto importante in un progetto

package: usato come identificativo univoco

Android: il manifest.

dellapplicazione

Ogni applicazione Android deve avere un file

android:versionCode: un numero intero

chiamato AndroidManifest.xml nella directory

che serve per vedere se una versione pi

principale; questo contiene informazioni essen-

recente di unaltra

ziali che servono al sistema prima di caricare


lapplicazione. Di seguito riportiamo il manifest

android:versionName: versione visualizzata allutente

del progetto HelloWorld ed andremo a vedere i

Tag <uses-sdk> permette di discriminare sulla

tag fondamentali.

compatibilit con una o pi versioni di Android.


Infatti i valori vengono confrontati con quelli

<?xml version=1.0 encoding=utf-8?>


<manifest xmlns:android=http://schemas.android.com/apk/res/android
package=com.example.helloworld
android:versionCode=1
android:versionName=1.0 >
<uses-sdk
android:minSdkVersion=8
android:targetSdkVersion=17 />
<application
android:allowBackup=true
android:icon=@drawable/ic_launcher
android:label=@string/app_name
android:theme=@style/AppTheme >
<activity
android:name=com.example.helloworld.MainActivity
android:label=@string/app_name >
<intent-filter>
<action android:name=android.intent.action.MAIN />
<category android:name=android.intent.category.LAUNCHER />
</intent-filter>
</activity>
</application>
</manifest>

del sistema operativo sui singoli dispositivi.


Gli attributi che contiene sono:
android:minSdkVersion: il livello minimo di
API richiesto per far girare lapplicazione
android:targetSdkVersion: il livello di API
per cui lapplicazione stata sviluppata, se
nullo assume lo stesso valore di minSdkVersion
android:maxSdkVersion: il livello massimo
di API richiesto per far girare lapplicazione
Tag <uses-feature>: dichiara quale caratteristica hardware deve avere il dispositivo per
far girare lapplicazione. Gli attributi che contiene sono:
android:name: specifica una caratteristica
hardware o software che il device deve avere per far girare lapplicazione
android:require: valore booleano che indica se lapplicazione richiede la caratteristica specificata in android:name. Se
android:require=true

allora

lapplica-

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


zione stata disegnata per usare quella

to e viene lanciata lactivity associata.

specifica caratteristica e quindi lapplica-

Prendendo come esempio sempre il manifest

zione si potr installare solamente su di-

del progetto HelloWorld vediamo che nella no-

spositivi aventi tale caratteristica. Se invece

stra activity vi un unico intent filter (se ne pos-

android:name=false allora lapplicazione

sono aggiungere altri a seconda delle esigen-

pu comunque essere installata sul telefo-

ze):

{14

no, anche se la feature non presente.

android:glEsVersion: richiede una specifi- <intent-filter>

<action android:name=android.intent.action.MAIN/>
<category android:name=android.intent.category.LAUNCHER/>
Tag <uses-permission>: permessi che lappli- </intent-filter>

ca versione delle OpenGL

cazione necessita per accedere ad alcune fun-

zionalit (per esempio la fotocamera). Lutente

Come si vede vi un tag action e un tag catego-

acconsente ad utilizzare questi permessi nel

ry che indicano che questa lactivty principa-

momento dellistallazione, ha un unico attributo:

le che verr visualizzata quando verr lanciata

android:name: nome del permesso (es:

lapplicazione.

<uses-permission android:name=android.

Come abbiamo visto con i tag <uses-sdk>,

permission.INTERNET />)

<uses-feature> e <uses-permission>, si defi-

Tag <application> la dichiarazione dellappli-

nisce su quale SDK deve girare lapplicazione,

cazione e contiene le dichiarazioni dei compo-

che caratteristiche fisiche deve avere il telefono

nenti utilizzati nellapplicazione.

e i permessi di cui lapplicazione ha bisogno per

Allinterno del tag <application> troviamo il

funzionare. Possiamo aggiungere che tramite

tag <activity> che dichiara le activity utilizzate

la uses-sdk e uses feature viene anche discri-

nellapplicazione.

minato su quale telefono possibile installare

Importante: Tutte le activity utilizzate devono

lapplicazione, per esempio se abbiamo

essere dichiarate nel manifest.


Gli attributi che possono essere presenti nel tag

<uses-sdk android:minSdkVersion = 10 />

<application> sono molti, vediamo quelli pi co- <uses-feature android:name=android.hardware.bluetooth />


munemente utilizzati:
android:name: nome della classe che implementa lactivity
android:label: label dellapplicazione che
viene visualizzata insieme allicona

I due valori ci dicono che la versione minima di


Android su cui pu essere installata lapplicazione la 2.3.3 e che il dispositivo deve avere

android:screenOrientation: rende fisso lo-

il bluetooth; se una delle due condizioni non

rientamento dellactivity (portrait, landscape)

verificata, lapplicazione non viene installata sul

Come ultimo tag vediamo <intent-filter> che

dispositivo. Questo il motivo per cui alcune

aggiunge un action al filtro. Ogni tag deve con-

volte dal PlayStore alcuni telefoni vedono le ap-

tenere almeno una <action>. Quando viene lan-

plicazioni e altri no.

ciato un intent (definito pi avanti) se definito

Di seguito una tabella che associa ad ogni

un intent filter apposito, lintent viene intercetta-

Platform il relativo API level

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


te pu interagire con questa (in questo stato
lActivity in stato running)
paused : Unaltra Activity in foreground ed
ha il focus anche se quella precedente ancora visibile.
stopped: LActivity oscurata completamente da unaltra Activity.
Quando unActivity si trova negli stati paused e
stopped il SO pu decidere di distruggerla se in
quel momento ha bisogno di risorse. Di seguito
viene mostato uno schema del ciclo di vita di
unactivity:

Activity e Ciclo di vita dellApplicazione


LActivity un componente applicativo che fornisce una parte visuale allutente, grazie al quale pu interagire con le funzionalit del telefono come: mandare sms, telefonare, guardare
le foto,etc. Unapplicazione formata da una
o pi Activity, una principale che viene visualizzata allapertura dellapplicazione e le altre
visualizzate a seconda della scelta dellutente.
E possibile visualizzare unActivity alla volta, infatti ogni volta che unActivity viene visualizzata
quella precedente viene messa in background

Come si vede dalla figura, lActivity vive tra i

dal SO. Questa logica viene gestita dal SO in

metodi di callback onCreate ed onDestroy, tra

modo trasparente allutente tramite uno stack

questi due ci sono tutti quelli in cui lactivity pu

(pila LIFO Last In First Out). Quando si naviga

trovarsi nel corso della sua vita:

nellapplicazione e si sceglie di visualizzare una

onCreate(): Chiamato quando lActivity

nuova Activity quella precedente viene messa


nello stack e recuperata quando viene premuto
il tasto back e mostrata a video.
Nel corso della sua vita unactivity pu trovarsi
essenzialmente in 3 stati:
resumed: lActivity in foreground e luten-

creata per la prima volta


onStart(): Chiamata quando lActivity diventa visibile per la prima volta
onResume(): Chiamata quando lActivity
inizia linterazione con lutente
onPause(): Chiamata quando lapplicazione

{15

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


viene messa in pausa
onStop(): Chiamata quando lapplicazione
non pi visibile dallutente
onDestroy(): Chiamata quando lActivity
viene distrutta dal sistema
onRestart(): Chiamata quando lActivity
stata stoppata e poi riavviata

ca Activity principale che viene visualizzata nel


momento in cui viene lanciata lapplicazione.
Ma unapplicazione formata da pi Activity ed
ora vedremo come possibile passare da una
activity allaltra. Per ottenere questo risultato
vengono utilizzati gli intent. Questi non sono altro che dei messaggi grazie ai quali si possono

Come visto nel Progetto HelloWorld lactivity

attivare i componenti di base come le Activity, i

deve estendere la classe android.app.Activity e

service e i broadcast receiver.

implementare il metodo onCreate in pi nel ma-

Creiamo un nuovo progetto NavigationFromAc-

nifest deve essere dichiarata allinterno del tag

tivity seguendo i passi effettuati per HelloWord

<application>.

creando come activity principale MainActivity

<activity
android:name=com.example.helloworld.MainActivity
android:label=@string/app_name >
<intent-filter>
<action android:name=android.intent.action.MAIN />
<category android:name=android.intent.category.LAUNCHER />
</intent-filter>
</activity>

come in figura:

La classe java che estende Activity deve poi visualizzare lxml associato. Come vedremo Android separa la parte grafica dalla parte logica
utilizzando per la parte visuale dei file in formato
xml cosi da rendere pi gestibile e comprensibile il codice. Come si vede dallestratto di codice
il file xml si chiama main e viene caricato con

Creiamo ora la seconda Activity: espandiamo la

listruzione setContentView(R.layout.main);

root del progetto e la cartella con i sorgenti (src),

public class MainActivity extends Activity {


/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}

Intent, creare unActivity e navigare


tra Activity
Nel progetto HelloWorld stata creata ununi-

posizioniamoci sul package com.example.navigatefromactivity e premiamo il tasto dx del mouse, si aprir un menu a tendina in cui andremo
a scegliere new-->class

{16

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Inserire come nome dellActivity SecondActivi-

selezionare Android xml file e premere Next

ty e nella sezione Superclass inserire android.


app.Activity; alla fine premere Finish

Inserire nella sezione nome activity_second


e premere Finish

Verr cos creata unActivity vuota a cui andremo ad aggiungere il metodo onCreate:
package com.example.navigatefromactivity;
import android.app.Activity;
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
}
}

Andiamo ora a creare un file activity_second.


xml da associare allactivity appena creata:
espandere la cartella res
premere il tasto dx sulla cartella layout
dal menu scegliere new-->Other

Nel file appena creato aggiungiamo una label,


per fare questo inseriamo una TextView come
indicato di seguito:

{17

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


<TextView
android:layout_width= match_parent
android:layout_height=wrap_content
android:text=seconda activity
/>

perare il componente utilizzato nel file xml, cos


da poterlo utilizzare. Subito dopo associamo un
listener con setOnClickListener per intercettare la pressione del bottone da parte dellutente.
Nellimplementazione del metodo inseriamo le
istruzioni

Vedremo in dettaglio gli attributi nella parte in

Intent intent = new Intent(context, Secon-

cui spiegheremo i componenti visuali.

dActivity.class);

Per ora scriviamo direttamente il testo nel tag

startActivity(intent);

android:text, vedremo che consigliato definirlo

grazie alle quali viene creato un nuovo intent e

nel file string.xml.

con il metodo startActivity viene detto al SO di

Passiamo ora allActivity principale che come

voler passere allActivity definita nellintent (Se-

abbiamo visto formata solamente da una la-

condActivity).

bel, per dare la possibilit di passare alla se-

A questo punto compiliamo il progetto e faccia-

conda activity inseriamo un bottone. Quindi nel

mo il run. Verr visualizzata lactivity principale

file activity_main inseriamo il codice seguente:

con il bottone. Premendo su questo verr visua-

<Button
android:id=@+id/idButon
android:layout_width=wrap_content
android:layout_height=wrap_content
android:text=Vai alla seconda Activity
/>

lizzata la seconda Activity.

Notare lattributo android:id=@+id/idButon che


ci servir per recuperare il bottone ed utilizzarlo
nel codice java.
Nella classe MainActivity andiamo a recuperare il riferimento al bottone ed associare un
listener per intercettare la pressione da parte
dellutente.
Button btnGoToSecond = (Button)findViewById(R.id.idButton);
btnGoToSecond.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.
class);
startActivity(intent);
}
});

In questo modo abbiamo creato e visualizzato una seconda activity, ma se volessimo


tornare indietro?
Per tornare allActvity precedente Android
mette a disposizione il tasto back grazie al
quale il SO distrugge lActivity corrente e recupera dallo stack quella precedente. possibile terminarla anche intervenendo diretta-

Grazie al metodo findViewById possiamo recu-

mente sul codice utilizzando il metodo finish().

{18

{19

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Per provare questo metodo, aggiungiamo un

pi in dettaglio come interagire con ognuno di

bottone nella seconda Activity che ci permetter

questi quando parleremo dei componenti visua-

di tornare a quella precedente. Quindi inseria-

li. Per ora vediamo brevemente cosa sono gli

mo nel file activity_second.xml il codice:

event listener e gli event Handler.


Un event listener uninterfaccia che riceve le

<Button
android:id=@+id/idFinishButton
android:layout_width=wrap_content
android:layout_height=wrap_content
android:text=Chiudi
/>

notifiche di eventi e quando un evento accade


viene pasato allevent handler che gestisce levento tramite degli appositi metodi.

Event Listener & Description


Implementato dallinterfaccia OnClickListener, viene
onClick()
invocato quando lutente
Come visto per la prima Activity, nel codice java
tocca un componente.
dobbiamo recuperare il bottone e settare un liImplementato dallinterfacstener per intercettare il click fatto dallutente e
cia OnLongClickListener,
onLongClick()
viene invocato quando
quindi richiamare il metodo finish();
lutente preme per qualche
Nella classe SecondActivity.java inseriamo il
secondo su un componente.
codice:
Implementato dallinterfaccia OnFocusChangeListener
Button btnGoToSecond = (Button)findViewById(R.
onFocusChange()
viene invocato quando
id.idFinishButton);
cambia il focus su una view
btnGoToSecond.setOnClickListener(new OnClickListener() {
Implementato dallinter@Override
faccia OnKeyListener, viene
public void onClick(View v) {
richiamato quando lutente
onKey()
finish();
ha il focus su un componente e preme o rilascia un
}
tasto della tastiera hardware
});
Implementato dallinterfaccia OnTouchListener, viene
Lanciando lapplicazione si vede che la prima
onTouch()
richiamato quando lutente
Activity non cambiata, mentre nella seconeffettua una pressione sullo
da comparso il bottone con la scritta Chiudi,
schermo.
OnCreateContextMenuListepremendolo viene invocato il metodo finish() rionCreateContextMenu()
ner, viene chiamato quando
chiamato nellonClick e lActivity corrente viene
il menu viene creato
distrutta e la prima viene recuperata dallo stack
e mostrata a video.

Event Handler

Per definire questi metodi allinterno della


propria

Activity

bisogna

implementare

il

Event Listeners & Event Handlers

listener ed agganciarlo al componente tramite

Come abbiamo visto in questi semplici esempi

il metodo setOnClickListener come abbiamo

stato intercettato levento di onClick sullog-

visto nellesempio precedente con listruzione:

getto Button. Gli eventi sono un modo utile per

btnGoToSecond.setOnClickListener(new

recuperare dati mentre lutente interagisce con

OnClickListener()

i componenti visuali dellapplicazione. Vedremo

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

Passaggio di parametri tra activity

Abbiamo visto come possibile passare ad

Abbiamo visto come aprire una nuova Activity

unActivity a quella successiva passandole dei

ora verr illustrato come poter passare dei valori tra le Activity, per far questo vengono utilizzati
gli intent passandogli un parametro, utilizzando
la seguente istruzione:

parametri e tornare indietro, ma se una volta


che si ritorna allActivity chiamante si volesse sfruttare il risultato della seconda, come

intent.putExtra(EXTRA_PARAMETER, va-

potremmo fare?

lore del parametro);

Per vedere come funziona il passaggio in en-

Per recuperare il parametro dalla seconda activity si utilizza sempre lintent:


getIntent().getStringExtra(EXTRA_PARAMETER);

trambi i versi possiamo impostare un progetto


con due activity in modo da passare dalla prima
activity un valore intero alla seconda che lo in-

Per esempio nella prima Activty possiamo so-

crementa di un valore e lo restituisce in risposta

stituire il codice che implementa il bottone con

allActivity chiamante. Definiamo due file xml, il

il seguente:
Button btnGoToSecond = (Button)findViewById(R.id.idButton);
btnGoToSecond.setOnClickListener(new OnClickListener() {

{20

primo contenente un bottone, che fa passare


alla seconda Activity, e due label, una che contiene una stringa fissa e laltra che visualizza il

valore di partenza e poi modificato.


@Override
public void onClick(View v) {
Creiamo il progetto come quello precedente e
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra(com.example.navigatefromactivity.PARAMETER, modifichiamo il file activity_main.xml come setest parametro);
gue:
startActivity(intent);
}
});
e nella seconda nel metodo onCreate aggiungiamo:
Log.d(Test,getIntent().getStringExtra(com.example.
navigatefromactivity.PARAMETER));

<LinearLayoutxmlns:android=http://schemas.android.com/apk/res/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width=match_parent
android:layout_height=match_parent
android:paddingBottom=@dimen/activity_vertical_margin
android:paddingLeft=@dimen/activity_horizontal_margin

Dove Log la classe che si occupa di scrivere i

android:paddingRight=@dimen/activity_horizontal_margin

log e possono essere controllati comodamente

android:paddingTop=@dimen/activity_vertical_margin

nella view LogCat di eclipse:

tools:context=.MainActivity
android:orientation=vertical>
<Button
android:id=@+id/idButton
android:layout_width=wrap_content
android:layout_height=wrap_content

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


android:text=Vai alla seconda Activity
/>

Nel metodo onCreate dobbiamo recuperare la


TextView nella quale settare il valore iniziale e
poi modificato, quindi

<LinearLayout
android:layout_width=wrap_content
android:layout_height=wrap_content
android:orientation=horizontal
>
<TextView
android:layout_width=wrap_content
android:layout_height=wrap_content
android:text=@string/labelValue
android:layout_marginRight=10dp
/>
<TextView
android:id = @+id/idTxtResult
android:layout_width=wrap_content
android:layout_height=wrap_content
/>
</LinearLayout>
</LinearLayout>

Il file costituito da un LinearLayout verticale (ha


come attributo android:orientation=vertical)
questo indica che ogni componente che segue
verr aggiunto sotto quello precedente. Quindi
avremo un bottone sotto il quale viene messo
un altro LinearLayout contenente due TextView,
che in questo caso orizzontale, il che significa
che le due label saranno affiancate (vedremo
pi in dettaglio quando parleremo dei layout);
per ora concentriamoci sul passaggio dei para-

txtResult = (TextView)findViewById(R.id.idTxtResult);
txtResult.setText( + initValue);

Notare che abbiamo trasformato in String il valore initValue, perch il metodo setText prende
come pramatro una stringa, esiste un metodo
che prende un intero che per rappresenta lid
di una risorsa, quindi se gli passassimo un intero il SO andrebbe a cercare questo valore nella
classe R (che contiene tutti gli id) e non trovandolo genererebbe un errore.
Definiamo il bottone come fatto nel progetto
precedente ma cambiando il modo di invocare
lactivity:
Button btnGoToSecond = (Button)findViewById(R.id.idButton);
btnGoToSecond.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.
class);
intent.putExtra(com.example.navigatefromactivity.
PARAMETER, initValue);
startActivityForResult(intent, REQUEST_CODE);
}
});

metri.
Andiamo ora a vedere come deve essere la

Si vede che viene passato un parametro intent.

classe java.

putExtra(com.example.navigatefromactivity.

Definiamo le variablili di classe:

PARAMETER, initValue), che verr recuperato nella seconda Activity. Possiamo notare che

private final int REQUEST_CODE = 0;


private int initValue
= 1;
private TextView txtResult = null;

il parametro intero viene passato nello stesso modo come quello di tipo stringa (visto nel
progetto precedente) quello che cambia la
chiamata allActivity dove viene usato il meto-

{21

{22

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


do startActivityForResult(intent, REQUEST_

Il metodo verr inserito nel punto del codice

CODE);

scelto.

dove REQUEST_CODE un argomento che

Il plugin genera un metodo vuoto con la sola

identifica univocamente la richiesta.

chiamata al super, modifichiamo il codice come

Il risultato viene catturato nel metodo di callback

segue:

onActivityResult (int requestCode, int resultCode, Intent data):


questo include 3 argomenti:
requestcode: passato con la con startActivityForResult;
resultCode: codice del risultato passato
dalla seconda Activity. Pu assumere RESULT_OK se loperazione avvenuta con
successo o RESULT_CANCELED se loperazione fallita per qualche motivo.
data: lintent che porta i dati
Aggiungiamo il metodo di callback:
posizionarsi nel punto del codice dove si
vuole inserire il metodo
premere il tasto dx del mouse --> source->Override/Implement methods

@Override
protected void onActivityResult(int requestCode, int
resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (resultCode == RESULT_OK) {
int result = data.getIntExtra(com.example.
navigatefromactivity.PARAMETER_RESULT,
-1);
txtResult.setText( + result);
}
}
}

Il resultCode viene confrontato con il codice di


richiesta chiamante, perch la seconda activity potrebbe essere chiamata in pi punti e si
vorrebbe avere un comportamento diverso per
ognuno.
Subito dopo si controlla se il risultato corretto, se corretto allora viene recuperato il parametro inviato dalla seconda Activity

data.

getIntExtra(com.example.navigatefromactivity.
PARAMETER_RESULT, -1); e poi viene assegnato alla TextView.
Passiamo alla seconda activity.
Il file activity_second.xml risulta essere con un
selezionare onActivityResult(int,int,Intent) e

solo bottone:

premere ok
<?xml version=1.0 encoding=utf-8?>
<LinearLayout xmlns:android=http://schemas.
android.com/apk/res/android
android:layout_width=match_parent
android:layout_height=match_parent
android:orientation=vertical >
<TextView

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


android:layout_width=match_parent
android:layout_height=wrap_content
android:text=@string/labelSecondActivity

{23

Da cui si vede che sul click del bottone viene:


recuperato il parametro passato dalla prima activity int parameter = getIntent().
getIntExtra(com.example.navigatefromacti-

/>

vity.PARAMETER, -1);

<Button
android:id=@+id/idFinishButton
android:layout_width=wrap_content
android:layout_height=wrap_content
android:text=@string/labelClose
/>
</LinearLayout>

La classe della seconda activity si presenta

incrementato parameter += 1 ;
instanziato un nuovo intent Intent intent =
new Intent();
aggiunto il parametro da restituire: com.
example.navigatefromactivity.PARAMETER_RESULT
settato il risultato da ritornare al chiamante
setResult(RESULT_OK, intent);
fatto il finish dellactivity tramite il metodo fi-

come:

nish();
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button btnGoToSecond = (Button)findViewById(R.
id.idFinishButton);
btnGoToSecond.setOnClickListener(new
OnClickListener() {
@Override
public void onClick(View v) {
int parameter = getIntent().getIntExtra(com.example.
navigatefromactivity.PARAMETER,
-1);

Naturalmente vanno aggiunte le activity nel manifest


<activity
android:name=com.example.navigatefromactivity.
MainActivity
android:label=@string/app_name >
<intent-filter>
<action android:name=android.intent.action.MAIN
/>
<category android:name=android.intent.category.
LAUNCHER />
</intent-filter>
</activity>
<activity android:name=.SecondActivity />

parameter += 1 ;
Intent intent = new Intent();

A questo punto se si fa il run del progetto pos-

intent.putExtra(com.example.navigatefromactivity.

siamo vedere le activity:

PARAMETER_RESULT,
parameter);
setResult(RESULT_OK, intent);
finish();
}
});
}
}

{24

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

Activity appena creata

Seconda Activity incrementa


il valore

Prima Activity con parametro


incrementato

Si vede che premendo su chiudi della seconda


Activity si ritorna sulla prima ed il valore numeri-

Scarica codice sorgente

co aumentato.
Allegato

Dimensione

Riassumendo possiamo dire che in questo articolo abbiamo analizzato le Activity ed il loro
ciclo di vita ed abbiamo visto che giocano un

NavigateFromActivityForResult.zip 1003.51 KB

ruolo molto importante nello sviluppo delle applicazioni. Abbiamo visto anche come il sistema
operativo le gestisce e gli stati in cui si possono
trovare. E importante capire il funzionamento
delle Activity perch grazie a queste siamo in
grado di implementare applicazioni complesse.
Le Activity che abbiamo realizzato sono molto
semplici e per crearle abbiamo utilizzato dei Layout che vedremo pi in dettaglio nel prossimo
articolo.

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:
http://it.emcelettronica.com/corso-su-android-lavorare-con-le-activity

{25

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

3. Lavorare con i Layout

n layout definisce una struttura visuale per

pre

dichiarare

il

namespace

di

Android:

una interfaccia utente. Android separa la

xmlns:android=http://schemas.android.com/

parte visuale dalla parte logica, utilizzando

apk/res/android

per la prima dei file xml. I componenti grafici

cos tutti gli altri elementi ereditano questo na-

possono essere definiti anche da codice, ma

mespace.

per un fatto di leggibilit e manutenzione con-

Analizziamo gli attributi dei componenti utilizza-

sigliato tenere separate le due parti.

ti:

Il file xml che rappresenta il nostro layout pu

android:layout_width e android:layout_

essere creato manualmente o tramite strumen-

height rappresentano la larghezza e lal-

ti grafici quali DroidDraw e il plugin di Eclipse,

tezza del componente e possono assumere

per esperienza preferisco scrivere a mano i vari

valori: wrap_content: adatta alla dimensione

componenti nel layout. Una volta presa la mano

del componente; fill_parent: adatta il compo-

abbastanza veloce.

nente a tutto schermo; match_parent: come

Ogni layout contiene un solo elemento princi-

fill_parent ma dalle versioni recenti di an-

pale nel quale si possono aggiungere oggetti o

droid;

widgets.

android:orientation un attributo che in-

Riprendiamo il file activity_second.xml usato

dica se i componenti allinterno del layout

per la seconda Activity nel precedente tutorial

vengono aggiunti seguendo un allineamento

ed andiamo ad analizzarne le varie parti:

orizzontale o verticale
android:id indica lid che viene utilizzato per

<?xml version=1.0 encoding=utf-8?>


<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
android:layout_width=match_parent
android:layout_height=match_parent
android:orientation=vertical >
<TextView
android:layout_width=match_parent
android:layout_height=wrap_content
android:text=@string/labelSecondActivity
/>
<Button
android:id=@+id/idFinishButton
android:layout_width=wrap_content
android:layout_height=wrap_content
android:text=@string/labelClose
/>
</LinearLayout>

legare la parte visuale con la parte di codice.


La convenzione di usare @+id/ che sta a
significare che un nuovo id e va aggiunto
alla lista degli id nella classe R.
android:text setta il testo del componente;
come si vede, non stato scritto il testo direttamente ma stata definita una stringa ed
stata presa tramite @string/labelClose,
consigliato mettere tutte le stringhe nel file
string. In questo modo per aggiungere il supporto dellapplicazione alle altre lingue, basta aggiungere un altro file di stringhe nella
lingua desiderata
Una volta definito un elemento nellxml pu
essere instanziato nella classe che utilizza il

Il primo elemento di un file xml deve sem-

layout. Per associare il componente si deve

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


far riferimento allid ed utilizzare il metodo
findViewById(int) come mostrato di seguito:
Button btnGoToSecond = (Button)findViewById(R.id.idFinishButton);

Una volta associato il bottone, pu essere utilizzato nel codice, come visto nellesempio per intercettare levento del click da parte dellutente.
Android mette a disposizione vari tipi di layout
tra cui si pu scegliere per realizzare linterfaccia grafica della nostra applicazione. Andremo a
vedere i seguenti layout:
LinearLayout;
RelativeLayout;

In questo modo non verr n creata automaticamente unActivity n aggiornato il file AndroidManifest ma li andremo a definire noi.

TableLayout;

Infatti, se si espande il progetto sulla finestra

ListView;

Package Explorer si nota che le cartelle src e

GridLayout.

layout sono vuote.


Per prima cosa creiamo il package che deve

LinearLayout

coincidere con quello scelto al passo preceden-

Allinea tutti i figli in una singola direzione orizzon-

te:

tale o verticale. Questo layout lo abbiamo utilizzato nel progetto delle Activity. Creiamo un nuovo progetto che chiameremo LayoutProject, il
cui layout principale conterr dei bottoni allineati
in verticale ed alla pressione di questi andremo
a visualizzare vari altri tipi di layout.
Dal Menu cliccare su File-->New-->Android
Application Project
Sulla schermata inserire il nome del progetto
e premere Finish.

Premere il tasto dx sulla cartella src


Selezionare New-->Package
Inserire il nome del packeage com.example.
layoutproject
Premere Finish

{26

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Aggiungiamo ora la nuova Activity:
premere il tasto destro sul package appena
creato
selezionare New-->Class
Immettere il nome della classe LinearLayoutActivity
Selezionare come superclasse android.app.
Activity

Il file creato risulta essere:


<?xml version=1.0 encoding=utf-8?>
<LinearLayout
xmlns:android
= http://schemas.android.com/apk/res/android
android:layout_width = match_parent
android:layout_height = match_parent
android:orientation
= vertical >
</LinearLayout>

allinterno del quale, essendo il primo elemento,

Premere Finish

viene inserita la dichinarazione del namespace

Ora dobbiamo inserire il metodo onCreate.

di Android.

Posizioniamoci allinterno della classe e pre-

Creiamo prima i file e le variabili che ci servi-

miamo il tasto destro del mouse


Selesionare Source-->Override/Implements
Methods
Scegliere il medodo onCreate e premere Finish
Definiamo ora il file xml:
premere il tasto destro sulla cartella layout

ranno.
Nel file string.xml definiamo 5 label che verranno usate per i bottoni:
<string name=labelRelativeLayout>Relative Layout</string>
<string name=labelTableLayout>Table Layout</string>
<string name=labelListView>List view</string>
<string name=labelGridLayout>Grid Layout</string>

selezionare New-->Android XML file

Creare poi il file color.xml:

inserire il nome del file linear_layout_activity

premere il tasto destro del mouse sulla car-

(Bisogna fare attenzione al nome del file che


deve essere scritto in caratteri minuscoli)
selezionare come root element LinearLayout
premere Finish

tella values
selezionare New-->Android XML File
nella finestra che si apre nel campo Resource Type selezionare la voce Values
nel campo File scrivere il nome del file color.xml
tra i tag resouces definiamo il colore che
useremo per il testo del bottone <color nam
e=textButtonColor>#FF000000</color>
premere Finish

{27

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


<?xml version=1.0 encoding=utf-8?>
<resources>
<dimen name=marginTopButton>5dp</dimen>
</resources>

Abbiamo cos definito la dimensione che useremo per dare lo spazio ai bottoni.
Andiamo ora ad inserire 5 bottoni nel
LinearLayout creato, ad ogni bottone verr
associata unActivity con un layout diverso.
Lxml risulta essere:

Creiamo il file dimens.xml:


premere il tasto destro sulla cartella values
selezionare New-->File
inserire il nome dimens.xml
premere Finish

<?xml version=1.0 encoding=utf-8?>


<LinearLayout
xmlns:android = http://schemas.android.com/apk/res/android
android:layout_width = match_parent
android:layout_height = match_parent
android:orientation
= vertical >
<Button
android:id
= @+id/idRelative
android:layout_width = match_parent
android:layout_height = wrap_content
android:gravity
= center
android:text
= @string/labelRelativeLayout
android:textColor = @color/textButtonColor
android:layout_marginTop = @dimen/marginTopButton
/>
<Button
android:id
= @+id/idTable
android:layout_width = match_parent
android:layout_height = wrap_content
android:gravity
= center
android:text
= @string/labelTableLayout
android:textColor = @color/textButtonColor
android:layout_marginTop= @dimen/marginTopButton
/>

Cos viene creato un file vuoto, inserire il seguente codice:

<Button
android:id
= @+id/idList
android:layout_width = match_parent
android:layout_height = wrap_content
android:gravity
= center
android:text
= @string/labelListView
android:textColor = @color/textButtonColor
android:layout_marginTop= @dimen/marginTopButton
/>

{28

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


private Button btnGoToRelative = null;
private Button btnGoToTable = null;
private Button btnGoToListView = null;
private Button btnGoToGrid = null;

<Button
android:id
= @+id/idGrid
android:layout_width = match_parent
android:layout_height = wrap_content
android:gravity
= center
android:text
= @string/labelGridLayout
android:textColor = @color/textButtonColor
android:layout_marginTop= @dimen/marginTopButton
/>
</LinearLayout>

Recuperiamo i bottoni:

Il layout formato da 5 bottoni, analizziamo gli

pressione del bottone:

btnGoToRelative = (Button)findViewById(R.id.idRelative);

ed associamo un listener per intercettare la

attributi utilizzati:
android:id: id che useremo per associare il

btnGoToRelative.setOnClickListener(this);

widget al codice java


android:layout_width= match_parent:

Sul listener viene passato come riferimento this,

il bottone occuper tutta larghezza dello

questo perch vogliamo implementare il listener

schermo

nella stessa Activity, quindi modifichiamo la

android:layout_height=wrap_content:

definizione della classe come:

il bottone si adatta allaltezza


android:gravity = center: il testo viene
centrato rispetto al bottone
android:text=@string/labelRelativeLayout: assegna al testo del bottone quello

public class LinearLayoutActivity extends Activity implements


OnClickListener

ed implementiamo il metodo onClick:

definito nella stringa labelRelativeLayout


android:textColor= @color/textButtonColor:
assegna al testo il colore definito nella
variabile textButtonColor
android:layout_marginTop=@dimen/
marginTopButton:definisce

il

margine

superiore che il bottone deve avere e il


valore lo prende dalla dimensione definita
nel file apposito.
Una volta definiti i file xml andiamo ad associare

@Override
public void onClick(View v) {
if (v == btnGoToRelative){
Log.d(TAG,Button RelativeLayout pressed);
}else if (v == btnGoToTable){
Log.d(TAG,Button TableLayout pressed);
}else if (v == btnGoToListView){
Log.d(TAG,Button ListView pressed);
}else if (v == btnGoToGrid){
Log.d(TAG,Button GridLayout pressed);
}
}

questi bottoni al codice java. Riprendendo


la classe LinearLayoutActivity andiamo ad

Da notare che nel metodo viene effettuato il

associare il layout creato con listruzione:

controllo sulla view, cio sul bottone premuto.

setContentView(R.layout.linear_layout_

Per ora limitiamoci ad effettuare delle loggate,

activity);

in seguito invocheremo le Activity.

Aggiungiamo ora le definizioni dei bottoni:

Come ultima cosa, aggiungiamo lActivity

{29

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


nel manifest. Di seguito vediamo come
possibile inserire unActivity utilizzando gli
strumenti messi a disposizione dallIDE, ma si
pu anche tranquillamente inserire unActivity
manualmente.
fare doppio clic sul file AndroidManifest.xml
premere sul tab Application
premere sul bottone Add nella sezione
Application Nodes

Selezionare lActivity e premere il bottone


Add

Selezionare Activity e premere Ok


Selezionare Intent-Filter e premere Ok

Inserire il nome dellActivity

Sotto il nome dellActivity viene inserito un Intent-Filter (come visto nel secondo articolo),
quindi:
selezionare Intent-Filter

{30

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


premere il bottone Add

Salvare il manifest ed effettuare il run del


progetto; verr visualizzata la schermata seguente con il nostro LinearLayout:

Selezionare Action e premere Ok

RelativeLayout
Il RelativeLayout visualizza le view in posizioni
relative. Le view vengono posizionate a destra
di, sotto di etc. Per capire come funziona faccia Selezionare come action android.intent.action.MAIN
Selzionare di nuovo Intent-Filter e premere
Add
Selezionare Category
Selezionare come categoria android.intent.
category.LAUNCHER

mo un esempio. Vogliamo arrivare ad avere un


layout come quello mostrato in figura:

{31

{32

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Iniziamo aggiungendo il file relative_layout_activity.xml:
premere il tasto destro del mouse sulla cartella layout
selezionare New-->Android XML File
inserire il nome relative_layout_activity.xml
selezionare RelativeLayout nella sezione
Root Element
Premere Finish

android:layout_height
= wrap_content
android:layout_below
= @id/idDescription
android:layout_alignParentLeft = true
android:layout_toLeftOf
= @+id/idSurname
android:hint
= @string/hintName/>
<EditText
android:id
= @id/idSurname
android:layout_width
= 150dp
android:layout_height
= wrap_content
android:layout_below
= @id/idDescription
android:layout_alignParentRight = true
android:hint
= @string/hintSurname/>
<Button
android:layout_width
= 96dp
android:layout_height
= wrap_content
android:layout_below
= @id/Surname
android:layout_alignParentRight = true
android:text
= @string/labelBtnOk />
</RelativeLayout>

Come funziona: prima di tutto, notiamo che


ogni componente ha un id, questo ci serve per
mettere in relazione i vari elementi. Andiamo a
posizionare come prima la TextView che risulter in testa al nostro layout.
Posizioniamo lEditText con id idEdName sotto
la TextView con id idDescription android:layout_
below=

@id/idDescription,

posizioniamolo

alla sinistra dellEditText con id idSurneame


Aggiungiamo al file XML una label (TextView), due
campi di testo(EditText) ed un bottone(Button):
<?xml version=1.0 encoding=utf-8?>
<RelativeLayout xmlns:android=http://schemas.android.com/
apk/res/android
android:layout_width=match_parent
android:layout_height=match_parent >
<TextView
android:id
= @+id/idDescription
android:layout_width = fill_parent
android:layout_height = wrap_content
android:text
= @string/labelDescription
/>
<EditText
android:id
= @+id/idEdName
android:layout_width
= 0dp

definito subito dopo il nome usando lattributo


android:layout_toLeftOf= @+id/idSurname.
Ora, andiamo ad aggiungere nel file string.xml
le stringhe utilizzate nel layout:
<string name=labelDescription>Inserire Nome e Cognome</string>
<string name=hintName>Nome</string>
<string name=hintSurname>Cognome</string>
<string name=labelBtnOk>Ok</string>

Creiamo la nuova Activity come visto in precedenza dandole il nome RelativeLayoutActivity


ed aggiungiamo il RelativeLayaut creato

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


public class RelativeLayoutActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.relative_layout_activity);
}
}

TableLayout
Come dice il nome, TableLayout, permette di disporre i componenti in modo tabellare.
Iniziamo con il creare un nuovo file XML, premiamo con il tasto destro sulla cartella layout->New-->Android XM File

Andiamo ad aggiungere nel manifest lActivity

Inserire come nome table_layout_activity

appena create. Questo pu essere fatto ma-

Selezionare TableLayout

nualmente od utilizzando il plugin di Eclipse

Premere Finish

come gi visto precedentemente. In entrambi i


casi il risultato di aggiungere una riga nel manifest:
<activity android:name=RelativeLayoutActivity></activity>

Aggiorniamo ora il metodo onClick della classe


LinearLayout per richiamare la seconda Activity.
Come abbiamo gi visto, definiamo un intent
e chiamiamo il metodo startActivity(intent).
Il metodo onClick risulter essere:
@Override
public void onClick(View v) {
Intent intent = null;
if (v == btnGoToRelative){
Log.d(TAG,Button RelativeLayout pressed);
intent = new Intent(LinearLayoutActivity.this, RelativeLayoutActivity.class);
startActivity(intent);
}else if (v == btnGoToTable){
Log.d(TAG,Button TableLayout pressed);
}else if (v == btnGoToListView){
Log.d(TAG,Button ListView pressed);
}else if (v == btnGoToGrid){
Log.d(TAG,Button GridLayout pressed);
}
}

Effettuamio il run del progetto e premendo sul


bottone con label RelativeLayout visualizzeremo allActivity appena creata.

Verr creato il file con tag principale TableLayout. Prima di aggiungere elementi dobbiamo
precisare che ogni riga di questo layout viene indicata utilizzando il tag <TableRow> ,
quindi per una tabella con pi righe avremo pi
tag.
Modifichiamo il codice nel seguente modo:
<?xml version=1.0 encoding=utf-8?>
<TableLayout xmlns:android=http://schemas.android.com/
apk/res/android
android:layout_width=match_parent
android:layout_height=match_parent
android:stretchColumns=*>
<TableRow >
<TextView

{33

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


android:layout_width = fill_parent
android:layout_height = wrap_content
android:text
= @string/cell_1_1
android:gravity
= center
/>
<TextView
android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
= @string/cell_1_2
android:gravity
= center
/>
</TableRow>
<TableRow >
<TextView
android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
= @string/cell_2_2
android:gravity
= center
android:layout_column = 1
/>
</TableRow>
<TableRow >
<TextView
android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
= @string/cell_3
android:gravity
= center
android:layout_span = 2
/>
</TableRow>
</TableLayout>

questo fa in modo da effettuare lo stretch


orizzontale di tutte le celle della tabella, cos
da fargli occupare tutto lo spazio a disposizione.
Pu essere fatto lo stretch anche di una singola
cella mettendo al posto dell * il numero della
cella da espandere (ricordate che la numerazione parte da 0)
Definiamo nel file string.xml le costanti utilizzate:
<string name=cell_1_1>Cell 1_1</string>
<string name=cell_1_2>Cell 1_2</string>
<string name=cell_2_2>Cell 2_2</string>
<string name=cell_3>Cell 3 span</string>

Creiamo lActivity TableLayoutActivity ed associamo il layout appena creato:


public class TableLayoutActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.table_layout_activity);
}
}

Aggiungere la chiamata allAcrivity nel metodo


onClick della classe LinearLayoutActivity

La tabella composta da 3 righe delimitate dal


tag <TableRow> e 2 colonne. Nella prima riga
vengono assegnati i valori ad entrambe le celle
della tabella. Nella seconda riga viene popolata
solo la cella con coordinata 2,1 grazie allattributo
android:layout_column

= 1

Mentre con la terza riga abbiamo utilizzato lattributo android:layout_span= 2 che permette di
posizionare il componente nelle 2 celle. Da notare lattributo android:stretchColumns=*,

@Override
public void onClick(View v) {
Intent intent = null;
if (v == btnGoToRelative){
Log.d(TAG,Button RelativeLayout pressed);
intent = new Intent(LinearLayoutActivity.this, RelativeLayoutActivity.class);
startActivity(intent);
}else if (v == btnGoToTable){
Log.d(TAG,Button TableLayout pressed);
intent = new Intent(LinearLayoutActivity.this, TableLayoutActivity.class);
startActivity(intent);
}else if (v == btnGoToListView){
Log.d(TAG,Button ListView pressed);
}else if (v == btnGoToGrid){
Log.d(TAG,Button GridLayout pressed);
}

{34

{35

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Non ci resta che aggiungere al manifest lActi-

premere Finish

vity:
<activity android:name=TableLayoutActivity></activity>

e lanciare lapplicazione. Il risultato sar come


in figura:

Al file appena creato aggiungiamo come attributo lid: android:id= @+id/idList


Definiamo nel file string.xml un array che defini-

ListView
Visualizza una lista di items scrollabili. Gli items
sono inseriti automaticamente utilizzando un
adapter che prende i dati da un array o da un
database e trasformano ogni risultato in una
view da inserire nella lista. Faremo due esempi uno con un adaper standard ed uno con un
adapter custom.
Come per gli altri layout posizioniamoci sulla cartella layout e premiamo il tast destro del

sce i giorni della settimana che verranno passati


alladapter:
<string-array name=listOfDays>
<item>Lunedi</item>
<item>Martedi</item>
<item>Mercoledi</item>
<item>Giovedi</item>
<item>Venerdi</item>
<item>Sabato</item>
<item>Domenica</item>
</string-array>

creiamo una nuova activity (ListViewArrayA-

mouse -->New-->Android XML File. Nella fine-

dapterActivity);

stra aperta

recuperiamo

inseriamo come nome listview_layout_acti-

setContentView(R.layout.listview_layout_activi-

vity
selezioniamo ListView nella sezione RootElement

il

layout

da

utilizzare

ty);
recuperiamo loggetto ListView ed assegnamogli ladapter passandogli larray definito nel file
string.xml.

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


public class ListViewArrayAdapterActivity extends Activity {
private ListView list = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.listview_layout_activity);
String[] days = getResources().getStringArray(R.array.
listOfDays);
list = (ListView)findViewById(R.id.idList);
list.setAdapter(new ArrayAdapter<String>(this,
R.layout.text_item_list, days)); }
}

Nel manifest aggiungere lActivity creata:


<activity android:name=ListViewArrayAdapterActivity></
activity>

Ora bisogna creare il file text_item_list.xml che


dovr contenere la TextView da visualizzare sugli items della lista:
Premere il tasto destro sulla cartella layout
Selezionare New-->File (crea un file vuoto)
Inserire il nome del file text_item_list.xml
Premere Finish

Inserire il seguente codice:


<?xml version=1.0 encoding=utf-8?>
<TextView
xmlns:android
= http://schemas.android.com/apk/
res/android
android:layout_width
= match_parent
android:layout_height = match_parent
android:paddingTop = 5dp
android:paddingBottom = 5dp
/>

Aggiungere linvocazione alla classe nel metodo onClick della classe LinearLayoutActivity:
@Override
public void onClick(View v) {
Intent intent = null;
if (v == btnGoToRelative){
Log.d(TAG,Button RelativeLayout pressed);
intent = new Intent(LinearLayoutActivity.this, RelativeLayoutActivity.class);
startActivity(intent);
}else if (v == btnGoToTable){
Log.d(TAG,Button TableLayout pressed);
intent = new Intent(LinearLayoutActivity.this, TableLayoutActivity.class);
startActivity(intent);
}else if (v == btnGoToListView){
Log.d(TAG,Button ListView pressed);
intent = new Intent(LinearLayoutActivity.this, ListViewArrayAdapterActivity.class);
startActivity(intent);
}else if (v == btnGoToGrid){
Log.d(TAG,Button GridLayout pressed);
}
}

Effettuare il run del progetto, sulla lista premendo sul bottone listView va sulla schermata con
la lista appena creata:

{36

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


android:id

= @+id/idImage

android:layout_width = wrap_content
android:src

= @drawable/ic_launcher

android:layout_height = wrap_content
android:layout_gravity = center
/>
<TextView
android:id

= @+id/idText

android:layout_width = match_parent
android:layout_height = wrap_content
/>
</LinearLayout>

Creiamo un nuovo package dove andremo a


creare ladapter:
Ora che abbiamo visto come creare una sempli-

premere con il tasto destro sulla cartella src

ce lista passandogli un ArrayAdapter vediamo

selezionare New-->Package

come recuperare la scelta effettuata dalluten-

inserire il nome: com.example.layoutproject.

te (per esempio viene selezionato il giorno x)


e come associare creare una ListView passandogli un adapter custom. Realizzeremo questo

adapter
premere il tasto destro del mouse sul package

creando una nuova activity che verr visua-

selezionare New-->Class

lizzata premendo sul primo elemento della

inserire come nome CustomAdapter

lista (lunedi). Partiamo con il creare il layout

inserire come superclasse android.widget.

che utilizzeremo per visualizzare le righe della

BaseAdapter

lista. Come fatto in precedenza, creiamo il file

premere Finish

text_item_list_custom.xml
<?xml version=1.0 encoding=utf-8?>
<LinearLayout
xmlns:android

= http://schemas.android.com/

apk/res/android
android:layout_width

= match_parent

android:layout_height

= wrap_content

android:orientation

= horizontal

android:layout_marginTop = 10dp
android:paddingBottom

= 10dp

android:paddingTop

= 10dp

android:paddingLeft

= 5dp

android:layout_gravity = center
>
<ImageView

{37

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Viene creata la struttura della classe con i metodi obbligatori che devono essere implementati.
Vediamo come modificare la classe per i nostri
scopi.
Creiamo un costruttore a cui viene passato il
context ed inizializziamo un LayoutInflater che
permette di instanziare un layout allinterno di
una determinata View:
public CustomAdapter(Context context){
inflater = LayoutInflater.from(context);
}

Definiamo nello stesso file la classe ViewValues


che conterr un solo valore che assoceremo
con la TextView contenuta nel file text_item_
list_custom.xml
static class ViewValues {
TextView value;
}

Implementiamo il costruttore

@Override
public Object getItem(int position) {
return items[position];
}

Il metodo getItemId ritorna il row id associato


ad una specifica posizione nella lista. Per ora
non lo utilizziamo lasciamo che ritorna a zero.
@Override
public long getItemId(int position) {
return 0;
}

Il metodo getView costruisce la view da visualizzare allinterno di una riga. Grazie


allinflater viene inserito il layout, poi viene recuperato il componente TextView e gli viene
assegnato il testo preso dallarray items. Questi
metodi vengono chiamati in automatico da Android per popolare una lista, noi dobbiamo solo
implementarli.

public CustomAdapter(Context context){


inflater = LayoutInflater.from(context);
items
= new String[]{Gennaio,Febbraio,Marzo,Aprile,Maggio,Giugno,
Luglio,Agosto,Settembre, Ottobre, Novembre, Dicembre};
}

Ora andiamo a modificare i metodi implementati


dal plug-in:
il metodo getCount restituisce il numero di elementi della lista quindi andiamo a ritornare la
lunghetta degli elementi nellarray items.
@Override
public int getCount() {
return items.length;
}

Il metodo getItem restituisce lelemento in una


determinata posizione

@Override
public View getView(int position, View convertView,
ViewGroup parent) {
ViewValues values = null;
if (convertView == null) {
values = new ViewValues();
convertView = inflater.inflate(R.layout.text_item_list_
custom, null);
values.value
= (TextView)convertView.
findViewById(R.id.idText);
convertView.setTag(values);

}
else {
values = (ViewValues) convertView.getTag();
}

{38

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


values.value.setText( (String)items[position]);
return convertView;
}

Creiamo lActivity ListViewCustomAdapterActivity utilizzando lo stesso layout listview_layout_activity per la lista e instanziando il Custo-

arg1, int position,


long arg3) {
if ( position == 0){
Intent intent = new Intent(ListViewArrayAdapter
Activity.this, ListViewCustomAdapterActivity.class);
startActivity(intent);
}
}
});

mAdapter
public class ListViewCustomAdapterActivity extends Activity {
private ListView list = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview_layout_activity);
list = (ListView)findViewById(R.id.idList);
list.setAdapter(new CustomAdapter(this));
}
}

Inseriamo nel manifest lActivity create.


Torniamo alla classe ListViewArrayAdapterActivity, perch vogliamo passare alla nuova Activity dopo aver premuto il primo valore della lista,
quindi andiamo ad implementare il metodo per
intercettare quale elemento della lista stato
premuto:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview_layout_activity);
String[] days = getResources().getStringArray(R.array.
listOfDays);
list = (ListView)findViewById(R.id.idList);
list.setAdapter(new ArrayAdapter<String>(this,
R.layout.text_item_list, days));
list.setOnItemClickListener(new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> arg0, View

GridLayout
Visualizza gli elementi in una griglia in due dimensioni. Faremo visualizzare delle immagini
e alla pressione di queste verr visualizzata la
posizione con un Toast.
Come prima cosa creiamo il file grid_layout_activity.xml
<?xml version=1.0 encoding=utf-8?>
<GridView xmlns:android=http://schemas.android.com/
apk/res/android
android:id=@+id/gridview
android:layout_width=match_parent
android:layout_height=match_parent
android:columnWidth=90dp

{39

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


android:numColumns=auto_fit
android:verticalSpacing=10dp
android:horizontalSpacing=10dp
android:stretchMode=columnWidth
android:gravity=center
>
</GridView>

Creiamo la classe CustomGridAdapter nello


stesso package di CustomAdapter estendendo
sempre BaseAdapter e, come in precedenza,

ImageView imageView;
if (convertView == null) { // if its not recycled, initialize
some attributes
imageView = new ImageView(context);
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}

verranno creati i metodi obbligatori da imple-

imageView.setImageResource(imgIds[position]);
return imageView;

mentare.
La classe risulta essere:
public class CustomGridAdapter extends BaseAdapter {

Dove imgIds un array contenente gli id delle

private Context context = null;

immagini che andremo ad utilizzare, il metoto

private Integer[] imgIds = {


R.drawable.img0, R.drawable.img1,
R.drawable.img2, R.drawable.img3,
R.drawable.img4, R.drawable.img5,
R.drawable.img6, R.drawable.img7,
R.drawable.img8, R.drawable.img9
};

getCount ritorna il numero di elementi nella gri-

public CustomGridAdapter(Context context){


this.context = context;
}
@Override
public int getCount() {
return imgIds.length;
}
@Override
public Object getItem(int position) {
return imgIds[position];
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView,
ViewGroup parent) {

glia e il metodo getItem ritorna lelemento in posizione position della lista.


Aggiungiamo ora le immagini sotto la cartella
drawable.
Andiamo a creare lactivity GridViewActivity
come visto precedentemente, da cui andremo a
richiamare ladapeter per popolare la grid

public class GridViewActivity extends Activity {


private GridView gridview = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.grid_layout_activity);
gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new CustomGridAdapter(this));
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent,
View v, int position, long id) {
Toast.makeText(GridViewActivity.this, + posi

{40

{41

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


tion, Toast.LENGTH_SHORT).show();
}
});
}

Allegato
imgsgridlayout.zip

Dimensione
20.48 KB

LayoutProject.zip

988.61 KB

Nel metodo onClick della classe LinearLayoutActivity inserire lintent per il passaggio alla
nuova activity
intent = new Intent(LinearLayoutActivity.this, GridViewActivity.class);
startActivity(intent);

Non dimentichiamoci di definire lActivity nel


manifest e poi effettuare il run del progetto.
<activity android:name=GridViewActivity></activity>

Per tutti voi, ecco il link da cui potete scaricare


il sorgente.
Alla prossima.
Per ogni dubbio o domanda, resto a vostra
disposizione nei commenti.

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:

http://it.emcelettronica.com/corso-su-android-lavorare-con-i-layout

{42

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

4. UIControls

ndroid fornisce unampia variet di componenti visuali per disegnare interfacce grafi-

ProgressBar

Utilizzato per dare un


feedback di avanzamento
allutente

Spinner

Una lista espandibile che permette allutente di selezionare


un valore

TimePicker

Permette allutente di selezionare le ore

DatePicker

Permette allutente di selezionare una data

che e permettere allutente di interagire con

lapplicazione e alcuni di questi li abbiamo visti


negli esempi precedenti.
Abbiamo visto i layout che ci permettono di posizionare diversi tipi di View, dove per view si
intende un oggetto che disegna qualcosa sullo
schermo e permette allutente di interagire con
lapplicazione
Le view vengono definite allinterno di un file

Analizziamoli

xml, come abbiamo visto negli esempi prece-

esempi pratici. Creiamo un progetto con unac-

denti.

tivity in cui inseriremo i vari componenti ed in-

Di seguito elenchiamo i componenti messi a di-

trodurremo un nuovo layout, ScrollView, non

sposizione da Android per costruire le interfacce grafiche:


TextView

Utilizzato per visualizzare del


testo allutente

EditText

Campo editabile per dare la


possibilit allutente di inserire valori

una view molto simile allEditText eccetto per il fatto che


AutoCompletevisualizza suggerimenti per
TextView
completare automaticamente
la parola
Button

Pu essere premuto o cliccato per far eseguire unazione


allutente

CheckBox

Componente che pu assumere valori on/off.

RadioButton

Componente con due stati:


selezionato o deselezionato

RadioGroup

Usato per raggruppare i RadioButton

ImageButton

Button con unimmagine che


pu essere premuto

ToggleButton

Un bottone con un indicatore


on/off

singolarmente

facendo

degli

ancora utilizzato ma utile se i componenti che


compongono la nostra view superano la dimensione dello schermo.
Creiamo un progetto che chiameremo UIControlsProject:
premere menu File-->New--> Android Application Project

inserire il nome UICOntrolsProject

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


premere Next

deselezionare la voce Create custom launcher icon


premere Next

lasciare la scelta di default


premere Next
nella schermata successiva premere Finish

Quando si crea il progetto, il plugin non da la


possibilit di scegliere il layout del file activity_
main.xml, quindi apriamo il file e sostituiamo il
codice con:
<ScrollView
xmlns:android = http://schemas.android.com/apk/res/android
xmlns:tools
= http://schemas.android.com/tools
android:layout_width = match_parent
android:layout_height = match_parent
>
<LinearLayout
android:layout_width = match_parent
android:layout_height = wrap_content
android:orientation = vertical
>
<TextView
android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
= @string/hello_world />
</LinearLayout>
</ScrollView>

La struttura molto semplice, c uno ScrollView generale, seguito da un LinearLayout su


cui andremo ad aggiungere tutti i componenti.
Attenzione: lo ScrollView pu contenere un
solo figlio. Nel nostro caso un LinearLayout

TextView
Il TextView un componente grazie al quale

{43

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


possibile visualizzare del testo allutente. Ve-

name=password_hint>Password</string>

dendo il progetto appena creato si nota che il

e lanciamo lapplicazione otterremo il risultato

plugin mette una TextView automaticamente,

in figura:

infatti abbiamo riprodotto il progetto HelloWorld


visto nel primo tutorial.
<TextView
android:layout_width=wrap_content
android:layout_height=wrap_content
android:text=@string/hello_world />

EditText
Il componente EditText permette ad un utente
di digitare del testo nellapplicazione. Pu essere costituito da una singola linea o da pi
linee. Per scrivere allinterno del campo basta
toccare il componente cos da far apparire la
tastiera. Il campo pu assumere diversi tipi di

Toccando sul campo appare la tastiera e pro-

inserimento(number, date, password) che de-

vando a digitare dei caratterei si pu notare che

terminano quali caratteri possono essere inseriti

i caratteri digitati appaiono come asterischi per-

e quale tastiera far apparire per ottimizzare lin-

ch il campo definito come textPassword.

serimento.
Un esempio di EditTetxt pu essere il seguente:
<EditText
android:id
= @+id/password
android:layout_width = fill_parent
android:layout_height= wrap_content
android:hint
= @string/password_hint
android:inputType = textPassword />

dove lattributo inputType definisce il campo


come tipo password, il che significa che i caratteri che verranno digitati saranno sostituiti da
un *.
Ci sono diversi altri tipi di input che possono essere utilizzati: text, textEmailAddress,
texture, number, phone, etc.
Copiamo il codice visto sopra dellEditText subito sotto la TextView nel file activity_main.
xml del nostro progetto, aggiungiamo nel file
string.xml la definizione della stringa <string

AutoCompleteTextView
Se in un campo di testo si vogliono dare dei suggerimenti possibile utilizzare una sottoclasse
di EditText chiamata AutoCompleteTextView.
Per implementare questo campo occorre definire un adapter che fornisce i suggerimenti.
Di seguito vediamo come possibile implementare un AutoCompleteTextView utilizzando un
ArrayAdapter. Inserite il codice seguente sotto
la definizione dellEditText gi immesso in precedenza:
<AutoCompleteTextView
style
= @android:style/Theme.Black
android:id
= @+id/idColor
android:layout_width
= fill_parent
android:layout_height
= wrap_content
android:completionThreshold = 1
/>

Definire un array che contiene tutti I suggerimenti. Definire un array in (res/values/strings.

{44

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


xml):

Button and ImageButton

<?xml version=1.0 encoding=utf-8?>


<resources>
<string-array name=color_array>
<item>Red</item>
<item>Green</item>
<item>Blue</item>
<item>Black Samoa</item>
</string-array>
</resources>

NellActivity usare il codice seguente:

Un bottone pu essere visto come un testo,


unimmagine o entrambi e se viene premuto
scatena un evento che pu essere intercettato
per effettuare unazione.
A seconda di come si vuole il bottone (testo,
immagine, testo e immagine) possiamo crearlo
come segue:
solo testo utilizzando la classe Button:
<Button
android:id

= @+id/idButton1

AutoCompleteTextView textView = (AutoComplete-

android:layout_width = wrap_content

TextView) findViewById(R.id.idColor);

android:layout_height = wrap_content

// recupera larray di stringhe


String[] colors = getResources().getStringArray(R.array.

android:text

= @string/button_text

/>

color_array);
// Crea ladapter
ArrayAdapter<String> adapter = new

con unicona utilizzando la classe ImageButton

ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, colors);
textView.setAdapter(adapter);

<ImageButton
android:layout_width = wrap_content
android:layout_height = wrap_content

Abbiamo cos creato un ArrayAdapter che lega


ogni item dellarray di stringhe con la TextView.
Effettuando il run del progetto si pu vedere
come, digitando sul campo, vengano presentati

android:src

= @drawable/button_icon

/>

con testo ed icona utilizzando la classe But-

i suggerimenti disponibili:

ton e lattributo android:drawableLeft:


<Button
android:layout_width=wrap_content
android:layout_height=wrap_content
android:text=@string/button_text
android:drawableLeft=@drawable/button_icon
/>

Quando abbiamo creato un bottone in uno dei


modi visti sopra, bisogna implementare un listener per intercettare levento di pressione del
tasto.
Android mette a disposizione due modi per definire un evento di click:

{45

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


direttamente dallxml, aggiungendo lattri-

plementato il metodo pressMethod e onClickLi-

buto android:onClick, il cui valore sar un

stener e definito le stringhe nel file string.xml, si

metodo definito nellActivity

pu far girare il progetto ed il risultato quello


in figura:

<Button
android:id

= @+id/idButton1

android:layout_width

= wrap_content

android:layout_height

= wrap_content

android:text
android:onClick

= @string/button_text
= pressMethod

/>

da codice, mediante limplementazione del


metodo onClickListaner
Questa modalit quella vista negli esempi degli articoli precedenti, dove viene recuperato il
button dallxml e poi associato ad un listener
ImageButton btnImg = (ImageButton)findViewById(R.
id.idImageButton);
btnImg.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
pressMethod(v);
}
});
Button btn2 = (Button)findViewById(R.id.idButton2);
btn2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
pressMethod(v);
}
});

Checkbox
I checkbox sono dei componenti che permettono allutente di poter scegliere una o pi opzioni
di una lista. Questi possono essere creati nel
file di layout utilizzado il conponente CheckBox.
Inseriamo, nel nostro file, i due checkbox, notando che il secondo risulter spuntato grazie
allattributo: android:checked= true
Per definire un evento di click si hanno due modi
come nel caso del Button: i due metodi visti nei
casi precedenti per intercettare gli eventi (ormai
abbiamo capito come funzionano) ed andiamo
ad utilizzare lattributo android:onClick nel tag

Dove il metodo pressMethod definito come:


public void pressMethod(View view){
Toast.makeText(this, getString(R.string.labelPressed),
Toast.LENGTH_SHORT).show();
}

Dopo aver inserito i componenti nel file xml,


sempre sotto quelli precedenti, dopo aver im-

<CheckBox>
<CheckBox
android:id
= @+id/idCheck1
android:layout_width
= wrap_content
android:layout_height
= wrap_content
android:text
= @string/labelCheckBox1
android:onClick
= performCheckBoxOnClick
/>
<CheckBox
android:id
= @+id/idCheck2

{46

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

android:layout_width
= wrap_content
android:layout_height
= wrap_content
android:text
= @string/labelCheckBox2
android:checked
= true
android:onClick
= performCheckBoxOnClick
/>

selezionato</string>

si pu eseguire il run del progetto e verr visualizzata la schermata seguente:

Nella classe definiamo il metodo


public void performCheckBoxOnClick(View view) {
//Check if the view is checked
boolean checked = ((CheckBox) view).isChecked();
switch(view.getId()) {
case R.id.idCheck1:
if (checked){
Toast.makeText(this, getString(R.string.labelChecked1),
Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, getString(R.string.labelNotChecked1),
Toast.LENGTH_SHORT).show();
}
break;
case R.id.idCheck2:
if (checked){
Toast.makeText(this, getString(R.string.labelChecked2),
Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, getString(R.string.labelNotChecked2),
Toast.LENGTH_SHORT).show();
}
break;
}
}

RadioButton and RadioGroup


Permette allutente di selezionare unopzione da
un insieme e la scelta mutualmente esclusiva,
ossia tra n opzioni solo una selezionabile e le
altre sono escluse (diversamente dai checkbox
dove possibile effettuare selezioni multiple).
Per creare un radio button si utilizza il tag RadioButton, per, essendo mutualmente esclusi-

dove viene controllato se il checkbox premuto

vo, viene inserito allinterno del tag RadioGroup.

selezionato o meno e viene visualizzato un

Anche per i RadioButton si utilizzano i due me-

messaggio. Dopo aver definito le stringhe ne-

todi visti nei casi precedenti per intercettare

cessarie nel file string.xml

gli eventi. Ormai abbiamo capito come funzio-

<string name=labelCheckBox1>Check 1</string>


<string name=labelCheckBox2>Check 2</string>
<string name=labelChecked1>Il CheckBox 1 selezionato</string>
<string name=labelNotChecked1>Il CheckBox 1 non
selezionato</string>
<string name=labelChecked2>Il CheckBox 2 selezionato</string>
<string name=labelNotChecked2>Il CheckBox 2 non

nano per cui andiamo ad utilizzare lattributo


android:onClick nel tag <RadioButton> ed
inserendo lattributo android:checked= true
nel primo RadioButton cos da indicare che
selezionato di default

{47

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


<RadioGroup
android:layout_width
= fill_parent
android:layout_height
= wrap_content
android:orientation
= vertical>
<RadioButton
android:id
= @+id/idRadio1
android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
= @string/labelRadio1
android:onClick
= onRadioButtonClicked
android:checked
= true/>
<RadioButton
android:id
= @+id/idRadio2
android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
= @string/labelRadio2
android:onClick
= onRadioButtonClicked/>/>
</RadioGroup>

Effettuano il run del progetto si ottiene il risultato


in figura:

Definiamo il metodo:
public void onRadioButtonClicked(View view) {
boolean checked = ((RadioButton) view).isChecked();
switch(view.getId()) {
case R.id.idRadio1:
if (checked){
Toast.makeText(this, getString(R.string.labelRadioCheched1),
Toast.LENGTH_SHORT).show();
}
break;
case R.id.idRadio2:
if (checked){
Toast.makeText(this, getString(R.string.labelRadioCheched2),
Toast.LENGTH_SHORT).show();
}
break;
}
}

ed inseriamo le stringhe
<string name=labelRadio1>Radio 1</string>
<string name=labelRadio2>Radio 2</string>
<string name=labelRadioCheched1>Il RadioButton 1
selezionato</string>
<string name=labelRadioCheched2>Il RadioButton 2
selezionato</string>

ToggleButton
Un toggle button permette allutente di selezionare due stati (on/off).
Per vedere come intercettare lhandler utilizziamo lattributo android:onClick nel tag <ToggleButton>
<ToggleButton
android:id
= @+id/idToggle
android:layout_width
= wrap_content
android:layout_height
= wrap_content
android:textOn
= @string/labelToggleOn
android:textOff
= @string/labelToggleOff
android:onClick
= onToggleOnClick
/>

Si nota che vengono definiti due test uno per lo


stato on e laltro per lo stato off. Definiamo il
metodo
Il risultato dopo aver definito le stringhe il seguente:

{48

{49

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Abbiamo recuperato lo Spinner dal file xml,
abbiamo creato un arrayAdapter grazie al metodo createFromResource() e definito il layout
per visualizzare la lista con la scelta usando
setDropDownViewResource()
Andiamo, adesso, a definire un Listener per intercettare gli eventi scatenati dalla pressione su
un elemento della lista
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int pos, long id) {
Toast.makeText(context,selezionato [ +
(String)adapterSpinner.getItem(pos)+ ], Toast.LENGTH_
SHORT).show();
}

Spinner

@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});

Un componente spinner permette di seleziona-

Eseguendo il run del progetto si ottiene la scher-

re un valore da una lista, infatti appena viene

mata precedente con laggiunta alla fine dello

premuto viene visualizzata una lista di opzioni

Spinner, premendo il quale si ottiene la scher-

da scegliere.

mata successiva:

Questo pu essere aggiunto nel layout utilizzando il tag Spinner:


<Spinner
android:id
= @+id/idColors
android:layout_width = fill_parent
android:layout_height = wrap_content
/>

Per popolare lo Spinner si pu utilizzare un ArrayAdapter. Utilizziamo lo <string-array> impiegato per lesempio su AutoCompleteTextView
Spinner spinner = (Spinner) findViewById(R.id.idSpinnerColors);
final ArrayAdapter<CharSequence> adapterSpinner = ArrayAdapter.createFromResource(this,
R.array.color_array, android.R.layout.simple_spinner_item);
adapterSpinner.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapterSpinner);

Notate che la visualizzazione di questo


componente varia a
seconda del SO; infatti

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


la schermata sopra stata presa da un HTC
Desire con Android 2.2.2 mentre quella succes-

<TableLayout
android:layout_width = fill_parent

siva da un S3 con Android 4.1.2

android:layout_height = wrap_content
android:stretchColumns = 0
>
<TableRow >
<EditText
android:id

= @+id/idDatePicker

android:layout_width = fill_parent
android:layout_height = wrap_content
/>
<Button
android:id

= @+id/idBtnOpenDatePicker

android:layout_width = wrap_content
android:layout_height = wrap_content
android:text

= @string/btnLabelOpenDatePicker

/>
</TableRow>
</TableLayout>

Notare che nel tag TableLayout abbiamo mes-

TimePicker DatePicker

so lattributo android:stretchColumns= 0,visto

Android mette a disposizione dellutente dei

nellarticolo dove abbiamo parlato dei layout,

componenti grafici che permettono di sceglie-

che allarga la prima colonna cos da far occupa-

re lora in ore-minuti-AM/PM (TimePicker) e la

re pi spazio al campo di testo.

data in giorno-mese-anno (DatePiker). Grazie

Ora nel file string.xml definiamo la stringa

a questi componenti si forza lutente a scegliere


una data ed unora valida e formattata correttamente.

<string name=btnLabelOpenDatePicker>Set
Date</string>

Entrambi i componenti possono essere utilizzati

Nota: Dalla versione 3.0 sono state introdotte

direttamente nel layout oppure visualizzati tra-

nuove classi per gestire il ciclo di vita dei Da-

mite un dialog. Noi vedremo la seconda modali-

tePicker e TimePicker. Andremo ad utilizzare

t che , di solito, la pi utilizzata.

DialogFragment introdotto dalla versione 3.0 di

Per vedere come funziona inseriamo nel nostro


layout un EditText con vicino un Button e premendolo verr visualizzato il DatePicker. Una

Android ma reso retrocompatibile fino alla versione 1.6.

volta selezionata la data, essa verr visualizza-

Iniziamo con il creare una nuova classe Date-

ta nel campo di testo. Per far questo utilizziamo

PickerFragment che estender il nostro Dialog-

un TableLayout con una riga al cui interno vi

Frament e visualizzer il DataPicker

un EditText ed un Button. Il codice il seguente


e va inserito nel file xml:

{50

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

public class DatePickerFragment extends DialogFragment {


public static final String YEAR = year;
public static final String MONTH = month;
public static final String DAY = day;
private OnDateSetListener listener = null;
private int year;
private int month;
private int day;
public DatePickerFragment() {}
//setta il listener per la risposta
public void setCallBack(OnDateSetListener listener) {
this.listener = listener;
}
//setta anno mese e giorno da passare al DatePicket
@Override
public void setArguments(Bundle args) {
super.setArguments(args);
year = args.getInt(YEAR);
month = args.getInt(MONTH);
day = args.getInt(DAY);
}
//visualizza il dialog con il DatePicker
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new DatePickerDialog(getActivity(), listener, year, month,
day);
}
}

Passiamo ora alla classe principale e come


prima cosa cambiamo la classe con cui viene

edDate = (EditText)findViewById(R.id.idDatePicker);

recuperare il Button dal layout ed associargli un


listener.
Andiamo ad aggiungere il metodo per visualizzare il Dialog ed il listener per intercettare il risultato scelto.

private void showDatePicker() {


//instanzia la classe
DatePickerFragment date = new DatePickerFragment();
/*recupera i valori per visualizzare la data attuale */
Calendar calender = Calendar.getInstance();
Bundle args = new Bundle();
args.putInt(DatePickerFragment.YEAR, calender.get(Calendar.
YEAR));
args.putInt(DatePickerFragment.MONTH, calender.get(Calendar.
MONTH));
args.putInt(DatePickerFragment.DAY, calender.get(Calendar.DAY_OF_
MONTH));
date.setArguments(args);
date.setCallBack(listener);
date.show(getSupportFragmentManager(), DatePicker);
}
OnDateSetListener listener = new OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,int
dayOfMonth) {
edDate.setText(dayOfMonth + / + monthOfYear + / + year);

estesa mettendo al posto di Activity la classe


FragmentActivity

}
};

public class MainActivity extends FragmentActivity{.....

Nel metodo showDatePicker viene instanziata


definiamo la variabile

la classe da noi definita DatePickerFragment,


viene recuperata la data attuale tramite logget-

private EditText edDate = null;

{51

to Calendar e viene passato alla classe tramite un Bundle. Alla fine viene invocato il metodo

recuperiamo il campo di testo nel metodo On-

show per visualizzare il DatePicker. Viene set-

Create:

tato il listener definito da noi come metodo di


callBack cos, nel momento in cui viene scelta

{52

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


la data, viene invocato questo metodo al cui interno vengono recuperati i dati di mese, giorno

public class TimePickerFragment extends DialogFragment {


public static final String HOUR

e anno, viene costruita una stringa e assegnata

= hour;

public static final String MINUTE = minute;

allEditText.
Per finire, inseriamo il codice che prende il But-

private OnTimeSetListener listener = null;

ton ed implementa il metodo onClick al cui inter-

private int hour;

no si richiama il metodo showDatePicker

private int minute;

Button openDP = (Button)findViewById(R.


id.idBtnOpenDatePicker);
openDP.setOnClickListener(new OnClickListener() {

public TimePickerFragment() {}
//setta il listener per la risposta
public void setCallBack(OnTimeSetListener listener) {
this.listener = listener;
}

@Override
public void onClick(View v) {
showDatePicker();
}
});

//setta ora e minuti da passare al TimePicker


@Override
public void setArguments(Bundle args) {
super.setArguments(args);
hour = args.getInt(HOUR);
minute = args.getInt(MINUTE);
}
//visualizza il dialog con il TimePicker
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
return new TimePickerDialog(getActivity(), listener,
hour,
minute,DateFormat.
is24HourFormat(getActivity()));
}
}

Dove il metodo DateFormat.is24HourFormat reIl TimePicker praticamente molto simile al

cupera le informazioni settate per lora di siste-

DataPicker ed anche il modo di invocazione

ma, ossia se lutente visualizza lora con forma-

analogo. Andremo pi spediti:

to 24 ore oppure 12 ore. Questo influisce sulla

creiamo la classe TimePickerFragment:

visualizzazione del TimePiker.


Aggiungiamo nel file xml:

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

<TableLayout
android:layout_width = fill_parent
android:layout_height = wrap_content
android:stretchColumns = 0
>
<TableRow >
<EditText
android:id
= @+id/idTimePicker
android:layout_width = fill_parent
android:layout_height = wrap_content
/>
<Button
android:id
= @+id/idBtnOpenTimePicker
android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
= @string/btnLabelOpenTimePicker
/>
</TableRow>
</TableLayout>

Aggiungiamo la stringa nel file string.xml


<string name=btnLabelOpenTimePicker>Set Time</
string>

Calendar calender = Calendar.getInstance();


Bundle args = new Bundle();
args.putInt(TimePickerFragment.HOUR, calender.
get(Calendar.HOUR_OF_DAY));
args.putInt(TimePickerFragment.MINUTE, calender.
get(Calendar.MINUTE));
date.setArguments(args);
date.setCallBack(listenerTime);
date.show(getSupportFragmentManager(), TimePicker);
}
OnTimeSetListener listenerTime = new OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay,
int minute) {
edTime.setText(hourOfDay + : + minute);
}
};

A questo punto non resta che eseguire il run del


progetto.

Aggiungere nellactivity:
edTime = (EditText)findViewById(R.id.idTimePicker);

ed i metodi:
Button openTP = (Button)findViewById(R.
id.idBtnOpenTimePicker);
openTP.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
showTimePicker();
}
});
private void showTimePicker() {
TimePickerFragment date = new TimePickerFragment();
/*recupera i valori per visualizzare la data attuale */

Conclusioni
Dopo aver visto come funzionano i Layout ed
i tipi pi comuni di componenti grafici, siamo in
grado di creare applicazioni graficamente com-

{53

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


plesse che non hanno nulla da invidiare a quelle
che si trovano sul market.
Seguiteci nelle prossime puntate dove vedremo
dei progetti in cui applicare le nozioni acquisite
fino ad ora che stanno alla base della programmazione in ambiente Android, grazie alle quali
possibile iniziare a sviluppare applicazioni complesse.
In ultimo, sapppiate che il prossimo mese questo corso far una breve pausa. Ma non preoccupatevi: solo per tornare a stupirvi a Marzo
con i progetti. S perch la parte introduttiva
ormai conclusa, il grosso dei concetti e degli
strumenti sono stati illustrati.
Ovviamente, sono e resto a vostra disposizione
qui nei commenti per ogni genere di chiarimento
e/o domanda sul corso e se ci dovessero essere richieste di approfondimenti potremo certamente discuterne insieme.
Anzi, se avete delle preferenze, delle richieste
specifiche, su app da sviluppare nel corso della
seconda parte, o volete voi stessi proporre del
codice o lo sviluppo di unapplicazione, siete i
benvenuti.

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:

http://it.emcelettronica.com/corso-su-android-uicontrols

{54

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

5. Creiamo la nostra applicazione


per automatizzare il calcolo del valore delle resistenze

ome dicevamo in apertura, in questo articolo


realizzeremo il calcolo per resistenze con 4
e 5 anelli lasciando per esercizio a voi lettori

limplementazione per 6 anelli.


Lidea quella di realizzare un Activity principale con due pulsanti da cui verranno richiamate le
Activity che si occuperanno del calcolo del valore
della resistenza per 4 e 5 anelli.

Come si vede le due Activity sono molto simili,


la cosa che cambia laggiunta di un campo per
il terzo anello e limmagine della resistenza pi
grande per ospitare un anello in pi.

4 Anelli
Iniziamo ad implementare il calcolo per una resistenza con 4 anelli riferendoci ai valori riportati
Le Activity hanno una grafica semplice da utiliz-

nella seguente tabella:

zare, si presentano con degli Spinner da cui poter


selezionare il colore degli anelli e una resistenza
che visualizza graficamente i colori degli anelli che
cambiano dinamicamente a seconda del valore
selezionato negli Spinner, dando cos un riscontro
visivo. Di seguito il risultato per la schermata che
si occupa del calcolo per 4 anelli e per 5 anelli
dalla quale si vede che se abbiamo una resistenza con colori: GIALLO-ROSSO-VERDE-BLU il

{55

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


valore della resistenza 4.2Mohms con una tolleranza dello 0.25%.
Iniziamo con il creare un progetto (ResistanceProject) e creiamo il layout, calc_four_ring_activity.xml, relativo alla nostra Activity per il calcolo
del valore della resistenza con 4 anelli. Strutturiamolo in modo da avere 4 Spinner, uno per ogni
anello e sotto una resistenza con 4 anelli di cui
andremo a cambiare il colore dinamicamente.
<?xml version=1.0 encoding=utf-8?>
<LinearLayout
xmlns:android
= http://schemas.android.com/apk/res/
android
android:layout_width = match_parent
android:layout_height = match_parent
android:orientation = vertical
>
<TableLayout
android:layout_width
= match_parent
android:layout_height
= wrap_content
android:stretchColumns
= 1
android:shrinkColumns
= 0
android:layout_marginLeft = 5dp
android:layout_marginRight = 5dp
>
<TableRow >
<TextView
android:layout_width = match_parent
android:layout_height = wrap_content
android:text
= @string/labelRing1
/>
<Spinner
android:id
= @+id/idRing1
android:layout_width = match_parent
android:layout_height = wrap_content
/>
</TableRow>
<TableRow >
<TextView
android:layout_width = match_parent
android:layout_height = wrap_content
android:text
= @string/labelRing2
/>
<Spinner
android:id
= @+id/idRing2
android:layout_width = match_parent
android:layout_height = wrap_content
/>
</TableRow>
<TableRow >
<TextView
android:layout_width = match_parent
android:layout_height = wrap_content
android:text
= @string/labelMultiplier
/>
<Spinner

android:id
= @+id/idMultiplier
android:layout_width = match_parent
android:layout_height = wrap_content

/>
</TableRow>
<TableRow >
<TextView
android:layout_width = match_parent
android:layout_height = wrap_content
android:text
= @string/labelTollerance
/>
<Spinner
android:id
= @+id/idTolleance
android:layout_width = match_parent
android:layout_height = wrap_content
/>
</TableRow>
</TableLayout>
<include layout=@layout/resistance4 android:layout_
gravity=center/>
</LinearLayout>

Dopo aver posizionato gli Spinner concentriamoci


sul disegno della resistenza. Per questa creiamo
un layout separato (resistance4.xml) che andremo ad includere dopo il TableLayout come riportato nel codice visto sopra.
<?xml version=1.0 encoding=utf-8?>
<LinearLayout
xmlns:android
= http://schemas.android.com/apk/
res/android
android:layout_width = wrap_content
android:layout_height = wrap_content
android:layout_gravity = center
android:orientation
= vertical
android:layout_marginTop = 20dp
>
<FrameLayout
android:layout_width = match_parent
android:layout_height = wrap_content
android:minHeight
= @dimen/resistence_min_
height
>
<ImageView
android:layout_height = wrap_content
android:layout_width = wrap_content
android:src
= @drawable/resistenza_4
android:layout_marginTop = 1dp
/>
<ImageView
android:id
= @+id/idImgRing1
android:layout_height
= wrap_content
android:layout_width
= wrap_content

{56

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


android:background
= @drawable/black
android:layout_marginLeft = 30dp
android:minHeight
= @dimen/ring_min_
height
/>

tezza minima della resistenza android:minHeight

<ImageView
android:id
= @+id/idImgRing2
android:layout_height
= wrap_content
android:layout_width
= wrap_content
android:background
= @drawable/black
android:layout_marginLeft = 50dp
android:minHeight
= @dimen/ring_min_
height
/>

background. Queste ultime sono di tipo 9 patch e

<ImageView
android:id
= @+id/idImgMultiplier
android:layout_height = wrap_content
android:layout_width
= wrap_content
android:background
= @drawable/black
android:layout_marginLeft = 70dp
android:minHeight
= @dimen/ring_min_
height
/>
<ImageView
android:id
= @+id/idImgTollerance
android:layout_height = wrap_content
android:layout_width
= wrap_content
android:background
= @drawable/black
android:layout_marginLeft = 100dp
android:minHeight
= @dimen/ring_min_
height
/>
</FrameLayout>

Una volta definito il file resistance4.xml lo pos-

<TextView
android:id
= @+id/idResult
android:layout_width
= match_parent
android:layout_height
= wrap_content
android:layout_marginLeft = 20dp
/>
</LinearLayout>

= @dimen/resistence_min_height e degli anelli


android:minHeight = @dimen/ring_min_height.
Vengono poi inserite le immagini degli anelli come
sono state create con il tool draw9patch.bat, questo tipo di immagini si allungano adattandosi alle
dimensioni della view.
E possibile trovare le immagini nel progetto allegato.
siamo includere con la linea di codice:
<include layout=@layout/resistance4 android:layout_
gravity=center/>

Dopo aver definito le stringhe necessarie nel file


string.xml e le dimensioni nel file dimens.xml
<string name=labelFourRings>Calcolo per 4 Anelli</
string>
<string name=labelFiveRings>Calcolo per 5 Anelli</string>
<string name=labelRing1>1 Anello</string>
<string name=labelRing2>2 Anello</string>
<string name=labelRing3>3 Anello</string>
<string name=labelMultiplier>Moltiplicatore</string>
<string name=labelTollerance>Tolleranza</string>
<string name=labelResult>Risultato</string>
<dimen name=resistence_min_height>50dp</dimen>
<dimen name=ring_min_height>35dp</dimen>

e i valori degli string-array che andranno a popolare i nostri Spinner il layout pronto.

Spendiamo due parole per capire come strutturato il layout. Si vede che vi un LinearLayout verticale al cui interno vi un FrameLayout nel quale
viene posizionata limmagine della resistenza e
degli anelli, e sotto il FrameLayout una TextView
in cui verr visualizzato il valore della resistenza.
Si vede che allinterno del FrameLayout viene posizionata (come primo componente) limmagine
della resistenza e di seguito gli anelli in successione e con un margine progressivo. Viene settata lal-

<string-array name=color_ring_array>
<item>Nero</item>
<item>Marrone</item>
<item>Rosso</item>
<item>Arancio</item>
<item>Giallo</item>
<item>Verde</item>
<item>Blu</item>
<item>Viola</item>
<item>Grigio</item>
<item>Bianco</item>
</string-array>
<string-array name=multiplier_ring_array>
<item>Nero</item>
<item>Marrone</item>

{57

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


<item>Rosso</item>
<item>Arancio</item>
<item>Giallo</item>
<item>Verde</item>
<item>Blu</item>
<item>Viola</item>
<item>Oro</item>
<item>Argento</item>
</string-array>
<string-array name=tollerance_array>
<item>Oro</item>
<item>Argento</item>
<item>Marrone</item>
<item>Rosso</item>
<item>Verde</item>
<item>Blu</item>
<item>Viola</item>
<item>Grigio</item>
<item>Nulla</item>
</string-array>

Notare che i colori sono stati messi rispettando


lordine della tabella, cos per il primo anello il nero
ha valore 0 ed il bianco il valore 9, questo ordine
verr sfruttato per il calcolo che andremo a vedere
pi avanti.
Andiamo ora ad esplorare la classe CalcFourRingsActivity.java dove abbiamo la logica
per il calcolo ed il cambio degli anelli colorati sulla
resistenza.
La classe dovr estendere Activity ed implementare linterfaccia OnItemSelectedListener in modo
da intercettare levento di selezione degli Spinner
e capire che valore viene selezionato.
public class CalcFourRingsActivity extends Activity implements OnItemSelectedListener{

Allinterno del metodo onCreate andremo a recuperare gli Spinner e ad associargli i valori grazie
ad un ArrayAdapter come abbiamo visto nellarticolo che parlava dei componenti grafici.

Spinner spinnerRing1 = (Spinner) findViewById(R.


id.idRing1);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.
createFromResource(this,
R.array.color_ring_array, android.R.layout.simple_
spinner_item);
adapter.setDropDownViewResource(android.R.layout.
simple_spinner_dropdown_item);
spinnerRing1.setOnItemSelectedListener(this);
spinnerRing1.setAdapter(adapter);
//secondo anello
Spinner spinnerRing2 = (Spinner) findViewById(R.
id.idRing2);
spinnerRing2.setOnItemSelectedListener(this);
spinnerRing2.setAdapter(adapter);
//Moltiplicatore
Spinner spinnerMultiplier = (Spinner) findViewById(R.
id.idMultiplier);
adapter = ArrayAdapter.createFromResource(this,
R.array.multiplier_ring_array, android.R.layout.
simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.
simple_spinner_dropdown_item);
spinnerMultiplier.setOnItemSelectedListener(this);
spinnerMultiplier.setAdapter(adapter);
//Tolleranza
Spinner spinnerTollerance = (Spinner) findViewById(R.
id.idTolleance);
adapter = ArrayAdapter.createFromResource(this,
R.array.tollerance_array, android.R.layout.simple_
spinner_item);
adapter.setDropDownViewResource(android.R.layout.
simple_spinner_dropdown_item);
spinnerTollerance.setOnItemSelectedListener(this);
spinnerTollerance.setAdapter(adapter);

Recuperiamo anche la TextView dove andremo


a scrivere il risultato del calcolo e le ImageView
relative agli anelli della resistenza
txtResult = (TextView)findViewById(R.id.idResult);
imgRing1
= (ImageView)findViewById(R.id.idImgRing1);
imgRing2
= (ImageView)findViewById(R.id.idImgRing2);
imgMultiplier = (ImageView)findViewById(R.
id.idImgMultiplier);
imgTollerance = (ImageView)findViewById(R.
id.idImgTollerance);

La logica del calcolo la inseriamo allinterno del

{58

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


metodo onItemSelected cos che ogni volta che
viene modificato un valore degli Spinner si entra
in questo metodo e viene aggiornato il valore e il

colore degli anelli della resistenza


public void onItemSelected(AdapterView<?> parent, View
view, int pos, long id) {
//E stato selezionato un elemento
if (parent.getId() == R.id.idRing1){
Log.d(TAG,Primo anello);
firstRing = id;
setRingImage(id, Constants.RING_1);
}else if (parent.getId() == R.id.idRing2){
Log.d(TAG,Secondo anello);
secondRing = id;
setRingImage(id, Constants.RING_2);
}else if (parent.getId() == R.id.idMultiplier){
Log.d(TAG,Moltiplicatore);
setMultiplier(id);
}else if (parent.getId() == R.id.idTolleance){
Log.d(TAG, Tolleranza);
setTollerance(id);
}
double valueMultiplier
= 0;
long valueWithoutMultiplier = firstRing*10 + secondRing;

value = + valueMultiplier/1000;
suffix = Kohms;

String result = value + + suffix + + tollerance;


Log.d(TAG,value [ + result + ] );
txtResult.setText(result);

La prima cosa da fare capire quale Spinner


stato modificato utilizzando il parent.getId().
Guardando la tabella i primi 2 anelli hanno valori
crescenti da 0 a 9 che rappresentano le posizioni dei colori allinterno degli Spinner, quindi utilizziamo due variabili firstRing e secondRing
che conterranno il valore posizionale dello Spinner cio lid. In questo modo per sapere le prime
due cifre effettuiamo il calcolo
long valueWithoutMultiplier = firstRing*10 + secondRing;

dopo di che effettuiamo il calcolo usando il mol-

Log.d(TAG,multiplier [ + multiplier + ]);


Log.d(TAG,valueWithoutMultiplier [ + valueWithoutMultiplier + ]);
valueMultiplier
= valueWithoutMultiplier * multiplier;

tiplcatore

double roundValue = Util.round(valueMultiplier, 2, BigDecimal.ROUND_HALF_UP);;

Il risulatato va arrotondato, perch quando si la-

String sMultilier = + roundValue;


Log.d(TAG,value [ + sMultilier + ]);
String value = sMultilier;
String suffix = ohms;
if (multiplier != 0.1 && multiplier != 0.01){
if (valueMultiplier >=10000000){
value = + valueMultiplier/1000000;
suffix = Mohms;
}else if (valueMultiplier >=1000000){
value = + valueMultiplier/1000000;
suffix = Mohms;
}else if (valueMultiplier >=100000){
value = + valueMultiplier/1000;
suffix = Kohms;
}else if (valueMultiplier >=10000){
value = + valueMultiplier/1000;
suffix = Kohms;
}else if (valueMultiplier >=1000){

valueMultiplier

= valueWithoutMultiplier * multiplier;

vora con i double, alcune volte vengono ritornati


valori con molti zeri decimali.
double roundValue = Util.round(valueMultiplier, 2, BigDecimal.ROUND_HALF_UP);

dove il metodo round definito come:


public static double round(double unrounded, int precision,
int roundingMode){
BigDecimal bd = new BigDecimal(unrounded);
BigDecimal rounded = bd.setScale(precision, roundingMode);
return rounded.doubleValue();
}

A questo punto non ci resta che calcolare la tol-

{59

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


leranza. Definiamo due variabili:

private void setRingImage(long color, int ring){

String value = sMultilier;


String suffix = ohms;

if (color == 0){
//nero
setDrawable(ring,R.drawable.black);
}else if (color == 1){
//marrone
setDrawable(ring,R.drawable.brown);
}else if (color == 2){
//rosso
setDrawable(ring,R.drawable.red);
}else if (color == 3){
//arancio
setDrawable(ring,R.drawable.orange);
}else if (color == 4){
//giallo
setDrawable(ring,R.drawable.yellow);
}else if (color == 5){
//verde
setDrawable(ring,R.drawable.green);
}else if (color == 6){
//blu
setDrawable(ring,R.drawable.blue);
}else if (color == 7){
//viola
setDrawable(ring,R.drawable.violet);
}else if (color == 8){
//grigio
setDrawable(ring,R.drawable.gray);
}else if (color == 9){
//bianco
setDrawable(ring,R.drawable.white);
}

la prima rappresenta il valore della resistenza e


la seconda la grandezza (ohm). Vogliamo fare
in modo di visualizzare valori della resistenza
del tipo 10 kOhm e non 10000 Ohm, per far
questo utilizziamo il seguente codice:
if (multiplier != 0.1 && multiplier != 0.01){
if (valueMultiplier >=10000000){
value = + valueMultiplier/1000000;
suffix = Mohms;
}else if (valueMultiplier >=1000000){
value = + valueMultiplier/1000000;
suffix = Mohms;
}else if (valueMultiplier >=100000){
value = + valueMultiplier/1000;
suffix = Kohms;
}else if (valueMultiplier >=10000){
value = + valueMultiplier/1000;
suffix = Kohms;
}else if (valueMultiplier >=1000){
value = + valueMultiplier/1000;
suffix = Kohms;
}
}
String result = value + + suffix + + tollerance;
txtResult.setText(result);

concateniamo il risultato ed assegnamolo alla


TextView.
Non ci resta che vedere come cambiare il colore
degli anelli della nostra resistenza nel momento in cui agiamo sugli Spinner. Definiamo i seguenti metodi:

}
/**
* carica limmagine in base allanello
*
* @param ring
* @param drawable
*/
private void setDrawable(long ring, int drawable){
if (ring == Constants.RING_1){
imgRing1.setBackgroundResource(drawable);
}else if (ring == Constants.RING_2){
imgRing2.setBackgroundResource(drawable);
}
}

setRingImage viene richiamato nellonItemSelected per il primo e secondo anello e come si


vede non fa altro che assegnare il background
allimmagine corrispondente.
Per gli anelli del moltiplicatore e della tolleranza
viene fatta una cosa analoga con la differenza,

{60

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


vengono utilizzate due variabili: multiplier e
tollerance per memorizzare il rispettivo valore
e utilizzarlo nel calcolo.
private void setMultiplier(long id){
/* lid la posizione dellelemento nello spinner,
quindi associamo la posizione al colore */
if (id == 0){
//nero
multiplier = 1;
imgMultiplier.setBackgroundResource(R.drawable.
black);
}else if (id == 1){
//marrone
multiplier = 10;
imgMultiplier.setBackgroundResource(R.drawable.
brown);
}else if (id == 2){
//rosso
multiplier = 100;
imgMultiplier.setBackgroundResource(R.drawable.
red);
}else if (id == 3){
//arancio
multiplier = 1000;
imgMultiplier.setBackgroundResource(R.drawable.
orange);
}else if (id == 4){
//giallo
multiplier = 10000;
imgMultiplier.setBackgroundResource(R.drawable.
yellow);
}else if (id == 5){
//verde
multiplier = 100000;
imgMultiplier.setBackgroundResource(R.drawable.
green);
}else if (id == 6){
//blu
multiplier = 1000000;
imgMultiplier.setBackgroundResource(R.drawable.
blue);
}else if (id == 7){
//viola
multiplier = 10000000;
imgMultiplier.setBackgroundResource(R.drawable.
violet);
}else if (id == 8){
//oro
multiplier = 0.1;
imgMultiplier.setBackgroundResource(R.drawable.
gold);
}else if (id == 9){
//argento
multiplier = 0.01;
imgMultiplier.setBackgroundResource(R.drawable.
silver);
}
}

public void setTollerance(long id){


if (id == 0){
//oro
tollerance = 5%;
imgTollerance.setBackgroundResource(R.drawable.
gold);
}else if (id == 1){
//argento
tollerance = 10%;
imgTollerance.setBackgroundResource(R.drawable.
silver);
}else if (id == 2){
//marrone
tollerance = 1%;
imgTollerance.setBackgroundResource(R.drawable.
brown);
}else if (id == 3){
//rosso
tollerance = 2%;
imgTollerance.setBackgroundResource(R.drawable.
red);
}else if (id == 4){
//verde
tollerance = 0.5%;
imgTollerance.setBackgroundResource(R.drawable.
green);
}else if (id == 5){
//blue
tollerance = 0.25%;
imgTollerance.setBackgroundResource(R.drawable.
blue);
}else if (id == 6){
//viola
tollerance = 0.1%;
imgTollerance.setBackgroundResource(R.drawable.
violet);
}else if (id == 7){
//grigio
tollerance = 0.05%;
imgTollerance.setBackgroundResource(R.drawable.
gray);
}else if (id == 8){
//nulla
tollerance = 20%;
imgTollerance.setBackgroundResource(R.drawable.
transparent);
}
}

Come si vede dal codice per cambiare il colore degli anelli stato utilizzato il metodo setBackgroundResource che prende come parametro lid dellimmagine.

{61

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

5 Anelli
Il calcolo del valore della resistenza con 5 anelli
analogo a quella con 4 la differenza che vi
una cifra in pi, quindi dovremo considerarla
nel calcolo.

android:layout_height
= wrap_content
android:layout_width
= wrap_content
android:background
= @drawable/black
android:layout_marginLeft = 50dp
android:minHeight
= @dimen/ring_min_
height
android:layout_gravity
= top
/>
<ImageView
android:id
= @+id/idImgRing3
android:layout_height
= wrap_content
android:layout_width
= wrap_content
android:background
= @drawable/black
android:layout_marginLeft = 70dp
android:minHeight
= @dimen/ring_min_
height
android:layout_gravity
= top
/>

Creiamo il file resistance5.xml analogo a resistance4.xml:

<?xml version=1.0 encoding=utf-8?>


<LinearLayout
xmlns:android
= http://schemas.android.com/apk/
res/android
android:layout_width = wrap_content
android:layout_height = wrap_content
android:layout_gravity = center
android:orientation
= vertical
android:layout_marginTop= 20dp
>
<FrameLayout
android:layout_width = match_parent
android:layout_height = wrap_content
android:minHeight
= @dimen/resistence_min_
height
>
<ImageView
android:layout_height = wrap_content
android:layout_width = wrap_content
android:src
= @drawable/resistenza_5
android:layout_marginTop= 1dp
/>
<ImageView
android:id
= @+id/idImgRing1
android:layout_height
= wrap_content
android:layout_width
= wrap_content
android:background
= @drawable/black
android:layout_marginLeft = 30dp
android:minHeight
= @dimen/ring_min_
height
android:layout_gravity
= top
/>
<ImageView
android:id
= @+id/idImgRing2

<ImageView
android:id
= @+id/idImgMultiplier
android:layout_height
= wrap_content
android:layout_width
= wrap_content
android:background
= @drawable/black
android:layout_marginLeft = 90dp
android:minHeight
= @dimen/ring_min_
height
android:layout_gravity
= top
/>
<ImageView
android:id
= @+id/idImgTollerance
android:layout_height
= wrap_content
android:layout_width
= wrap_content
android:background
= @drawable/black
android:layout_marginLeft = 120dp
android:minHeight
= @dimen/ring_min_
height
android:layout_gravity
= top
/>
</FrameLayout>
<TextView
android:id
= @+id/idResult
android:layout_width
= match_parent
android:layout_height
= wrap_content
android:layout_marginLeft = 20dp
/>
</LinearLayout>

Il file calc_five_ring_activity.xml si differenzia


dal precedente per laggiunta di una riga nel TableLayout

{62

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


<TableRow >
<TextView
android:layout_width = match_parent
android:layout_height = wrap_content
android:text
= @string/labelRing3
/>
<Spinner
android:id
= @+id/idRing3
android:layout_width = match_parent
android:layout_height = wrap_content
/>
</TableRow>

e per linclude che ora punta al nuovo layout


<include layout=@layout/resistance5 android:layout_
gravity=center/>

quindi andiamo a creare la classe CalcFiveRingsActivity come quella precedente con in

<?xml version=1.0 encoding=utf-8?>


<LinearLayout
xmlns:android
= http://schemas.android.com/apk/
res/android
android:layout_width = match_parent
android:layout_height = match_parent
android:orientation
= vertical >
<Button
android:id
= @+id/idBtnFourBands
android:layout_width = match_parent
android:layout_height = wrap_content
android:text
= @string/labelFourRings
/>
<Button
android:id
= @+id/idBtnFiveRings
android:layout_width = match_parent
android:layout_height = wrap_content
android:text
= @string/labelFiveRings
/>
</LinearLayout>

la classe risulta essere:

pi il riferimento al terzo anello, nellonCreate


aggiungiamo:
//terzo anello
Spinner spinnerRing3 = (Spinner) findViewById(R.
id.idRing3);
spinnerRing3.setOnItemSelectedListener(this);
spinnerRing3.setAdapter(adapter);
imgRing3
= (ImageView)findViewById(R.id.idImgRing3);

nel metodo onItemSelected aggiungiamo


else if (parent.getId() == R.id.idRing3){
Log.d(TAG, Terzo anello);
thirdRing = id;
setRingImage(id, Constants.RING_3);
}

Quindi nel calcolo entrer in gioco anche la nuova variabile


long valueWithoutMultiplier = firstRing*100 + secondRing*10 + thirdRing;

Non ci resta che creare la HomeActivity che


conterr i due bottoni per la scelta del calcolo
con 4 o 5 anelli
Il layout semplice:

public class HomeActivity extends Activity{


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home_activity);
Button btnFour = (Button)findViewById(R.
id.idBtnFourBands);
btnFour.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(HomeActivity.
this,CalcFourRingsActivity.class);
startActivity(intent);
}
});
Button btnFive = (Button)findViewById(R.
id.idBtnFiveRings);
btnFive.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(HomeActivity.
this,CalcFiveRingsActivity.class);
startActivity(intent);
}
});
}

{63

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Abbiamo visto come strutturare il nostro codice
in modo da effettuare il calcolo del valore delle resistenze per 4 e 5 anelli. Come accennato
allinizio dellarticolo non stata implementata
la parte per il calcolo con 6 anelli, questo un
esercizio interessante lasciato a chi vuole verificare se entrato nellottica del progetto ed
in grado di modificarlo aggiungendo questo
calcolo. Quindi chi ha voglia pu implementare
la propria versione personalizzata dellapplicazione con 6 anelli.
Unanticipazione di quello che troverete nel
prossimo articolo. Avete mai sentito parlare di
NFC? Vedremo unapplicazione Android che
sfrutta questa tecnologia.
Qui possibile scaricare il codice sorgente del
progetto.

Allegato

Dimensione

ResistanceProject.zip

388.94 KB

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:
http://it.emcelettronica.com/corso-su-android-creiamo-nostra-applicazione-automatizzare-calcolo-del-valore-delle-resistenze

{64

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

6. Creiamo unapplicazione per scrivere e leggere tag NFC

FC (Near Field Communication) una


tecnologia che permette il trasferimento di
informazioni a corto raggio ad una distanza

massima di 10cm cos da rendere le comunicazioni molto pi sicure perch difficilmente intercettabili, a differenza di una comunicazione
wireless o Bluetooth. Per questo motivo molte
aziende stanno realizzando applicazioni in grado di sfruttare questa tecnologia per effettuare
pagamenti con carta di credito presente sulla
SIM del telefono o, se il telefono non NFCcompliant, utilizzando apposite schede SD con
tecnologia NFC. Negli ultimi anni, il numero di
telefoni che montano un chip NFC aumentato
in modo considerevole e quindi anche le applicazioni che sfruttano questa tecnologia sono in
aumento. Ad oggi possibile trovare allinterno di musei, vicino alle opere, dei tag NFC e
appoggiando il proprio smartphone possibile
acquisire ulteriori informazioni.
Nellarticolo vedremo come realizzare una nostra applicazione per poter leggere il contenuto
di un tag NFC e scrivere sui nostri tag in modo
da poter effettuare le seguenti azioni:
impostare il telefono in silenzioso;
spegnere la wifi;
attivare il bluetooth;
attivare lallarme.
Cosa ci serve
un telefono NFC, io ho usato un Samsung
S2 NFC;
uno o pi tag NFC, io ho usato i tag della
sony in figura:

Image credits:nfctimes.com

Come prima cosa pensiamo allinterfaccia grafica ossia come si deve presentare la nostra applicazione:
home page: da cui si effettuano le scelte;
sar composta da 3 pulsanti per poter scrivere e leggere i dati sul tag, in particolare:
Scrivi tag: permette di personalizzare i
tag NFC;
Leggi Tag: legge un generico tag contenente messaggi in formato NDEF;
Leggi Tag/Imposta il telefono: permette di leggere il tag e di impostare il telefono nella modalit scelta.

{65

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Scrivi tag: pagina di scelta della modalit
da scrivere sul tag;

Una volta definita linterfaccia utente andiamo a


vedere come implementare il tutto.
Il supporto alla tecnologia NFC stato introdotto con le API level 9 (Android 2.3.1) quindi il
progetto deve avere una versione maggiore o
uguale a 2.3.1

Scrivere il tag
Partiamo dalla classe che ci permetter di personalizzare il nostro tag NFC (WriteTagActivity.java), che sar costituita da una TextView
per una breve descrizione, da tre RadioButton e
da un bottone per effettuare la scrittura.
Il file write_tag_activity.xml risulta essere molto semplice:
Leggi il tag e Leggi/Imposta il telefono:
hanno lo stesso layout ma le azioni che svolgono sono diverse, la prima mostrer a video le informazioni lette dal tag, mentre la
seconda effettuer delle azioni in base allinformazione contenuta nel tag.

{66

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


<?xml version=1.0 encoding=utf-8?>
<LinearLayout
xmlns:android

= http://schemas.android.com/apk/res/android

android:layout_width = fill_parent
android:layout_height = fill_parent
android:orientation

= vertical >

<LinearLayout
android:layout_width

= match_parent

android:layout_height

= wrap_content

android:layout_marginTop = 20sp
android:orientation

= vertical>

<TextView
android:layout_width

= match_parent

android:layout_height

= wrap_content

android:text

= @string/labelDescription

android:layout_marginTop = 10sp
android:layout_marginBottom = 10sp
/>
<RadioGroup
android:layout_width

= fill_parent

android:layout_height

= wrap_content

android:orientation

= vertical>

<RadioButton
android:id

= @+id/idRadioSilent

android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
android:checked

= @string/labelRadioSilent
= true/>

<RadioButton
android:id

= @+id/idRadioTurnOffWifi

android:layout_width = wrap_content
android:layout_height = wrap_content
android:text

= @string/labelRadioTurnOffWifi/>

<RadioButton
android:id

= @+id/idRadioTurnOnBT

android:layout_width = wrap_content
android:layout_height = wrap_content
android:text

= @string/labelRadioTurnOnBT/>

{67

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca


<RadioButton
android:id

= @+id/idRadioSetAlarm

android:layout_width = wrap_content
android:layout_height = wrap_content
android:text

= @string/labelRadioSetAlarm/>

</RadioGroup>
<Button
android:id

= @+id/btnWriteTag

android:layout_width

= wrap_content

android:layout_height = wrap_content
android:layout_weight = 1
android:text

= @string/btnWrite

android:layout_gravity = center
android:layout_marginTop = 10sp />
</LinearLayout>
</LinearLayout>

Concentriamoci sulla classe WriteTagActivity.

ty in foreground per intercettare il dispatch del

java e pensiamo a come scrivere i dati sul tag.

tag NFC. In pi definiamo lintent filter, grazie al

Lidea quella di associare un valore ad una-

quale registriamo lActivity allevento di tag di-

zione e scrivere i valori su tag diversi, in modo

scovered. Inoltre definiamo lintent con flag: In-

che quando si legge il valore si effettua lazione

tent.FLAG_ACTIVITY_SINGLE_TOP, cos che

corrispondente:

quando viene effettuato il detect del tag, venga

Dato sul tag

Azione

Silent

Imposta il telefono in
modo silenzioso

wifiOff

Spegne la wifi

BTOn

Accende il Bluetooth

Alarm-ora:minuti

Imposta lallarme

Nel metodo onCreate dobbiamo recuperare


lNFCAdapter che useremo nei metodi onResume e onPause per dare la priorit allActivi

lanciato un nuovo intent e richiamato in atomatico il metodo onNewIntent.

{68

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


adapter = NFCAdapter.getDefaultAdapter(this);
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(NFCAdapter.ACTION_TAG_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
writeTagFilters = new IntentFilter[] { tagDetected };

Definiamo ora il metodo:


@Override
protected void onNewIntent(Intent intent){
if(NFCAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
tag = intent.getParcelableExtra(NFCAdapter.EXTRA_TAG);
}
}

Il quale controller laction e, se corrisponde ad


un discover del tag, allora recuperer loggetto
Tag che andremo ad utilizzare quando scriveremo sul tag fisico.
Ritorniamo ora al metodo onCreate, dove dopo
aver instanziato i RadioButton per la scelta di
cosa scrivere sul tag, settiamo il listener del Button e sovrascriviamo il metodo onClick:
btnWrite.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
try {
if (radioSetAlarm.isChecked()){
openTimePickerDialog(false);
}else {
if(tag==null){
Toast.makeText(context, context.getString(R.string.tagNotDetected),
Toast.LENGTH_LONG ).show();
}else{
if (radioSilent.isChecked()){
writeTag(getString(R.string.stateSilent),tag);
Toast.makeText(context, context.getString(R.string.tagWrite),
Toast.LENGTH_LONG ).show();
}else if (radioTurnOffWifi.isChecked()){

{69

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


writeTag(getString(R.string.stateTurnOffWifi),tag);
Toast.makeText(context, context.getString(R.string.tagWrite),
Toast.LENGTH_LONG ).show();
}else if (radioTurnOnBT.isChecked()){
writeTag(getString(R.string.stateTurnOnBT),tag);
Toast.makeText(context, context.getString(R.string.tagWrite),
Toast.LENGTH_LONG ).show();
}
}
}
} catch (IOException e) {
Toast.makeText(context, context.getString(R.string.errorWritingTag),
Toast.LENGTH_LONG ).show();
e.printStackTrace();
} catch (FormatException e) {
Toast.makeText(context, context.getString(R.string.errorWritingTag) ,
Toast.LENGTH_LONG ).show();
e.printStackTrace();
}
}
});

nel quale si controlla quale RadioButton selezionato e si effettua lazione necessaria. Si nota
che per radioSilent, radioTurnOffWifi e radioTurnOnBT viene richiamato il metodo:
private void writeTag(String text, Tag tag) throws IOException, FormatException{
NdefRecord[] records = { createTextRecord(text,Locale.getDefault(),true) };
NdefMessage message = new NdefMessage(records);
// Get an instance of Ndef for the tag.
Ndef ndef = Ndef.get(tag);
// Enable I/O
ndef.connect();
// Write the message
ndef.writeNdefMessage(message);
// Close the connection
ndef.close();
}

} catch (FormatException e) {
Toast.makeText(context, context.getString(R.string.errorWritingTag) ,

{70

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Toast.LENGTH_LONG ).show();
e.printStackTrace();
}
}
});

Mentre per settare lallarme (radioSetAlarm)


viene aperto un TimePickerDialog per sceglire
lorario per poi richiamare il metodo writeTag.
private void openTimePickerDialog(boolean is24r) {
Calendar calendar = Calendar.getInstance();
timePickerDialog = new TimePickerDialog(WriteTagActivity.this,
onTimeSetListener, calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE), is24r);
timePickerDialog.setTitle(Set Alarm Time);
timePickerDialog.show();
}
OnTimeSetListener onTimeSetListener = new OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

timeToSet = getString(R.string.stateAlarm) + - + hourOfDay + : + minute;


try {
if(tag==null){
Toast.makeText(context, context.getString(R.string.tagNotDetected),
Toast.LENGTH_LONG ).show();
}else{
writeTag(timeToSet,tag);
Toast.makeText(context, context.getString(R.string.tagWrite),
Toast.LENGTH_LONG ).show();
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(context, context.getString(R.string.errorWritingTag),
Toast.LENGTH_LONG ).show();
}
}
};

{71

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Come si vede il metodo writeTag richiama il metodo seguente come riportato sul sito ufficiale di
Android
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8){
byte[] langBytes = locale.getLanguage().getBytes(Charset.forName(US-ASCII));
Charset utfEncoding = encodeInUtf8 ? Charset.forName(UTF-8) :
Charset.forName(UTF-16);
byte[] textBytes = payload.getBytes(utfEncoding);
int utfBit = encodeInUtf8 ? 0 : (1 << 7);
char status = (char) (utfBit + langBytes.length);
byte[] data = new byte[1 + langBytes.length + textBytes.length];
data[0] = (byte) status;
System.arraycopy(langBytes, 0, data, 1, langBytes.length);
System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
NdefRecord.RTD_TEXT, new byte[0], data);
return record;
}

il quale crea il messaggio secondo le specifiche


NDEF e restituisce un oggetto NdefRecord. In
seguito viene creato un oggetto NdefMessage
e viene scritto il messaggio sul tag.
Come specificato sopra, andiamo a vedere
come si presentano i metodi onPause e onResume:
@Override
public void onPause(){
super.onPause();
writeMode = false;
adapter.disableForegroundDispatch(this);
}
@Override
public void onResume(){
super.onResume();
writeMode = true;
adapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null);
}

{72

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


NellonResume, grazie al metodo enableForegroundDispatch, viene data la priorit allActivity in foreground di intercettare il dispatch del
tag NFC che viene rimossa nellonPause grazie
al metodo: disableForegroundDispatch.
Per scrivere sul tag, selezioniamo il RadioButton corrispondente, avviciniamo il telefono al
tag e premiamo il bottone Scrivi.

Leggere il tag
Vediamo ora come recuperare le informazioni
dal tag appena scritto. Utilizziamo una classe
ReadActivity.java molto simile a WriteTagActivity.java; la differenza essenziale si trova nel
metodo onNewIntent in cui recuperiamo le informazioni dal tag e le presentiamo a video tramite
un Dialog:
@Override
protected void onNewIntent(Intent intent){
if(NFCAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
String action = intent.getAction();
Tag tag = intent.getParcelableExtra(NFCAdapter.EXTRA_TAG);
String s = action + \n\n + tag.toString();
// parse through all NDEF messages and their records and pick text type only
Parcelable[] data = intent.getParcelableArrayExtra(NFCAdapter.EXTRA_NDEF_MESSAGES);
if (data != null) {
try {
for (int i = 0; i < data.length; i++) {
NdefRecord [] recs = ((NdefMessage)data[i]).getRecords();
for (int j = 0; j < recs.length; j++) {
if (recs[j].getTnf() == NdefRecord.TNF_WELL_KNOWN &&
Arrays.equals(recs[j].getType(), NdefRecord.RTD_TEXT)) {
byte[] payload = recs[j].getPayload();
String textEncoding = ((payload[0] & 0200) == 0) ? UTF-8 : UTF-16;
int langCodeLen = payload[0] & 0077;

{73

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

s += (\n\nNdefMessage[ + i + ], NdefRecord[ + j + ]:\n\ +


new String(payload, langCodeLen + 1, payload.length - langCodeLen - 1,
textEncoding) + \);
}
}
}
} catch (Exception e) {
Log.e(TagDispatch, e.toString());
}
DialogFragment dialog = new DialogMessage(s);
dialog.show(getFragmentManager(), message);
}
}
}
dove la Classe DialogMessage non fa altro che visualizzare il dialog:
public class DialogMessage extends DialogFragment {
private String message = null;
public DialogMessage(String message){
this.message = message;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(message)
.setPositiveButton(R.string.btnOk, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int id) {
}
});
return builder.create();
}
}

{74

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

Leggere il tag ed impostare il telefono

if(!setAlarm && NFCAdapter.ACTION_NDEF_DI-

Passiamo alla lettura del tag per poter imposta-

SCOVERED.equals(getIntent().getAction())){

re il telefono nelle modalit descritte in precedenza.

String s

Creiamo la classe ReadAndSetPnoneActivity.

String message = ;

java, questa risulta essere leggermente diversa, a parte la logica che ci permetter di disabilitare il wifi, abilitare il bluetooth, etc, quello che
cambia come viene registrata lActivity con
lintent filter per il discover del tag NFC. Questa
volta definiamo lintent-filter nel manifest perch vogliamo che quando viene riconosciuto il
tag, venga aperta in automatico la nostra Activity per eseguire loperazione memorizzata sul
tag. Ricordo che se viene registrato lintent filter
nellActivity come la classe vista in precedenza,
allora vince la classe aperta per intercettare
lintent.
<activity
android:label=@string/app_name
android:name=.read.ReadAndSetPnoneActivity
android:screenOrientation=portrait
>
<intent-filter>
<action android:name=android.NFC.action.
NDEF_DISCOVERED/>
<category android:name=android.intent.category.
DEFAULT/>
<data android:mimeType=text/plain />
</intent-filter>
</activity>

Quando viene fatto il detect del tag, se non


aperta una delle classi implementate precedentemente, viene aperta questa Activity (ReadAndSetPhoneActivity.java) che attivandosi entra

= ;

// parse through all NDEF messages and their records


and pick text type only
Parcelable[] data = getIntent().
getParcelableArrayExtra(NFCAdapter.EXTRA_
NDEF_MESSAGES);
if (data != null) {
try {
for (int i = 0; i < data.length; i++) {
NdefRecord rec = ((NdefMessage)data[i]).getRecords()[0];
byte[] payload = rec.getPayload();
Log.d(payload,rec [+ payload.length + ]);
String textEncoding = ((payload[0] & 0200) == 0)
? UTF-8 : UTF-16;
int langCodeLength = payload[0] & 0077;
Log.d(TagDispatch,langCodeLen [+ langCodeLength + ]);
s = new String(payload,langCodeLength + 1,
payload.length - langCodeLength - 1,textEncoding);
if (s.equals(getString(R.string.stateSilent))){
setSilentMode();
message = getString(R.string.silentOk);
}else if (s.equals(getString(R.string.stateTurnOffWifi))){
disableWifi();
message = getString(R.string.wifiOffOk);
}else if (s.equals(getString(R.string.stateTurnOnBT))){

nel metodo onResume in cui implementiamo la

enableBT();

logica per riconoscere cosa c scritto nel tag ed

message = getString(R.string.bluetoothOk);

effettuare le operazioni desiderate.

}else if (s.startsWith(getString(R.string.stateA

{75

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


larm))){

MODE_VIBRATE);
}

enableAlarm(s);
setAlarm = true;
message = getString(R.string.enableAlarmOk);
}

Per disabilitare la Wifi viene usata la classe WifiManager:

}
DialogFragment dialog = new
DialogMessage(message);
dialog.show(getFragmentManager(), message);

private void disableWifi()throws Exception{


WifiManager wifiManager = (WifiManager) this.
getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(true);

} catch (Exception e) {
DialogFragment dialog = new
DialogMessage(getString(R.string.errorReadingTag));
dialog.show(getFragmentManager(), message);
e.printStackTrace();
}

wifiManager.setWifiEnabled(false);
}

Per abilitare il Bluetooth viene utilizzata la classe BluetoothAdapter


private void enableBT() throws Exception{

//Disable bluetooth

BluetoothAdapter mBluetoothAdapter = Bluetoo-

Si vede che in base al payload letto vengono

thAdapter.getDefaultAdapter();
if (!mBluetoothAdapter.isEnabled()) {

eseguiti i metodi che si occupano di settare il

mBluetoothAdapter.enable();

telefono negli stati scelti.


Per settare il telefono in silenzioso viene usata
la classe AudioManager:
private void setSilentMode()throws Exception{
AudioManager am;
am= (AudioManager) getBaseContext().
getSystemService(Context.AUDIO_SERVICE);
//For Normal mode

}
}

Mentre per settare lallarme viene prima parsato il valore letto dal tag che ricordiamo contiene
lora e di seguito invocato lintent:
Intent openNewAlarm = new Intent(AlarmClock.
ACTION_SET_ALARM);

am.setRingerMode(AudioManager.RINGER_
MODE_NORMAL);

a cui vengono passati i parametri per settare


lora

//For Silent mode


am.setRingerMode(AudioManager.RINGER_
MODE_SILENT);
//For Vibrate mode
am.setRingerMode(AudioManager.RINGER_

{76

{77

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


private void enableAlarm(String s) throws Exception{
String hour = s.substring(s.indexOf(-) + 1,s.indexOf(:));
String min

= s.substring(s.indexOf(:) + 1,s.length());

Log.d(ReadAndSetPnoneActivity, hour [ + Integer.parseInt(hour) + ]);


Log.d(ReadAndSetPnoneActivity, min [ + Integer.parseInt(min) + ]);
Intent openNewAlarm = new Intent(AlarmClock.ACTION_SET_ALARM);
openNewAlarm.putExtra(AlarmClock.EXTRA_HOUR, Integer.parseInt(hour));
openNewAlarm.putExtra(AlarmClock.EXTRA_MINUTES, Integer.parseInt(min));
openNewAlarm.putExtra(AlarmClock.EXTRA_SKIP_UI,true);
startActivity(openNewAlarm);
}

Per concludere dobbiamo aggiungere alcuni


permessi nel manifest:
<uses-permission android:name=android.permission.NFC />
<uses-permission android:name=android.permission.ACCESS_WIFI_STATE></uses-permission>
<uses-permission android:name=android.permission.CHANGE_WIFI_STATE></uses-permission>
<uses-permission android:name=android.permission.BLUETOOTH/>
<uses-permission android:name=android.permission.BLUETOOTH_ADMIN/>
<uses-permission android:name=com.android.alarm.permission.SET_ALARM/>

Questa una base di partenza per lo sviluppo


nel campo NFC. Avendo a disposizione pi tag
e posizionandoli in punti strategici della casa
possibile far eseguire delle azioni al nostro
smartphone solamente appoggiando il telefono
vicino ai vari tag. Per esempio si pu pensare di
programmare un tag in modo che quando viene
avvicinato il telefono, venga fatta una chiamata
ad un numero specifico.

Nel prossimo articolo andremo a trattare un


tema molto delicato: la sicurezza dei dati. Prima
o poi quando si sviluppa unapplicazione Android nascer il problema della persistenza dei
dati. Vedremo quindi come realizzare un database sqlite e come sia possibile rendere sicuri i
nostri dati.
Buon divertimento!

Allegato

Dimensione

ReadWriteNFCTag.zip

661.1 KB

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:
http://it.emcelettronica.com/corso-su-android-creiamo-unapplicazione-scrivere-leggere-tag-nfc

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

7. Impariamo a proteggere i nostri dati

niziamo col dire che Android mette a disposi-

ed una schermata per linserimento ed il recu-

zione vari modi per rendere persistenti i dati

pero dei dati:

applicativi. Il metodo da utilizzare viene scelto

di volta in volta in base alle esigenze. I modi


sono essenzialmente quattro:
Shared Preferences: salva i dati in coppie
Chiave-Valore;

Internal Storage: salva i dati sulla memoria


interna del dispositivo;
External Storage: salva i dati sulla memoria
esterna del dispositivo;
SQLite Databases: salva i dati in un database privato.
Noi ci concentreremo sullultimo caso e quindi
andremo a vedere come criptare un database
SQLite, realizzando uninterfaccia grafica che
prenda in ingresso la password per criptare il
db:
Per fare questo, useremo la libreria sqlchiper
che pu essere scaricata qui. Essa utilizza un
algoritmo di crypt a 256-bit AES ed distribuita
sotto licenza Apache 2.0.
Andiamo a vedere come integrare la libreria nel
nostro progetto:
I file di cui avremo bisogno sono:
libdatabase_sqlcipher.so
libsqlcipher_android.so
libstlport_shared.so
sqlcipher.jar
guava-r09.jar
icudt46l.zip
scaricabili del sito sqlcipher.net cliccando sulla
voce SQLCipher For Android

{78

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Nota: Le libreria Android si dividono in 2 cate-

Andiamo ora a vedere come creare il nostro da-

gorie:

tabase criptato.

1. Librerie java (file .jar)

Creiamo una classe DbCrypt

2. Librerie native (.so)


Le librerie native (scritte in C++) sono dipendenti dallarchitettura del dispositivo. Android presente su molti tipi di dispositivi e questi hanno
tra loro vari tipi di processori: arm, armv7, x86,
quindi quando si includono delle librerie native
al fine di supportare tutti i tipi di processore si
devono avere librerie separate.
Nel nostro caso, la libreria sqlcipher contiene le
librerie per tutti e 3 i tipi di processori. Vediamo
come possibile includerle nel nostro progetto:
creare allinterno della cartella lib 3 cartelle:
1. armeabi;
2. armeabi-v7a;
3. x86.
inserire allinterno delle cartelle i relativi file
libdatabase_sqlcipher.so;
libsqlcipher_android.so;
libstlport_shared.so.
Nota: inserire nella cartella armeabi-v7a gli
stessi di armeabi
Copiare i file sqlcipher.jar e guava-r09.jar
allinterno della cartella lib
Copiare il file icudt46l.zip allinterno della
cartella asset
In figura mostrato il progetto con i file inclusi:

{79

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

Allinterno della classe DbCrypt possiamo notaCliccare su browse nella sezione superclass e

re i metodi:

scrivere nel campo di ricerca: SQLiteOpenHel-

onCreate: metodo invocato per la creazione

per:

del database;
onUpgrade: metodo invocato per controllare la versione del db attualmente salvato con
una nuova versione per effettuare eventuale
upgrade del database.
Alla classe cos creata andremo ad aggiungere
il costruttore:
private DbCrypt(Context context) {
super(context, DB_LOCATION, null, DATABASE_VERSION);
DbCrypt.context = context;
}

Definendo come variabili globali:


private static final int
DATABASE_VERSION = 1;
public static String DB_NAME = /test_db;
public static String DB_LOCATION = DB_NAME; //
verr poi deciso da colui che
invocher il costruttore

Selezionare la classe con il package net.sqlcipher.database, premere ok e poi Finish.

private static SQLiteDatabase db;


private static Context context
private static boolean passwordOk;

{80

{81

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Con super(context, DB_LOCATION, null, DA-

Dove

TABASE_VERSION); stiamo invocando il co-

getWritableDatabase(password) permette di

struttore della classe genitore dove passeremo

fare il crypt del database.

il context della nostra applicazione, la posizione

La password per il crypt del database per una

su file system dove sar salvato il db, la factory

maggiore sicurezza andrebbe creata durante

per la creazione di un oggetto cursor (null per

lesecuzione dellapplicazione e non salvata o

usare quello di default) e la versione del data-

inserita come una stringa direttamente nel co-

base.

dice in quanto con tecniche di decompilazione

Lidea generale di utilizzare questa classe

sono facilmente individuabili. Nel nostro caso

come un singleton cio come un oggetto di in-

infatti la password per il crypt del db verr defi-

terfaccia unico, invocabile quando si deve fare

nita come la password dellutente per accedere

unoperazione sul database.

allapplicazione.

Per fare questo aggiungiamo il metodo:

Aggiungeremo anche il metodo:

public static DbCrypt getInstance(Context


context,String password) {
if(dbCrypt == null && !passwordOk) {
//Carica le librerie native della libreria sqlcipher
SQLiteDatabase.loadLibs(context);
//Salviamo il database nella posizione di memoria interna riservata alla
nostra applicazione dal sistema operativo
String root=context.getFilesDir().getPath();
DB_LOCATION = root + DB_NAME;
//Crea unistanza di se stesso
dbCrypt = new DbCrypt(context);
try {
//impostiamo la password utilizzata per il
crypt del db
db = dbCrypt.getWritableDatabase(password);
database

//invochiamo il metodo per la creazione del

dbCrypt.onCreate(db);
;
passwordOk = true;
} catch (Exception e) {
// TODO: handle exception
passwordOk = false;
}

}
return dbCrypt;

il

metodo

public static DbCrypt getInstance() {


return dbCrypt;
}

per prendere loggetto gi creato con il metodo


precedente.
Andiamo, ora, a definire la struttura che dovr
avere il nostro database.
Creiamo un database di esempio con la seguente tabella Utente:
Nome

Cognome

Indirizzo

Nel metodo onCreate della classe DbCrypt scriviamo:


@Override
public void onCreate(SQLiteDatabase db) {
DbCrypt.db = db;
try {
db.query(user, null, null, null, null, null, null);
} catch (Exception sqlex) {
String queryTabellaUtente= CREATE TABLE IF
NOT EXISTS
utente (UTENTE_ID INTEGER PRIMARY KEY AUTOINCREMENT, +
nome varchar(50) NOT NULL, +
cognome varchar(30) NOT NULL, +
indirizzo varchar(255) NOT NULL);;
db.execSQL(queryTabellaUtente);
}
}

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Nota: Lutilizzo del try/catch viene utilizzato per
stabilire se la tabella gi esiste sul database in
quanto se il db non esiste, linvocazione del metodo query generer uneccezione.

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DbCrypt dbCrypt = DbCrypt.getInstance();
if (dbCrypt!= null){
dbCrypt.reset();
}

Andiamo ora a vedere come richiamare questa


classe dalla nostra activity e come effettuare dei
nuovi inserimenti nella tabella.
Per effettuare la creazione del db ed avere unistanza della classe DbCrypt baster scrivere:
dbCrypt = DbCrypt.getInstance(MainActivity.
this,password.getText().toString());
passando quindi alla getInstance il context e la
password per il cript del db.
Con questo abbiamo creato il db.
Andiamo ora a memorizzare le informazioni
allinterno del db criptato:
nella classe DbCrypt aggiungiamo un metodo
inserimentoUtente che prende in ingresso il
nome, cognome e la data di nascita e ci restituisce lesito dellinserimento.
public long inserimentoUtente(String nome, String
cognome,String indirizzo){
long result;
String table = utente;
ContentValues valoriDaInserire = new ContentVa
lues();
valoriDaInserire.put(nome, nome);
valoriDaInserire.put(cognome, cognome);
valoriDaInserire.put(indirizzo, indirizzo);
result = db.insert(table, null, valoriDaInserire);
}

return result;

Richiamiamo la classe DbCrypt dalla nostra activity e andiamo ad effettuare un inserimento sul

final EditText password = (EditText)


findViewById(R.id.password);
Button okButton = (Button)findViewById(R.
id.button);
okButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (!password.getText().toString().trim().
equals()) {
DbCrypt dbCrypt = DbCrypt.
getInstance(MainActivity.this,
password.getText().toString());
if (dbCrypt.isPasswordOk()) {
Intent intent = new Intent(getApplicationC
ontext(),DbQuery.class);
startActivity(intent);
finish();
} else{
dbCrypt.reset();
//messaggio password errata
AlertDialog dialog = new AlertDialog.
Builder(MainActivity.this)
.setTitle(R.string.dialog_error_title)
.setMessage(R.string.error_message)
.setPositiveButton(R.string.ok_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface
dialog,
int whichButton) {
//activity.finish();
}})
.create();
dialog.show();
}

db:

}
});

Il metodo onCreate dellactivity MainActivity diventer:

{82

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Alla pressione del pulsante ok sullapplicazione
verr instanziato loggetto DbCypt e settata la
variabile passwordOk, se la password inserita corretta o se la prima volta che si lancia
lapplicazione e di conseguenza creato il db se
non esistente. In caso di password errata sar
visualizzato un messaggio di errore.
Se il db verr creato correttamente o la password esatta verr lanciata una nuova Activity
dove sar possibile andare ad effettuare un inserimento sul db.
Questa la schermata che si visualizzer alla
partenza dellapplicazione:

Lxml della pagina il seguente:


<RelativeLayout xmlns:android=http://schemas.android.com/apk/res/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width=match_parent
android:layout_height=match_parent
tools:context=.DbQuery >

Come si vede molto semplice ed costituita


da una TextView, un EditText e da un Button.
Andiamo ora a creare lActivity dbQuery che ci
permetter di inserire e recuperare i dati dal database.
La pagina avr questa struttura:

<RelativeLayout
android:layout_width=match_parent
android:layout_height=match_parent
>
<LinearLayout
android:id=@+id/linearLayout
android:layout_width=match_parent
android:layout_height=wrap_content
android:orientation=vertical
>
<TextView
android:layout_width=match_parent
android:layout_height=wrap_content
android:text=Dati inserimento utente
android:singleLine=true
android:textSize=20sp/>
<EditText
android:id=@+id/nome
android:layout_width=match_parent
android:layout_height=wrap_content

{83

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


android:hint=nome
android:inputType=text
android:singleLine=true />
<EditText
android:id=@+id/cognome
android:layout_width=match_parent
android:layout_height=wrap_content
android:hint=cognome
android:inputType=text />
<EditText
android:id=@+id/indirizzo
android:layout_width=match_parent
android:layout_height=wrap_content
android:hint=indirizzo
android:inputType=text />
<Button
android:id=@+id/inserisci
android:layout_width=match_parent
android:layout_height=wrap_content
android:text=Inserisci/>
</LinearLayout>
<ScrollView
android:layout_width=match_parent
android:layout_height=match_parent
android:layout_below=@id/linearLayout
>
<TableLayout
android:id=@+id/tabella
android:layout_width=match_parent
android:layout_height=wrap_content
>
</TableLayout>
</ScrollView>
</RelativeLayout>

seriti i dati allinterno del database utilizzando il


metodo:
public long inserimentoUtente(String nome, String
cognome,String indirizzo){
long result;
String table = utente;
ContentValues valoriDaInserire = new ContentValues();
valoriDaInserire.put(nome, nome);
valoriDaInserire.put(cognome, cognome);
valoriDaInserire.put(indirizzo, indirizzo);
result = db.insert(table, null, valoriDaInserire);
}

Come prova dellavvenuto inserimento, recuperiamo i dati inseriti effettuando una query sul db
che ci restitutisca tutte le righe della tabella ed
inserendo i valori allinterno di una tableview.
public ArrayList<UtenteBean> elencoUtenti(){
ArrayList<UtenteBean> lista = new
ArrayList<UtenteBean>();
Cursor cursor = db.query(utente, null, null, null,
null, null, null);
while (cursor.moveToNext()) {
UtenteBean utente = new UtenteBean();
utente.setNome(cursor.getString(cursor.
getColumnIndex(nome)));
utente.setCognome(cursor.getString(cursor.
getColumnIndex(cognome)));
utente.setIndirizzo(cursor.getString(cursor.
getColumnIndex(indirizzo)));

</RelativeLayout>

Come si vede, la prima parte fissa con 3 EditText, mentre la tabella (TableLayout) inserita
in uno ScrollView in modo da far scorrere i dati
se eccedono le dimensioni dello schermo.
Da questa sar, quindi, possibile inserire il
nome, cognome e indirizzo.
Alla pressione del tasto Inserisci verranno in-

return result;

lista.add(utente);
}
cursor.close();
return lista;

Questa la schermata che si visualizzer per


linserimento dellutente e la visualizzazione
della tabella:

{84

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


riga = (TableRow) inflater.inflate(R.layout.row_
table, null);
nome = (TextView) riga.findViewById(R.
id.nomeRow);
nome.setText(utenteBean.getNome());
cognome = (TextView) riga.findViewById(R.
id.cognomeRow);
cognome.setText(utenteBean.getCognome());
indirizzo = (TextView) riga.findViewById(R.
id.indirizzoRow);
indirizzo.setText(utenteBean.getIndirizzo());

tabella.addView(riga);

Dopo aver recuperato i dati dal databbase, la

In cui viene fatto linflate del layout row_table

tabella viene popolata grazie al metodo:

che rappresenta una riga della tabella:

private void aggiornaTabella(){


DbCrypt dbCrypt= DbCrypt.getInstance();
ArrayList<UtenteBean> lista = dbCrypt.elencoUtenti();
TableLayout tabella = (TableLayout)
findViewById(R.id.tabella);
tabella.removeAllViews();
LayoutInflater inflater = (LayoutInflater)
this.getSystemService(LAYOUT_INFLATER_SERVICE);
TableRow riga = (TableRow) inflater.inflate(R.
layout.row_table, null);
TextView nome = (TextView) riga.findViewById(R.
id.nomeRow);
nome.setText(Nome);
TextView cognome = (TextView) riga.
findViewById(R.id.cognomeRow);
cognome.setText(Cognome);
TextView indirizzo = (TextView) riga.
findViewById(R.id.indirizzoRow);
indirizzo.setText(Indirizzo);
tabella.addView(riga);
for (UtenteBean utenteBean : lista) {

<TableRow xmlns:android=http://schemas.android.
com/apk/res/android
android:layout_width=match_parent
android:layout_height=wrap_content
android:gravity=center_horizontal>
<TextView
android:id=@+id/nomeRow
android:layout_width=100dp
android:layout_height=wrap_content
android:text=Medium Text
android:textColor=@android:color/black
android:textAppearance=?android:attr/textAppearanceMedium />
<TextView
android:id=@+id/cognomeRow
android:layout_width=100dp
android:layout_height=wrap_content
android:text=Medium Text
android:textColor=@android:color/black
android:textAppearance=?android:attr/textAppearanceMedium />
<TextView
android:id=@+id/indirizzoRow
android:layout_width=100dp
android:layout_height=wrap_content
android:text=Medium Text
android:textColor=@android:color/black

{85

{86

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


android:textAppearance=?android:attr/textAppearanceMedium />

Allegato

Dimensione

CryptDB.zip

240.36 KB

</TableRow>

Con questo siamo in grado di memorizzare e di


prelevare delle informazioni da un db criptato.
Qualche spunto per prendere confidenza
con lutilizzo del database.
Chi vuole pu continuare a lavorare su questo
progetto creando una pagina iniziale in cui si
pu scegliere se:
dividere il flusso attuale in due pagine (inserimento e recupero dati);
cancellazione di un singolo record;
cancellazione di tutti i record della tabella.
Inoltre, partendo da quello che abbiamo imparato in questo articolo, si potrebbe realizzare
unapplicazione che sfrutti i concetti visti in grado memorizzazione delle password in un DB
cryptato e quindi non doverle ricordare ma recuperarle grazie ad un click su un bottone della
nostra applicazione.
Buon divertimento.
Nel prossimo articolo creeremo unapplicazione
che sfrutta il Bluetooth del telefono; ci ci permetter di collegare il telefono con una scheda
esterna.
Per saperne di pi, continuate a seguirci.
Il file allegato ha i sorgenti del progetto, escluse
le librerie (troppo pesanti) che possono essere
scaricate dai link allinterno dellarticolo.

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:

http://it.emcelettronica.com/corso-su-android-impariamo-proteggere-i-nostri-dati

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca

8. Lavoriamo con il Bluetooth del


nostro smartphone

niziamo a fare la lista della spesa e vedere

Di seguito uno schema di massima:

cosa ci serve :

telefono Android con Bluetooth (ancora non


ho trovato un telefono con sistema operativo
Android che non abbia il Bluetooth);
Arduino UNO;
scheda rel;
modulo Bluetooth (ho usato un RN41) certificato con il BT2.1 quindi con telefoni che

Di seguito la scheda fronte e retro dopo i colle-

hanno Bluetooth 4.0 potrebbe non funziona-

gamenti:

re.

Cosa andremo a realizzare:


realizzeremo unapplicazione in grado da accendere/spegnere i 4 rel messi a disposizione sulla scheda. Lapplicazione si connetter
al modulo Bluetooth e scambier dei dati con
Arduino che azioner i rel.
Ci concentreremo solo su questo aspetto, lasciando come esercizio, al lettore, il compito di
recuperare lo stato dei rel alla partenza dellapplicazione ed impostando gli stati dei bottoni.
Prima di iniziare lo sviluppo vero e proprio occorre collegare i componenti. Per Arduino non ci
sono problemi la scheda utilizzata ha un alloggiamento con il quale semplice assemblarlo,
mentre per il modulo Bluetooth occorre prendere saldatore e stagno e collegare i piedini:
RN-41
Rx
Tx
3,3v
Gnd

Piedini Arduino
D2
D3
3,3v
Gnd

Una volta che la scheda pronta, possiamo


iniziare ad implementare la nostra applicazione
che avr uninterfaccia grafica molto semplice,
sar costituita da 4 pulsanti che attiveranno/

{87

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


disattiveranno i rel sulla scheda e da quattro

di 115200 che risulta essere troppo elevato, per

LED che indicheranno lo stato dei rel:

questo motivo viene utilizzato il metodo :

verde: acceso

void setupBT(){
bluetooth.begin(115200); // Di default settato a
115200bps
// manda il comando di per far entrare il modulo bt
in command mode
bluetooth.print($);
bluetooth.print($);
bluetooth.print($);
delay(100); // Ritardo per aspettare la risposta del
modulo btD
bluetooth.println(U,9600,N); // cambia temporaneamente il baudrate a 9600,
no parity

rosso: spento

bluetooth.begin(9600); // Setta il Bluetooth seriale


a 9600
}

Iniziamo guardando la parte di codice relativa ad

che imposta il baud rate a 9600.

Arduino. Utilizzeremo la classe SoftwareSerial

Dobbiamo ora definire un protocollo per attivare

per comunicare con il modulo Bluetooth, quindi,

disattivare i rel. Si pu pensare di inviare una

allinizio del nostro sketch, ci sar listruzione:

stringa kx_ys dove x indica il numero del rel


che va da 1 a 4 ed y indica se acceso o spento

SoftwareSerial bluetooth(rxPin, txPin); // RX, TX

(0,1). Mentre il carattere s indica un carattere


di fine comando, infatti vengono recuperati i valori provenienti dal modulo e assegnati ad una

dove
const int rxPin = 3; //SoftwareSerial RX pin
const int txPin = 2; //SoftwareSerial TX pin

definiamo i pin collegati ai rel:


const int RELE_K1
const int RELE_K2
const int RELE_K3
const int RELE_K4

= 7;
= 6;
= 4;
= 5;

//pin collegato al rel K1


//pin collegato al rel K2
//pin collegato al rel K3
//pin collegato al rel K4

e li andiamo a definire come pin di OUTPUT.


pinMode(RELE_K1, OUTPUT);
pinMode(RELE_K2, OUTPUT);
pinMode(RELE_K3, OUTPUT);
pinMode(RELE_K4, OUTPUT);

Una cosa da tenere in considerazione: il modulo


Bluetooth utilizzato ha per default un baud rate

stringa, e quando arriva il carattere s il ciclo si


ferma perch s indica la fine del comando.
Andiamo a vedere il corpo dello sketch.
Nel metodo loop troviamo:
if(bluetooth.available() > 0){
Serial.print(Ricevuto: );
char c = Bluetooth.read();
if (c == s){
Serial.println( stop );
}else {
strsValues += c;
Serial.println( strsValues );
}
delay(5);
}else if (strsValues == k1_0){
Serial.println( set k1 off );
digitalWrite(RELE_K1, LOW);
resetValues();
}else if (strsValues == k1_1){

{88

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


Serial.println( set k1 on );
digitalWrite(RELE_K1, HIGH);
resetValues();
}else if (strsValues == k2_0){
Serial.println( set k2 off );
digitalWrite(RELE_K2, LOW);
resetValues();
}else if (strsValues == k2_1){
Serial.println( set k2 on );
digitalWrite(RELE_K2, HIGH);
resetValues();
}else if (strsValues == k3_0){
Serial.println( set k3 off );
digitalWrite(RELE_K3, LOW);
resetValues();
}else if (strsValues == k3_1){
Serial.println( set k3 on );
digitalWrite(RELE_K3, HIGH);
resetValues();
}else if (strsValues == k4_0){
Serial.println( set k4 off );
digitalWrite(RELE_K4, LOW);
resetValues();

}else if (strsValues == k4_1){


Serial.println( set k4 on );
digitalWrite(RELE_K4, HIGH);
resetValues();
}
delay(100);

Viene utilizzata la connessione Serial solo per


vedere cosa viene inviato dalla nostra applicazione.
Una volta definito lo sketch, passiamo al codice
Android.
Quando dobbiamo connetterci ad un dispositivo Bluetooth dobbiamo fare prima una ricerca

Creiamo il progetto BTArduinoComunicator che


sar costituito da tre classi:
MainActivity: activity principale;
BluetoothService: classe che si occupa
della connessione Bluetooth;
DeviceListActivity: activity che visualizza
la lista di dispositivi disponibili.
Per gestire lo stato dei rel stato usato un array
private int[] aReleStatus = new int[4];
che pu assumere i valori:
private int RELE_OFF = 0;
private int RELE_ON = 1;

che verranno modificati quando si attivano/disattivano i rel.


Come detto allinizio, assumiamo che i rel sia-

dei dispositivi disponibili per poi scegliere nostro

mo spenti e quindi utilizziamo il metodo initRe-

modulo BT e fare il pairing con questo. Quindi

leStatus per settare lo stato:

creiamo unActivity che fa questo:

{89

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


/**
* inizializza i rele come spenti
*/
private void initReleStatus(){
for (int i=0; i<4; i++){
aReleStatus[i] = RELE_OFF;
}
}

Nella MainActivity come prima cosa da fare


recuperare ladapter
mBluetoothAdapter = BluetoothAdapter.getDe-

btnK2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//
sendMessage(k2_0s);
}
});

btnK3 = (Button) findViewById(R.id.idBtnK3);


btnK3.setOnClickListener(new OnClickListener() {
public void onClick(View v) {

faultAdapter();
}
});

dopodich sul metodo onStart verifichiamo se


attivo; se non lo , viene effettuato un controllo
per attivarlo
if (!mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);

} else {
if (btService == null) enableToSend();
}

Nel metodo enableToSend() andiamo a defini-

sendMessage(calcNewState(k2,K2));

sendMessage(calcNewState(k3,K3));

btnK4 = (Button) findViewById(R.id.idBtnK4);


btnK4.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
}
});

sendMessage(calcNewState(k4,K4));

// Initialize the BluetoothService to perform


Bluetooth connections
btService = new BluetoothService(this,
mHandler);

// Initialize the buffer for outgoing messages


mOutStringBuffer = new StringBuffer();

re i listener per i bottoni e andiamo ad instanziare la classe BluetoothService


private void enableToSend() {
Log.d(TAG, enableToSend);
btnK1 = (Button) findViewById(R.id.idBtnK1);
btnK1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//
sendMessage(k1_0s);
Log.d(TAG, k1 pressed);
sendMessage(calcNewState(k1,K1));
}
});
btnK2 = (Button) findViewById(R.id.idBtnK2);

Sul metodo onResume va controllato se il servizio partito o meno:


if (btService != null) {
if (btService.getState() == BluetoothService.
STATE_NONE) {

btService.start();

Mentre nel metodo onDestroy ci accertiamo di


fare lo stop del servizio:

{90

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


if (btService != null) btService.stop();

Il metodo sendMessage invia una stringa al dispositivo:


private void sendMessage(String message) {
if (btService.getState() != BluetoothService.STATE_CONNECTED) {
Toast.makeText(this, R.string.not_connected,
Toast.LENGTH_SHORT).show();
return;
}

effettuare il pairing, tramite la classe DeviceListActivity (che non altro che una lista) richiamata dal menu nella MainActivity:
@Override
public boolean onOptionsItemSelected(MenuItem
item) {
Intent serverIntent = null;
switch (item.getItemId()) {
case R.id.secure_connect_scan:
serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE);
return true;
case R.id.discoverable:

if (message.length() > 0) {

ensureDiscoverable();
return true;

byte[] send = message.getBytes();


btService.write(send);
mOutStringBuffer.setLength(0);
}

Mentre il metodo calcNewState serve per calcolare lo stato del rel e quindi restituisce il valore da inviare alla scheda:
private String calcNewState(String who, int rele){
if (aReleStatus[rele] == RELE_ON){
Log.d(TAG, who + actual status ON --> set
to OFF);
//setta il nuovo stato del rel
aReleStatus[rele] = RELE_OFF;
setImages(who, false);
return who + _ + RELE_OFF + s;
}else {
Log.d(TAG, who + actual status OFF --> set
to ON);
//setta il nuovo stato del rel
aReleStatus[rele] = RELE_ON;
setImages(who, true);
return who + _ + RELE_ON + s;
}
}

Se non presente nessun accoppiato,occorre

}
return false;

Il discover dei dispositivi viene fatto tramite la


classe BluetoothAdapter, infatti, viene recuperato ladapter di default:
mBtAdapter = BluetoothAdapter.getDefaultAdapter();

e viene subito controllato se ci sono dispositivi


gi accoppiati, e se presenti vengono aggiunti
ad una lista:
Set<BluetoothDevice> pairedDevices = mBtAdapter.
getBondedDevices();

Per il discovery viene utilizzato il metodo che


richiama doDiscovery:
private void doDiscovery() {
Log.d(TAG, doDiscovery());
setProgressBarIndeterminateVisibility(true);
setTitle(R.string.scanning);
findViewById(R.id.title_new_devices).
setVisibility(View.VISIBLE);

{91

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


una volta ottenuta la lista, quando si selezioif (mBtAdapter.isDiscovering()) {
mBtAdapter.cancelDiscovery();
}
}

mBtAdapter.startDiscovery();

che lancia la ricerca in modo asincrono e quando vengono trovati dei dispositivi viene lanciato
un intent BluetoothDevice.ACTION_FOUND
quindi occorre registrare un BroadcastReceiver
a tale intent:
IntentFilter filter = new IntentFilter(BluetoothDevice.
ACTION_FOUND);
this.registerReceiver(mReceiver, filter);

dove mReceiver definito come:


private final BroadcastReceiver mReceiver = new
BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent
intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.
equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(
BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter.add(device.
getName() + \n +
device.getAddress());
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
if (mNewDevicesArrayAdapter.getCount()
== 0) {
String noDevices = getResources().
getText(R.string.none_found)
.toString();
mNewDevicesArrayAdapter.
add(noDevices);
}
}
}
};

na un dispositivo, il controllo torna alla classe


MainActivity e precisamente nel metodo onActivityResult da cui viene chiamato il metodo connectDevice che grazie alla classe BluetoothService si connette al dispositivo:
private void connectDevice(Intent data) {
String address = data.getExtras()
.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
BluetoothDevice device = mBluetoothAdapter.
getRemoteDevice(address);
}

btService.connect(device);

A questo punto analizziamo la classe BluetoothService. Quando si vogliono connettere due


dispositivi uno deve funzionare da server e laltro da client. Nel nostro caso, il modulo Bluetooth sar il server ed il telefono funzioner da
client. Il codice sorgente avr al suo interno entrambi i modi di funzionamento, ovvero il telefono funziona da server e il modulo Bluetooth da
client. Di seguito vedremo la prima modalit.
Per far questo viene implementato un thread:
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType
= Secure;//
indica connessione sicura
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToService
Record(MY_UUID_SECURE);
} catch (IOException e) {
Log.e(TAG, Socket Type: + mSocketType
+ create() failed, e);
}
mmSocket = tmp;

{92

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


}
public void run() {
Log.i(TAG, BEGIN mConnectThread SocketType: + mSocketType);
setName(ConnectThread + mSocketType);
mAdapter.cancelDiscovery();
try {
mmSocket.connect();
} catch (IOException e) {
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, unable to close() + mSocketType +
socket during connection failure,
e2);
}
connectionFailed();
return;
}

Message msg = mHandler.


obtainMessage(MainActivity.MESSAGE_DEVICE_
NAME);
Bundle bundle = new Bundle();
bundle.putString(MainActivity.DEVICE_NAME,
device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);

A questo punto premendo sui bottoni per attivare spegnere viene chiamato il metodo
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}

Il quale invia i dati al modulo Bluetooth e vengono analizzati da Arduino.

synchronized (BluetoothService.this) {
mConnectThread = null;
}

ype);
}

connected(mmSocket, mmDevice, mSocketT-

public void cancel() {


try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, close() of connect + mSocketType + socket failed, e);
}
}
}

Quando viene impiegato il Bluetooth, occorre


inserire nel maifest i permessi:
<uses-permission android:name=android.permission.
bluetooth_ADMIN />
<uses-permission android:name=android.permission.
bluetooth />

Abbiamo visto come sia possibile pilotare i rel


della scheda.
Lascio alla fantasia del lettore i possibili utilizzi
per esempio utilizzarla per accendere le luci di
casa, per aprire il garage, etc.

questo aspetta finch non viene connesso un

Come detto e visto, nellarticolo non si tiene

device mmSocket.connect();

conto dello stato dei rel: si presume che allo

appena il device viene connesso, viene notifica-

start dellapplicazione i rel siano tutti spenti.

to alla classe MainActivity.

Chi vuole pu analizzare lo stato dei rel alla

{93

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


partenza dellapp e modificare il metodo initReleStatus per settare lo stato attuale.
Nel prossimo articolo vedremo come possibile comunicare con Arduino utilizzando un modo
alternativo al Bluetooth: la portaUSB.

Allegato

Dimensione

readBT.zip

913 bytes

BTArduinoCommunicator.zip

583.8 KB

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:
http://it.emcelettronica.com/corso-su-android-lavoriamo-con-bluetooth-del-nostro-smartphone

{94

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca

9. Lavoriamo con la porta USB


del telefono

ome prima cosa bisogna sapere che Android supporta una variet di periferiche
USB attraverso 2 modalit:

USB host: agisce come USB host e fornisce


corrente al bus e fa un check sul numero di
dispositivi connessi;
USB accessory: Il dispositivo connesso
funziona come host e da corrente al bus.

un telefono Android con SO maggiore o


uguale 3.1 (ho usato un Samsung S2);
Arduino UNO (o altro tipo);
un cavo OTG per collegare Arduino al telefono.
Partiamo da Android creando un progetto: ArduinoUSBCommunication (ho usato API Level
15).
Il nostro layout main.xml risulta essere molto
semplice: un LinearLayout con una SeekBar e
due Button:
<?xml version=1.0 encoding=utf-8?>
<LinearLayout
xmlns:android
= http://schemas.android.
com/apk/res/android
android:layout_width = fill_parent
android:layout_height = fill_parent
android:orientation
= vertical >

Entrambe le modalit sono state introdotte dalle


API 12, quindi serve un telefono o tablet con sistema operativo maggiore o uguale alla 3.1. Noi
ci concentreremo sulla prima modalit.
Come abbiamo detto lidea di controllare lintensit di luce del led, per far questo nella nostra interfaccia utilizzeremo una SeekBar e 2
bottoni per abilitare/disabilitare il controllo della barra.Cosa ci serve:

<SeekBar
android:id
= @+id/seekBar1
android:layout_width = match_parent
android:layout_height = wrap_content
android:max
= 255
android:progress
= 0
android:padding
= 40dp
/>
<Button
android:id
= @+id/idBtnEnablePWM
android:layout_width = wrap_content
android:layout_height = wrap_content
android:text
= @string/enablePWM
android:layout_gravity = center_horizontal/>
<Button
android:id
= @+id/idBtnDisablePWM
android:layout_width = wrap_content
android:layout_height = wrap_content

{95

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca


android:text
= @string/disablePWM
android:layout_gravity = center_horizontal/>
</LinearLayout>
Prima di analizzare la nostra Activity andiamo a
vedere cosa deve essere inserito allinterno del
manifest:
prima di tutto dobbiamo garantire che il dispositivo supporti la modalit USB host, quindi inseriamo:
<uses-feature android:name=android.hardware.
usb.host />
Bisogna poi accertarsi che il livello minimo di
API sia maggiore od uguale a 12, nel nostro
caso risulta essere:
<uses-sdk
android:minSdkVersion=13
android:targetSdkVersion=14 />
Dobbiamo definire un <intent-filter> per fare
in modo che la nostra Activity riceva notifica
quando un dispositivo viene connesso alla porta USB, in aggiunta va inserito un tag <metadata> per identificare il dispositivo che viene
agganciato:
<intent-filter>
<action android:name=android.hardware.usb.
action.USB_DEVICE_ATTACHED/>
</intent-filter>
<meta-data android:name=android.hardware.usb.
action.USB_DEVICE_ATTACHED
android:resource=@xml/device_filter />
Dove device_filter.xml deve essere inserito
sotto la cartella res/xml/ e contiene il vendor-id
che rappresenta Arduino.

<?xml version=1.0 encoding=utf-8?>


<resources>
<usb-device vendor-id=9025 />
</resources>
Quindi il nostro manifest risulter essere:
<?xml version=1.0 encoding=utf-8?>
<manifest xmlns:android=http://schemas.android.com/apk/res/android
package=com.test
android:versionCode=1
android:versionName=1.0 >
<uses-sdk
android:minSdkVersion=13
android:targetSdkVersion=14 />
<uses-feature android:name=android.hardware.usb.host />
<application
android:icon=@drawable/ic_launcher
android:label=@string/app_name >
<activity
android:label=@string/app_name
android:name=.ArduinoActivity
android:launchMode=singleTask
android:screenOrientation=portrait>
<intent-filter>
<action android:name=android.intent.
action.MAIN />
<category android:name=android.intent.
category.LAUNCHER />
</intent-filter>
<intent-filter>
<action android:name=android.hardware.
usb.action.USB_DEVICE_ATTACHED />
</intent-filter>
<meta-data android:name=android.hardware.usb.action.USB_DEVICE_ATTACHED
android:resource=@xml/device_filter />
</activity>
</application>
</manifest>
Una volta definiti i parametri nel manifest creiamo la nostra Activity: ArduinoActivity. Questa

{96

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca


sar lActivity che verr aperta nel momento in
cui sar connesso Arduino al telefono, quindi
dovremo implementare un metodo che elenchi i
dispositivi collegati:
private void discoverDevice(){
mUsbManager = (UsbManager)
getSystemService(Context.USB_SERVICE);
UsbManager usbManager = (UsbManager)
getSystemService(Context.USB_SERVICE);
HashMap deviceList = usbManager.getDeviceList();
Iterator deviceIterator = deviceList.values().
iterator();
Toast.makeText(this.getApplicationContext(),
Device Count : +deviceList.size()
+ , Toast.LENGTH_SHORT).show();
UsbDevice usbDevice = null;
while (deviceIterator.hasNext()) {
next();

usbDevice = (UsbDevice)deviceIterator.

Toast.makeText(this.getApplicationContext(), Product ID : +
usbDevice.getProductId() + , Toast.LENGTH_
SHORT).show();
}
if (usbDevice != null) initDevice(usbDevice);
}
Questo sfrutta il metodo getDeviceList() della

// Arduino Serial usb Conv


conn.controlTransfer(0x21, 34, 0, 0, null, 0, 0);
conn.controlTransfer(0x21, 32, 0, 0, new
byte[] { (byte) 0x80,
0x25, 0x00, 0x00, 0x00, 0x00, 0x08 }, 7,
0);

UsbInterface usbIf = mDevice.getInterface(1);


for (int i = 0; i < usbIf.getEndpointCount();
i++) {
if (usbIf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (usbIf.getEndpoint(i).getDirection() ==
UsbConstants.USB_DIR_IN){
System.out.println(>>> EP_IN[ + i
+ ]);
epIN = usbIf.getEndpoint(i);
}else{
System.out.println(>>> EP_OUT[ +
i + ]);
epOUT = usbIf.getEndpoint(i);
}
}
}
arduinoController = new
ArduinoConnection(epOUT, conn);
new Thread(arduinoController).start();
}

classe UsbManager, che mappa tutti i dispositivi connessi. Da questa cicliamo sui dispositivi
connessi; nel nostro caso c solo Arduino, recuperiamo loggetto UsbDevice ed andiamo ad
inizializzare il dispositivo:
private void initDevice(UsbDevice mDevice){
UsbDeviceConnection conn = mUsbManager.
openDevice(mDevice);
if (!conn.claimInterface(mDevice.getInterface(1), true)) {
return;
}

Per prima cosa apriamo una connessione con


questa scheda
UsbDeviceConnection conn = mUsbManager.
openDevice(mDevice);
// Arduino Serial usb Conv
conn.controlTransfer(0x21, 34, 0, 0, null, 0, 0);
conn.controlTransfer(0x21, 32, 0, 0, new
byte[] { (byte) 0x80,
0x25, 0x00, 0x00, 0x00, 0x00, 0x08 }, 7,
0);
Successivamente recuperiamo lUsbEndPiont

{97

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca


di output per mandare i dati ad Arduino. Lo

del metodo run invieremo i dati ad Arduino nel

scambio vero e proprio viene effettuato in un

momento in cui vengono settati dalla nostra Ac-

Thread separato, altrimenti la comunicazione

tivity (vedremo di seguito come) grazie al meto-

dei dati con Arduino renderebbe inutilizzabile

do send(byte[] data)

linterfaccia grafica. Vediamo quindi come fat-

Nel metodo onCreate della nostra Activity, re-

ta la classe ArduinoConnection

cuperiamo i due Button, che come detto sopra


ci serviranno per abilitare o disabilitare linvio

public class ArduinoConnection implements Runnable{


public Handler mHandler;
private volatile UsbEndpoint mOutUsbEndpoint;
BroadcastReceiver mReceiver;
private byte[] data = new byte[]{0x00};
private volatile UsbDeviceConnection mUsb
Connection;
public ArduinoConnection(UsbEndpoint mOutUsbEndpoint,
UsbDeviceConnection mUsbConnection){
this.mOutUsbEndpoint = mOutUsbEndpoint;
this.mUsbConnection = mUsbConnection;
}

dei dati ad Arduino. Con il Button di attivazione


invieremo la lettera P per indicare ad Arduino di
iniziare a leggere i dati ed invieremo la lettera
N per indicare di smettere di interpretare i dati.
Limplementazione dei due Button risulter essere:
Button btnEnablePWM = (Button)
findViewById(R.id.idBtnEnablePWM);
btnEnablePWM.setOnClickListener(new
OnClickListener() {
@Override
public void onClick(View v) {
String s = P;
try {
byte[] b = s.getBytes(US-ASCII);
System.out.println(EnablePWM);

public void send( byte[] data) {


this.data = data;
System.out.println(send data);
}
@Override
public void run(){
System.out.println(Thread started);
while (true){
if (data != null){
System.out.println(sending data);
mUsbConnection.
bulkTransfer(mOutUsbEndpoint, data, data.
length, 0);
System.out.println(data sent);
data = null;
}
}
}
}
che deve implementare Runnable e allinterno

if (arduinoController != null) arduinoController.send(b);


} catch (Exception e) {

}
});

e.printStackTrace();

Button btnDisablePWM = (Button)


findViewById(R.id.idBtnDisablePWM);
btnDisablePWM.setOnClickListener(new
OnClickListener() {
@Override
public void onClick(View v) {
String s = N;

{98

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca

try {
byte[] b = s.getBytes(US-ASCII);

@Override
public void onProgressChanged(SeekBar
seekBar,int progress,boolean fromUser){
if(fromUser){

if (arduinoController != null) arduinoController.send(b);


} catch (Exception e) {

}
});

int i = (int)progress;
sValue = i + a;
try {
byte[] b = sValue.getBytes(US-

e.printStackTrace();

Non ci resta che recuperare la SeekBar ed


implementare il metodo OnSeekBarChan-

ASCII);

if(arduinoController != null) arduinoController.send(b);


} catch (UnsupportedEncodingExcep

tion e) {

geListener e inseriremo il codice da inviare


ad Arduino nellimplementazione del metodo
onProgressChanged cosi da inviare dati ogni
volta che c un cambiamento nella SeekBar.

}
});

e.printStackTrace();

Per farlo utilizziamo il metodo send illustrato


precedentemente della classe ArduinoController. Ma cosa stiamo mandando ad Arduino?
Gli inviamo il valore della SeekBar che varia da
un minimo di 0 ad un massimo di 255 seguito
dalla lettera a che ci servir nello sketch per
capire quando la serie di byte inviati che rappresentano un valore terminata:

Passiamo ora al codice da inserire su Arduino


dove viene utilizzato il PIN 11 per collegare il led
e le due variabili:
const char GAME_NO
= N;
const char GAME_PWM
= P;
per far in modo alla SeekBar di agire o meno

SeekBar seekBar = (SeekBar)findViewById(R.


id.seekBar1);

sul led.

seekBar.setOnSeekBarChangeListener(new
SeekBar.OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar
seekBar) {

quello che arriva dalla seriale un numero e

}
@Override
public void onStartTrackingTouch(SeekBar
seekBar) {

P o una N e viene settata la variabile currentGa-

Nel loop, viene controllato come prima cosa se


viene usata la variabile strValue per concatenare i valori numerici che arrivano dal telefono.
Se invece il dato che arriva dalla seriale un carattere, allora viene controllato se arrivata una
me per abilitare o meno il led, altrimenti se arriva la lettera a indica la fine della seguenza da
inviare al PIN 11 quindi viene convertita in intero
dutyCycle = atoi(strValue); e viene inviata al PIN

{99

{100

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


11 analogWrite(LED_PWM_11, dutyCycle);
dopo di che vengono resettati i valori utilizzati.
#define LED_PWM_11 11

strValue[index++] = ch; // add the ASCII character to the string;


}
} //end if (currentGame == GAME_PWM)

const char GAME_NO


= N;
const char GAME_PWM
= P;
const char STOP_SEQUENCE_OF_BYTE = a;
int dutyCycle;
//
char strValue[6];
// stringa di valori letti
int index = 0;
// indice dei valori letti
char currentGame; // indica il gioco di luci corrente,
inizialmente nessuno
void setup()
{
Serial.begin(9600);
currentGame = GAME_NO;
pinMode(LED_PWM_11, OUTPUT); //imposta il
pin come output
}
void loop()
{
if( Serial.available())
{
char ch = Serial.read();
//controlla se un carattere
if (!isDigit(ch)){
//controlla quale gioco di luce selezionato
if (ch == GAME_PWM){
currentGame = GAME_PWM;
}else if (ch == GAME_NO){
currentGame = GAME_NO;
analogWrite(LED_PWM_11, 0);
}else if (ch == STOP_SEQUENCE_OF_BYTE){
dutyCycle = atoi(strValue); // use atoi to convert the string to an int
analogWrite(LED_PWM_11, dutyCycle); //
manda al led il valore letto dalla
seriale
resetValue();
}
}else {
// un numero
if (currentGame == GAME_PWM){
if(index < 5 && isDigit(ch) ){

else {
//non deve fare niente
}
}//end if( isDigit(ch))
}
}
void resetValue(){
index = 0;
for (int i=0; i<5; i++){
strValue[i] = 0;
}
}

Notare che quando viene collegato Arduino con


il telefono, verr mostrato un popup del sistema
operativo che chiede se aprire la nostra applicazione quando il dispositivo connesso, acconsentire e se lapplicazione era chiusa verr
aperta.
Non ci resta che provare il tutto!

Conclusioni
Abbiamo quindi visto come semplice inviare
dati ad Arduino connettendo il telefono tramite
la porta USB. Come esercizio si potrebbe pensare di aggiungere un altro led e far in modo
di abilitare uno o laltro attraverso lapplicazione
sul telefono, per esempio introducendo un altro
Button che abiliti il secondo led e disabiliti il primo cos da pilotarli con la stessa SeekBar, oppure introducendone una nuova cos da averne
una per ciascuno. Buon divertimento.

Allegato

Dimensione

arduinoUSB.zip

841 bytes

ArduinoUSBCommunication.zip

73.52 KB

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:
http://it.emcelettronica.com/corso-su-android-lavoriamo-con-porta-usb-del-telefono

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca

10. Come collegare uno smartphone Android ad Arduino via Ethernet

a nostra scelta di sviluppare un webserver


su Arduino si collega ovviamente alla possibilit di poter gestire direttamente da remoto

un qualsiasi dispositivo ad esso collegato (rel,


etc) senza passare per linguaggi ad alto livello,
che necessitano di elaboratori pi potenti.
Quindi avremo il doppio scopo di sviluppare un
webserver sullArduino e di vedere lutilizzo di
una webview su Android.
La nostra applicazione Android si collegher
allArduino attraverso la rete internet oppure attraverso una rete locale alla quale saranno col-

Per poter usare lo shield ethernet necessario

legati entrambi i dispositivi. Quindi lapplicazio-

importare alcune librerie (gi presenti nel sof-

ne sar formata da una webview in cui verranno

tware di Arduino se si utilizza una delle schede

visualizzati due pulsanti, ON & OFF, grazie ai

ethernet compatibile).

quali potremmo far scattare un rel per far ac-

Per importarle si deve andare su Sketch-

cendere un LED.

>importa libreria->Ethernet. Verranno aggiun-

Concentriamoci per ora sul codice dellArduino.

te al nostro codice le seguenti librerie:

Per sviluppare un web server su Arduino abbiamo la necessit di avere:


Arduino;
una scheda ethernet (ho usato la scheda
Tinkerkit);
collegamento ad una rete tramite cavo ether-

#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetClient.h>
#include <EthernetServer.h>
#include <EthernetUdp.h>
#include <util.h>

net.
Per la gestione del rel andremo ad utilizzare il

Per lutilizzo della scheda Tinkerkit andremo ad

Tinkerkit, una scheda che permette attraverso

importare la seguente libreria:

una libreria nativa di richiamare semplicemente


le funzioni messe a disposizione per interagire

#include <TinkerKit.h>

con i nostri sensori e/o attuatori. Sulla scheda

Per poter far riconoscere correttamente la sche-

sono infatti presenti vari pin di input e output a

da ethernet ad Arduino andiamo a definire il mac

seconda se dobbiamo interagire con sensori o

address della scheda ethernet. Questo codice

attuatori.

presente sulla scheda:

{101

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca


;

}
//viene visualizzato il proprio indirizzo ip
Serial.print(My IP address: );
for (byte thisByte = 0; thisByte < 4; thisByte++) {
Serial.print(Ethernet.localIP()[thisByte], DEC);
Serial.print(.);
}
Serial.println();

Nella funzione loop() resteremo in attesa di una


quindi definiamo la nostra variabile:
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0x00, 0x31 };

connessione in ingresso con listruzione:


EthernetClient client = server.available();

Impostiamo la gestione del rel sulloutput 04:


TKRelay relay(O4);
Definiamo su quale porta logica il nostro server
dovr essere in ascolto:
EthernetServer server(80);

quando avremo una connessione da un client


andremo a memorizzare la sua richiesta tramite:
char c = client.read();
response += c;

scegliamo la porta 80 in quanto quella utilizza-

Riconosciamo la fine di una richiesta http in

ta da tutti i browser per il protocollo http.

quanto finisce con una linea vuota (\n).

Ovviamente potremmo tranquillamente sce-

Andremo quindi a leggere la richiesta e rispon-

gliere unaltra porta ma in quel caso dovremmo

deremo al client nel seguente modo:

sviluppare delle chiamate http utilizzando le API


native messe a disposizione da Android.
Nella funzione setup() andiamo ad inizializzare
la connessione con la scheda ethernet e stampare il proprio indirizzo IP sulla console.
void setup() {
// Apertura comunicazione seriale
Serial.begin(9600);
//alla partenza viene messo ad off il rel
relay.off();
//inizia la comunicazione con la scheda ethernet
if (Ethernet.begin(mac) == 0) {
Serial.println(Failed to configure Ethernet
using DHCP);
// non far pi nulla, si lascia in ciclo per sempre:
for(;;)

client.println(HTTP/1.1 200 OK\r\nContentType: text/html\r\nConnection: close\r\n\r\n


<!DOCTYPE html><html><head><title>Arduino
Ethernet</title></head>
<body><p>Gestione rel&egrave;</p><form
method=\get\ action=\/action\>
<button id=\button2\ type=\submit\
name=\value\ value=\ON\>ON</button>
<button id=\button1\ type=\submit\
name=\value\ value=\OFF\>OFF</button></
form>
</body></html>);
Nella risposta sar presente una parte definita
header dove sono presenti parametri riguardanti la richiesta e il codice html che sar il body

{102

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


della nostra risposta http.

Andiamo adesso a sviluppare una webview su

Se nella richiesta presente la stringa ON vuol

Android che ci permetta di raggiungere Arduino

dire che il client ha premuto sul pulsante ON del

e quindi gestire il nostro rel da remoto.

nostro codice html di risposta altrimenti, se

Come visto negli articoli precedenti creiamo un

presente la stringa OFF, si premuto il pulsan-

nuovo progetto ArduinoWebView.

te OFF; infatti alla pressione di uno dei pulsanti

La webview un componente nativo di Android

sar invocata la url dellArduino passando un

che come specificato anche prima ci permette

parametro in get (nella url) in ingresso.

di gestire un browser allinterno della nostra ap-

Questo il codice che effettua il controllo:

plicazione.

if(response.indexOf(ON)>=0){
//on del rel
Serial.println(rele on);
relay.on();
}else if(response.indexOf(OFF)>=0){
//off del
Serial.println(rele off );
relay.off();
}
Lo schema finale sar quindi:

Per esempio possiamo provare a modificare il


semplice pulsante che ci restituisce Arduino
cambiandone il colore. Teoricamente non ci
sono limiti alle modifiche che possiamo apportare.
Tutto ci sar possibile innestando tramite la
webview il codice javascript alla pagina html.
Andiamo a vedere sul nostro codice come inizializzare e configurare la webview.
Andiamo a definire nel nostro layout la webview:

In questo esempio abbiamo inserito il codice


html direttamente nellArduino ma possibile
tramite il modulo Arduino con la SD card salvare e leggere questo codice direttamente da
una SD.
Per poter raggiungere lArduino da qualunque
parte del globo necessario configurare il proprio router collegato ad internet (magari con un
ip statico) aprendo le connessioni in entrata sulla porta 80 sullindirizzo locale assegnato allArduino.
Raccomando inoltre di pensare ad un minimo
di sistema di sicurezza, per esempio con linserimento di un pagina html di login, per evitare
accessi indesiderati.

<RelativeLayout xmlns:android=http://schemas.
android.com/apk/res/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width=match_parent
android:layout_height=match_parent
android:paddingBottom=@dimen/activity_vertical_margin
android:paddingLeft=@dimen/activity_horizontal_margin
android:paddingRight=@dimen/activity_horizontal_margin
android:paddingTop=@dimen/activity_vertical_margin
tools:context=com.example.arduinowebview.
MainActivity$PlaceholderFragment >
<WebView
android:id=@+id/webView
android:layout_width=match_parent
android:layout_height=match_parent/>
</RelativeLayout>

{103

{104

Cors o s u A ndr o i d - Dal l a t eor i a al l a pr at i ca


Che richiameremo nel nostro codice java trami-

Quindi per eseguire direttamente del co-

te:

dice

javascript

baster

fare

webView.

loadUrl(javascript:...).
final WebView webview = (WebView) rootView.
findViewById(R.id.webView);
//abilitiamo lutilizzo di javascript sulla webview
webview.getSettings().
setJavaScriptEnabled(true);
webview.setWebViewClient(new
WebViewClient(){
@Override
public void onPageFinished(WebView view,
String url) {
//Tramite javascript coloriamo i pulsanti di
rosso
webview.
loadUrl(javascript:document.getElementById(
\button1\).style.
background=#ff0000;document.
getElementById(\button2\)
.style.background=#ff0000;);
super.onPageFinished(view, url);
}
});
//Questo lindirizzo dellarduino
webview.loadUrl(http://192.168.0.119);
return rootView;
}
La nostra Activity risulter essere:

In questo modo abbiamo visto come da codice Android interagire con una pagina web. Nel
caso in cui ci fosse la necessit per una pagina
web di interagire con Android basterebbe definire una classe JavascriptInterface con dei metodi al suo interno e configurarla con la webview
nel seguente modo:
JavascriptInterface javascriptInterface = new JavascriptInterface();
webview.addJavascriptInterface(JavascriptInterfa
ce, jsInterface);
fatto questo baster scrivere da codice javascript sulla pagina html:
jsInterface.metodoNativo();
dove metodoNativo un metodo definito nella classe JavascriptInterface e questo verr
eseguito. possibile inoltre passare dei dati in
input a questo metodo e far restituire dei valori
di ritorno.
Questa tecnica viene spesso usata per lanciare
la fotocamera del telefono da una pagina web.

Conclusioni
In questo articolo abbiamo visto la gesitone da
remoto tramite Android ma un lettore pi perspicace avr capito che sar possibile collegarsi allArduino anche attraverso un normale
browser inserendo semplicemente lindirizzo ip
della scheda e che quindi non necessario sviluppare una nuova app Android ma semplicemente aprire il browser del nostro smartphone

Cors o s u A ndr oi d - Dal l a t eor i a al l a pr at i ca


(non solo Android), PC o MAC. Abbiamo per
colto loccasione per spiegare un po anche il
funzionamento di questo componente webview
che permette di poter interagire dinamicamente
su una semplice pagina statica come quella ritornata dallArduino.

Allegato

Dimensione

ArduinoWebView.zip

38.85 KB

ArduinoWSRele.zip

1.51 KB

Lautore e a disposizione nei commenti per eventuali approfondimenti sul tema dellArticolo.
Di seguito il link per accedere direttamente allarticolo sul Blog e partecipare alla discussione:
http://it.emcelettronica.com/corso-su-android-come-collegare-smartphone-android-ad-arduino-ethernet

{105