78
Dott.Ing.Ivan Ferrazzi ANDROID Introduzione alla programmazione per applicativi Android Dott. Ing. Ivan Ferrazzi V1.1 del 05/08/2013 1/78

ANDROID - ferrazzi.info · (o quasi) per iniziare a programmare. ... android create project --target 1 --name MyFirstApp \--path c:\Users\user\android\workspace\MyFirstApp \

Embed Size (px)

Citation preview

Dott.Ing.Ivan Ferrazzi

ANDROID

Introduzionealla

programmazioneper applicativi

Android

Dott. Ing. Ivan FerrazziV1.1 del 05/08/2013

1/78

Dott.Ing.Ivan Ferrazzi

Copyright ©2013 Dott.Ing. Ivan FerrazziPermission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

2/78

Dott.Ing.Ivan Ferrazzi

Indice generaleINTRODUZIONE....................................................................................................5

Installazione dell'ambiente di lavoro...............................................................5Configurazione dell'ambiente di lavoro............................................................6Concetti base...................................................................................................7

Activity........................................................................................................................................7Intents..........................................................................................................................................7Service.........................................................................................................................................7

Un primo progetto...............................................................................................9Creiamo il nostro progetto...............................................................................9Compilare il progetto.....................................................................................11Installare l'applicazione.................................................................................11

Installazione su dispositivi virtuali...........................................................................................11Installazione su dispositivi reali................................................................................................12

File presenti all'interno di un progetto...........................................................12local.properties..........................................................................................................................12project.properties.......................................................................................................................13AndroidManifest.xml................................................................................................................13res/values/strings.xml................................................................................................................14res/layout/main.xml...................................................................................................................15gen/com/example/myfirstapp/R.java.........................................................................................16

I layout..............................................................................................................18LinearLayout..................................................................................................18RelativeLayout...............................................................................................19ListView.........................................................................................................20GridView........................................................................................................22

Oggetti di output...............................................................................................25TextView........................................................................................................25

Struttura XML...........................................................................................................................25ImageView.....................................................................................................26

Struttura XML...........................................................................................................................26Oggetti di input..................................................................................................27

EditText.........................................................................................................27Struttura XML...........................................................................................................................27Codice Java...............................................................................................................................29

Button............................................................................................................31Struttura XML...........................................................................................................................31Codice Java ..............................................................................................................................32

Checkbox.......................................................................................................32Struttura XML...........................................................................................................................32Codice Java...............................................................................................................................33

RadioGroup e RadioButton.............................................................................34Struttura XML...........................................................................................................................34Codice Java...............................................................................................................................34

ToggleButton.................................................................................................34Struttura XML...........................................................................................................................34Codice Java...............................................................................................................................35

Spinner..........................................................................................................35Struttura XML...........................................................................................................................35

3/78

Dott.Ing.Ivan Ferrazzi

Codice Java...............................................................................................................................36Le Activity in dettaglio.......................................................................................37

I metodi per il cambio degli stati...................................................................37Da starting a running.................................................................................................................37Da running a paused..................................................................................................................37Da paused a running..................................................................................................................37Da paused a stopped..................................................................................................................37Da stopped a running................................................................................................................37Da paused a destroyed...............................................................................................................38Da stopped a destroyed.............................................................................................................38

I View.............................................................................................................39Esempio: utilizzo Activity...............................................................................39Esempio: passaggio informazioni tra Activity................................................42

Salvare e recuperare i dati................................................................................49Salvare e recuperare copie di dati.................................................................49Utilizzare SQLite.............................................................................................50

Animazione 2D..................................................................................................56Nuovo progetto TestGame.............................................................................56

Come prima cosa creiamo un nuovo progetto come segue.......................................................56Nuova superficie di visualizzazione...............................................................57Gestione del ciclo di gioco.............................................................................59Aggiungiamo l'iterazione con lo schermo......................................................62Aggiungiamo un'immagine............................................................................63Animiamo l'immagine....................................................................................65Un'oggetto per lo sprite.................................................................................68Colpire il nemico............................................................................................70Effetti sonori..................................................................................................73

Google Play........................................................................................................76Registrazione sviluppatore............................................................................76Certificare le nostre applicazioni...................................................................76

Creare la nostra chiave privata..................................................................................................77Configurazione del progetto......................................................................................................77Creare la nostra release.............................................................................................................78

4/78

Dott.Ing.Ivan Ferrazzi

INTRODUZIONEQuesto tutorial è stato realizzato per introdurre gli interessati all'interno del mondo di sviluppo per applicazioni Android. Cercheremo di installare un ambiente di lavoro idoneo allo sviluppo con Linux per poi continuare nella realizzazione di un progetto. Come distribuzione abbiamo utilizzato Debian, ma si potrà naturalmente utilizzare anche delle distribuzioni diverse come ad esempio Ubuntu. Cercheremo di evitare l'utilizzo di Eclipse per riuscire ad entrare nel vivo della programmazione e per capire al meglio tutti i passaggi necessari.

Installazione dell'ambiente di lavoro

Per lo sviluppo di applicativi Android ci basiamo sul software di Android per gli sviluppatori (Android Software Development Kit – SDK) che possiamo scaricare al seguente indirizzo

http://developer.android.com/sdk/index.html

Consigliamo di scaricare il file bundle che contiene tutto quello che serve (o quasi) per iniziare a programmare. Il file scaricato è un file compresso (nel nostro caso adt-bundle-linux-x86.zip) che possiamo decomprimere all'interno di una cartella creata per il nostro sistema, ad esempio /home/{user}/android, oppure {SYSTEM_DIR}/Users/{user}/android, dove {user} è il nome dell'utente che stiamo utilizzando.

5/78

Dott.Ing.Ivan Ferrazzi

Configurazione dell'ambiente di lavoro

Come prima cosa aggiungiamo per Linux i seguenti percorsi alla nostra variabile di ambiente PATH

/home/user/android/adt-bundle-linux-x86/sdk/tools/home/user/android/adt-bundle-linux-x86/sdk/platform-tools

Per Windows aggiungiamo alla stessa variabile di ambiente i seguenti percorsi utilizzando la seguente forma

set PATH=%PATH%;{SYSTEM_DIR}\Users\user\android\adt-bundle-windows-x86\sdk\tools;{SYSTEM_DIR}\Users\user\android\adt-bundle-windows-x86\sdk\platform-tools

Per poter compilare i nostri progetti utilizzeremo il programma ant, un compilatore basato su Java, simile al make, che utilizza file XML al posto di file ″Makefile″ (utilizzati da make). Per questo motivo è indispensabile avere sulla propria macchina anche una Java Development Kit (JDK).

Una volta installati su Windows sia ant che il JDK sarà necessario eseguire le seguenti righe dal prompt dei comandi

set ANT_HOME=C:\Users\Ivan\android\antset JAVA_HOME=C:\Program Files\Java\jdk1.7.0_21set PATH=%PATH%;%ANT_HOME%\bin

Inoltre abbiamo bisogno di uno spazio di lavoro (workspace) all'interno del quale andremo a depositare tutti i nostri progetti. Creiamo quindi semplicemente la nostra cartella di lavoro come segue

mkdir /home/user/android/workspace

per quanto riguarda gli ambienti Linux, mentre sotto Windows possiamo creare la cartella

{SYSTEM_DIR}/Users/user/android/workspace

Facciamo ora partire l'ambiente Android SDK Manager con il seguente comando

android update sdk

Questo ci metterà a disposizione le varie versioni di Android disponibile che possiamo installa a piacere.

6/78

Dott.Ing.Ivan Ferrazzi

Concetti base

ActivityUn'attività (activity) equivale ad un pannello contenitore che l'utente può vedere. Un'applicazione è normalmente composta da più activities e l'utente ha la possibilità di passare da un activity all'altra. Se prendiamo come esempio un semplice gioco le activies necessarie saranno: activity per la visualizzazione del menu iniziale, activity per il gioco e activity per l'elenco dei punteggi.Un'applicazione Android è composta da un'attività principale (main activity) ed una serie di attività che possono essere richiamate a piacere. Lo stato in qui si trova un'attività viene gestita dall'Activity Manager di Android. Gli stati possibili sono:

Starting

L'attività viene fatta partire inizialmente caricando il necessario in memoria.

Running

L'utente sta attualmente interagendo con l'attività in questione.Paused

L'utente sta attualmente interagendo con un'altra attività.Stopped

Se l'utente interagisce con un pannello sovrapposto all'attività principale questa viene messa in modalità di pausa anche se in parte visibile sullo schermo. Un'attività viene definita come stopped quando l'utente esce dall'applicazione. L'applicazione non viene rimossa completamente dalla memoria, ma rimane in attesa di un'eventuale riavvio dell'applicazione stessa.

Destroyed

Solo se l'applicazione non viene utilizzata per un determinato periodo di tempo questa verrà completamente rimossa dalla memoria per liberare le risorse occupate.

IntentsUn'intenzione (intent) identifica un'azione che vogliamo svolgere all'interno della nostra applicazione. Mediante intenzione riusciamo a far capire al sistema come effettuare il passaggio da un'attività all'altra.

ServiceUn servizio è simile ad un'attività con l'unica differenza che non contiene componenti grafici in grado di interagire con l'utente, ma viene eseguito in background (es. riproduttore di musica). La vita di un servizio viene gestita dallo sviluppatore piuttosto che dal sistema Android. Non avendo a disposizione un'interfaccia grafica gli stati di un servizio sono di meno. Gli stati possibili sono:

Starting

Il servizio viene fatto partire inizialmente caricando il necessario in memoria.

Running

7/78

Dott.Ing.Ivan Ferrazzi

Il servizio è attualmente in esecuzione.Destroyed

Il servizio viene terminato e non ha più bisogno di esistere.

8/78

Dott.Ing.Ivan Ferrazzi

Un primo progettoVediamo ora come creare un semplice progetto iniziale con il quale potremmo controllare il corretto funzionamento dell'intero ambiente di lavoro.

Creiamo il nostro progetto

Eseguiamo come prima cosa il seguente comando per verificare i così chiamati target, che identificano le versioni di Android attualmente disponibili all'interno del nostro ambiente.

android list targets

Il comando restituisce un risultato simile al seguente:

Available Android targets:id: 1 or ″android-17″

Name: Android 4.2Type: PlatformAPI level: 17Revision: 1Skins: WQVGA432, WVGA800 (default), QVGA, WXGA800-7in, WXGA720,

HVGA, WXGA800, WSVGA, WQVGA400, WVGA854ABIs : armeabi-v7a

In base a queste informazioni creiamo il nostro progetto con il seguente comando sotto Linux

android create project --target {target_id} --name MyFirstApp \ --path /home/user/android/workspace/MyFirstApp \

9/78

Dott.Ing.Ivan Ferrazzi

--activity MainActivity --package com.example.myfirstapp

mentre per Windows modifichiamo il percorso alla cartella base con

android create project --target {target_id} --name MyFirstApp \ --path c:\Users\user\android\workspace\MyFirstApp \ --activity MainActivity --package com.example.myfirstapp

Dove target_id corrisponde all'id (numerico o testuale) presente nel risultato precedente. Nel nostro caso scriviamo quindi su Linux:

android create project --target 1 --name MyFirstApp \ --path /home/user/android/workspace/MyFirstApp \ --activity MainActivity --package com.example.myfirstapp

su Windows

android create project --target 1 --name MyFirstApp \ --path c:\Users\user\android\workspace\MyFirstApp \ --activity MainActivity --package com.example.myfirstapp

Il sistema crea la cartella indicata con path all'interno della quale deporrà una struttura di cartelle e file iniziale in base agli altri parametri forniti. I file inseriti corrispondono al semplice esempio funzionante ″HelloWorld″.

Vediamo qui di seguito alcune delle risorse più importanti:

AndroidManifest.xmlQuesto file contiene le caratteristiche base del nostro progetto, come versione minima e massima supportata.

src/

Questa cartella contiene il sorgente dell'intero progetto. Con il parametro package del comando precedente il sistema crea la struttura a cartelle e sottocartelle conosciuta nel mondo Java. All'interno dell'ultima cartella (quindi myfirstapp) il sistema crea il file .java che rappresenta l'attività iniziale lanciata da Android quando si clicca sull'apposita icona. Il nome del file è definito grazie al parametro activity.

res/

Questa cartelle contiene delle sottocartelle che a loro volta contengono le varie risorse della nostra applicazione.

drawable-*/Contiene file immagine che possono essere utilizzati per dispositivi con risoluzioni (densità di schermo) differenti.

layout/

Contiene i file che definiscono la disposizione dell'interfaccia

10/78

Dott.Ing.Ivan Ferrazzi

grafica per l'utente finale.

values/

Questa cartella può contenere diverse risorse della nostra applicazione come i file XML che definiscono le stringe o i colori da utilizzare.

Compilare il progetto

Ora possiamo provare a compilare l'esempio di default messo a disposizione dopo la creazione del nostro progetto iniziale. Ci spostiamo all'interno della cartella del nostro progetto, quindi in

/home/user/android/workspace/MyFirstApp

ed eseguiamo il seguente comando

ant debug

che ci porterà ad avere il progetto compilato e pronto per Android depositato all'interno della cartella bin del nostro progetto.

Installare l'applicazione

Installazione su dispositivi virtualiSe non siamo in possesso di un dispositivo Android possiamo creare dei dispositivi virtuali che ci permetteranno di emulare le versioni di Android a piacere. Ora possiamo provare a compilare l'esempio di default messo a disposizione dopo la creazione del nostro progetto iniziale.Eseguiamo nuovamente l'ambiente Android SDK Manager con

android update sdk

e apriamo la finestra per la gestione dei dispositivi virtuali dal menu Tools -> Manage AVDs...

Nella scheda Android Virtual Devices presente nella finestra clicchiamo sul pulsane New... per aggiungere un nuovo dispositivo. Confermando dovremo trovare il nuovo dispositivo nell'elenco della finestra. Clicchiamo sul dispositivo per selezionarlo e premiamo sul pulsante Start... per avviare l'emulatore.

A emulatore caricato ci spostiamo nella cartella root del nostro progetto e digitiamo il seguente comando da terminale per installare il nostro piccolo progetto sul dispositivo in questione.

11/78

Dott.Ing.Ivan Ferrazzi

adb install bin/MyFirstApp-debug.apk

Ad installazione terminata troviamo l'applicazione MainActivity tra i programmi installati. Clicchiamoci sopra per avviare ed ammirare la nostra prima applicazione.Per rimpiazzare un'applicazione attualmente presente sul dispositivo usiamo il parametro -r come segue

adb install -r bin/MyFirstApp-debug.apk

Installazione su dispositivi realiSe si possiede un dispositivo reale basta collegare il dispositivo al nostro pc ed attivare la funzione USB debugging. Infine ci spostiamo nella cartella root del nostro progetto e digitiamo il seguente comando da terminale per installare il nostro piccolo progetto sul dispositivo in questione.

adb install bin/MyFirstApp-debug.apk

Ad installazione terminata troviamo l'applicazione MainActivity tra i programmi installati. Clicchiamoci sopra per avviare ed ammirare la nostra prima applicazione direttamente dal nostro dispositivo.In caso di necessità possiamo disinstallare il pacchetto con

adb uninstall com.example.myfirstapp

File presenti all'interno di un progetto

La programmazione per dispositivi Android si basa su dei file XML intuitivi e facili da comprendere. Questo mette a disposizione la possibilità di creare progetti adattabili a dispositivi dalle dimensioni diverse e di gestire più lingue basandosi solamente sulle traduzioni dei testi necessari.local.propertiesQuesto file contiene il riferimento alla cartella che al suo interno ha il ADT utilizzato in fase di creazione del progetto. Nel nostro caso dovremmo trovare la riga

sdk.dir=/home/user/android/adt-bundle-linux-x86/sdk

su sistemi Linux e

sdk.dir={SYSTEM_DIR}\\Users\\user\\android\\adt-bundle-windows-x86\\sdk

su sistemi Windows, dove {SYSTEM_DIR} è la cartella di sistema di Windows. Siccome il backslash (\) ha una funzionalità particolare sui sistemi Windows è indispensabile sostituire ogni backslash con un doppo backslash.

12/78

Dott.Ing.Ivan Ferrazzi

Se trasferiamo un progetto da un computer all'altro dobbiamo verificare la presenza della cartella specificata.project.propertiesQuesto file contiene il target utilizzato per la creazione del nostro progetto. Il contenuto potrebbe essere

target=android-17

L'id del target utilizzato può variare da computer a computer mentre l'identificatore testuale (es. android-17) rimane invariato perché identifica direttamente la versione SDK utilizzata. In base di compilazione ant prende come punto di riferimento la versione SDK presente all'interno di questo file. Per evitare quindi problemi in fase di compilazione dobbiamo scaricare da Internet la versione SDK richiesta, oppure modificare il contenuto di questo file con la versione di SDK installata sul nostro computer.AndroidManifest.xmlQuesto file è il file base all'interno del quale si definisce da cosa è composta l'applicazione, qual'è l'attività principale (quella che viene fatta partire da Android quando clicchiamo sull'apposita icona) e quali permessi sono necessari per l'avvio. Vediamo un semplice esempio iniziale

<?xml version=″1.0″ encoding=″utf-8″?><manifest xmlns:android=″http://schemas.android.com/apk/res/android″ package=″com.example.myfirstapp″ android:versionCode=″1″ android:versionName=″1.0″> <uses-sdk android:minSdkVersion=″8″ android:targetSdkVersion=″17″ /> <application android:label=″@string/app_name″ android:icon=″@drawable/ic_launcher″> <activity android:name=″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>

Nel tag manifest si definiscono le proprietà principali dell'applicazione come il percorso del pacchetto (package), la versione numerica (android:versionCode) ed il nome della versione (android:versionName) dell'applicazione.

Nel tag uses-sdk si definisce la versione più bassa funzionante per l'applicazione (android: minSdkVersion) e quella utilizzata come target base (android:targetSdkVersion). Si consiglia di utilizzare come versione target quella più alta e di effettuare i test su tale versione.

13/78

Dott.Ing.Ivan Ferrazzi

All'interno del tag application troviamo gli attributi android:label e android:icon che identificano il nome dell'applicazione così come l'icona da mostrare all'interno delle applicazioni del nostro Android. Il nome definito mediante questo tag è quello che identifica l'applicazione all'interno della scheda dell'applicazione del sistema Android. Per garantire un determinato grado di flessibilità all'interno di questi attributi non inseriamo dei valori statici ma un identificatore a delle risorse ben precise all'interno della struttura del nostro progetto. L'identificatore ad una risorsa è composto dalle seguenti parti:

@[posizione_risorsa]/[nome_risorsa]

La posizione della risorsa identifica il file XML o la cartella dalla quale recuperare l'effettiva risorsa. La posizione string, ad esempio, recupera la risorsa dal file XML res/values/strings.xml estrapolando il valore del tag string identificato dal nome della risorsa. La posizione drawable invece identifica un file (ad esempio .png) all'interno di una delle cartelle res/drawable-* (ad esempio res/drawable-hdpi) create per rappresentare l'immagine nelle giuste dimensioni su dispositivi diversi.

All'interno del tag application troviamo una serie di tag activity uno dei quali si utilizza per specificare le caratteristiche delle singole attività presenti all'interno della nostra applicazione. Gli attributi principali sono android:name e android:label dove il primo identifica il nome della classe che Android deve far partire come attività principale, mentre il secondo ne identifica il titolo. Per far capire ad Android quale delle attività far partire come prima è indispensabile inserire il blocco

<intent-filter> <action android:name=″android.intent.action.MAIN″ /> <category android:name=″android.intent.category.LAUNCHER″ /></intent-filter>

all'interno dell'attività desiderata. Nel caso in cui il valore di android:name non esistesse l'applicazione verrebbe compilata senza errori, ma non saremmo in grado di avviare la nostra applicazione una volta installata sul nostro Android. Il valore di android:label dell'attività principale è quello utilizzato per il nome che identifica l'intera applicazione. In versioni differenti di Android il valore di android:label presente nel tag application e quello presente nel tag activity dell'attività principale possono essere sostituiti l'uno con l'altro. Si consigli quindi di utilizzare lo stesso valore per ambedue gli attributi.res/values/strings.xmlQuesto file contiene (o dovrebbe contenere) tutti i testi utilizzati all'interno della nostra applicazione. Anche questo è un file XML ed ha la seguente struttura

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

14/78

Dott.Ing.Ivan Ferrazzi

<resources> <string name=″hello″>Hello World!</string> <string name=″app_name″>MainActivity</string></resources>

Il tag root è resources che contiene una serie di tag string identificati dall'attributo name. Il valore lo troviamo tra il tag di apertura e quello di chiusura. Il valore di name è quello che si utilizza come nome della risorsa negli indicatori di risorsa, ad esempio

@string/app_name

res/layout/main.xmlIl file in questione è un file XML che permette di definire il layout da utilizzare per un'attività particolare. In questo caso definiamo il layout dell'attività principale (main). Il nome che utilizziamo per salvare il file in questione non è fondamentale visto che siamo noi a decidere quale file utilizzare all'interno di quale attività con apposito metodo Java (che vedremo più avanti). La struttura base di questo file potrebbe essere come segue

<?xml version=″1.0″ encoding=″utf-8″?><LinearLayout xmlns:android=″http://schemas.android.com/apk/res/android″ android:orientation=″vertical″ android:layout_width=″fill_parent″ android:layout_height=″fill_parent″> <TextView android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:text=″@string/hello″ /></LinearLayout>

Android premette di definire all'interno di questo file dei contenitori (ViewGroup) in grado di contenere degli oggetti allineati in base alla funzionalità del contenitore stesso. Il contenitore definito all'interno del file è LinearLayout che permette di allineare gli oggetti contenuti al suo interno in maniera orizzontale oppure verticale (android:orientation). La larghezza (android:layout_width) e l'altezza (android:layout_height) del contenitore principale dovranno corrispondere con le dimensioni dello schermo messo a disposizione. Ecco perché si utilizza come valore fill_parent per riempire completamente lo spazio presente.Gli oggetti presenti all'interno di un contenitore possono essere altri contenitori oppure degli elementi in grado di visualizzare qualcosa di particolare (View) come pulsanti, testi, campi di input, immagini, ecc. Nel nostro caso abbiamo l'oggetto TextView in grado di visualizzare il testo presente in android:text nelle dimensioni definite grazie agli attributi android:layout_width e android:layout_height. In larghezza utilizziamo tutto lo spazio disponibile, mentre in altezza solo quello strettamente necessario (wrap_content) per la visualizzazione del contenuto in questione.

15/78

Dott.Ing.Ivan Ferrazzi

gen/com/example/myfirstapp/R.javaIl file R.java è un file creato e modificato automaticamente in fase di compilazione. Il contenuto di questo file non verrà quindi mai modificato manualmente, ma è indispensabile comprenderne l'utilizzo per capire meglio come si identifichi una risorsa all'interno della nostra applicazione. Vediamo ora la struttura di questo file

/* AUTO-GENERATED FILE. DO NOT MODIFY.** This class was automatically generated by the* aapt tool from the resource data it found. It* should not be modified by hand.*/

package com.example.myfristapp;

public final class R { public static final class attr { } public static final class drawable { public static final int ic_launcher=0x7f020000; } public static final class layout { public static final int main=0x7f030000; } public static final class string { public static final int app_name=0x7f040001; public static final int hello=0x7f040000; }}

La presenza di questa classe permette di identificare all'interno di Android una qualsiasi risorsa presente in essa. Per recuperare la risorsa che identifica il layout main possiamo scrivere

R.layout.main

L'utilizzo di questa forma lo scopriamo all'interno del prossimo file.

src/com/example/myfirstapp/MainActivity.javaQuesto file contiene l'effettivo codice Java in gradi di far partire la nostra applicazione. Nel nostro caso il file sarà simile al seguente:

package com.example.myfirstapp;

import android.app.Activity;import android.os.Bundle;

public class MainActivity extends Activity {

/** Called when the activity is first called. */ @Override public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

16/78

Dott.Ing.Ivan Ferrazzi

setContentView(R.layout.main);

}}

Il file inizia con l'identificazione della posizione del file mediante comando package ed importa le classi necessarie per il corretto funzionamento del nostro codice con import. La classe android.app.Activity è indispensabile per l'utilizzo delle attività. La classe Activity ha al suo interno i motodi

protected void onCreate(Bundle savedInstanceState);protected void onStart();protected void onRestart();protected void onResume();protected void onPause();protected void onStop();protected void onDestroy();

Il metodo che serve a noi è onCreate(Bundle) che viene fatto partire da Android nel momento in cui lanciamo l'applicazione. Facciamo capire al compilatore l'intenzione di sovrascrivere il contenuto del metodo della superclasse (la classe Activity che estendiamo con il comando extends) con l'annotazione @override.La classe android.os.Bundle è indispensabile per permettere di memorizzare eventuali informazioni prima che l'applicazione venga interrotta da Android per poi riprendere le informazioni ad un prossimo riavvio. Ecco perché troviamo Bundel savedInstanceState all'interno del metodo onCreate().Con la riga

super.onCreate(savedInstanceState);

richiamiamo il metodo in questione della superclasse, mentre la riga

setContentView(R.layout.main);

permette di indicare la risorsa che punta al file XML da utilizzare per il layout desiderato.

17/78

Dott.Ing.Ivan Ferrazzi

I layoutI layout di Android si dividono in layout come Linear Layout e Relative Layout e view come List View e Grid View. Vediamo qui di seguito quando e come poterli utilizzare.

LinearLayout

Questo tipo di layout permette di allineare gli elementi in una direzione, verticale (vertical) o orizzontale (horizontal), utilizzando l'attributo android:orientation. Vediamo un semplice esempio:

<?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:paddingLeft="16dp" android:paddingRight="16dp" android:orientation="vertical" > <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="@string/to" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="@string/subject" /> <EditText android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="top" android:hint="@string/message" /> <Button android:layout_width="100dp" android:layout_height="wrap_content"

18/78

Dott.Ing.Ivan Ferrazzi

android:layout_gravity="right" android:text="@string/send" /></LinearLayout>

RelativeLayout

Questo tipo di ViewGroup ci permette di inserire elementi posizionandoli in relazione agli elementi padre e/o agli elementi fratelli. Gli attributi che possiamo utilizzare per il posizionamento relativo sono:

android:layout_alignParentTopSe attivato (true) l'angolo alto del View in questione corrisponde all'angolo alto del View padre.

android:layout_centerVertical

Se attivato (true) centra il View verticalmente all'interno del View padre.

android:layout_below

Posiziona l'angolo alto del View in questione al di sotto del View specificato dal relativo id di risorsa.

android:layout_toRightOfPosiziona l'angolo sinistro del View in questione a destra del View specificato dal relativo id di risorsa.

Vediamo il seguente esempio:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:paddingLeft="16dp" android:paddingRight="16dp" > <EditText android:id="@+id/name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="@string/reminder" /> <Spinner android:id="@+id/dates" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_below="@id/name" android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/times" /> <Spinner android:id="@id/times" android:layout_width="96dp" android:layout_height="wrap_content" android:layout_below="@id/name" android:layout_alignParentRight="true" /> <Button android:layout_width="96dp" android:layout_height="wrap_content" android:layout_below="@id/times" android:layout_alignParentRight="true" android:text="@string/done" />

19/78

Dott.Ing.Ivan Ferrazzi

</RelativeLayout>

ListView

Questo tipo di ViewGroup è probabilmente l'elemento più utilizzato nel mondo Android, perché è quello in grado di visualizzare dei contenuto sotto forma di elenco. Vediamo nel seguente esempio come visualizzare un'array contenente stringhe all'interno di un elenco.Modifichiamo il file res/layout/main.xml come segue inserendo un ListView

<?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" > <ListView android:id="@+id/listViewDemo" android:layout_width="fill_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>

Inoltre aggiungiamo il nuovo file res/layout/row.xml che contiene il layout da utilizzare per ogni elemento del nostro elenco

<?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:id="@+id/textViewList" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:padding="10dip" android:textSize="22dip"/>

</LinearLayout>

Infine modifichiamo il file principale ListViewLoader.java come segue

package com.example.listviewloader;

import android.app.Activity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.view.View;

20/78

Dott.Ing.Ivan Ferrazzi

public class ListViewLoader extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

ListView listView = (ListView)findViewById(R.id.listViewDemo); String[] array = {"Antonio", "Giovanni", "Michele", "Giuseppe", "Leonardo", "Alessandro"}; ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>( this, R.layout.row, R.id.textViewList, array ); listView.setAdapter(arrayAdapter);

listView.setOnItemClickListener( new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { Toast.makeText(getApplicationContext(), "Position:" + position, Toast.LENGTH_SHORT).show(); } } ); } }

All'interno di questo codice creiamo il ListView dall'id definito nel file xml con

ListView listView = (ListView)findViewById(R.id.listViewDemo);

e l'array delle stringhe che vogliamo aggiungere con

String[] array = {"Antonio", "Giovanni", "Michele", "Giuseppe", "Leonardo", "Alessandro"};

Infine creiamo un ArrayAdapter che aggiungiamo al ListView creato precedentemente

ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>( this, R.layout.row, R.id.textViewList, array ); listView.setAdapter(arrayAdapter);

I parametri necessari per creare un ArrayAdapter sono il Context (nel nostro esempio this), il layout da utilizzare per le singole righe (R.layout.row), l'id dell'elemento che vogliamo riempire all'interno dell'elenco

21/78

Dott.Ing.Ivan Ferrazzi

(R.id.textViewList) e l'array popolato con le stringhe da visualizzare. Siccome ArrayAdapter può essere utilizzato con tipi di dati diversi facciamo capire al compilatore di voler utilizzare le stringhe mettendo l'indicatore <String>.Per controllare su quale degli elementi l'utente ha cliccato aggiungiamo un OnItemClickListener che ci permette di catturare l'evento click sul rispettivo elemento.

listView.setOnItemClickListener( new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { Toast.makeText(getApplicationContext(), "Position:" + position, Toast.LENGTH_SHORT).show(); } } );

Il metodo onItemClick() cattura l'evento ricevendo i seguenti elementi

public void onItemClick(AdapterView<?> parent, View v, int position, long id) { …}

Il parametro parent contiene il ListView di riferimento, v identifica l'elemento View (nel nostro caso TextView) all'interno del LinearLayout, position determina la posizione dell'elemento View all'interno del LinearLayout (partendo da 0), mentre id è l'id della riga di riferimento. Il più delle volte position e id hanno lo stesso valore.Per verificare i parametri inviati abbiamo utilizzato un Toast, una notifica visualizzata sullo schermo del dispositivo come segue

Toast.makeText(getApplicationContext(), "Position:" + position, Toast.LENGTH_SHORT).show();

Il metodo makeText() necessita del Context, della stringa o riferimento ad una stringa (R.string.testo) e della durata di visualizzazione che può essere breve (Toast.LENGTH_SHORT) oppure lungo (Toast.LENGTH_LONG).

GridView

Anche questo fa parte della categoria dei ViewGroup e viene utilizzato per visualizzare una griglia di informazioni. Nel seguente esempio visualizzeremo il contenuto di un array.Modifichiamo il file res/layout/main.xml come segue

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

22/78

Dott.Ing.Ivan Ferrazzi

<GridView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/gridView1" android:numColumns="auto_fit" android:gravity="center" android:columnWidth="50dp" android:stretchMode="columnWidth" android:layout_width="fill_parent" android:layout_height="fill_parent" > </GridView>

Gli attributi che abbiamo utilizzato sono:

android:numColumnsPermette di definire quante colonne visualizzare. Nel nostro caso abbiamo usato auto_fit come valore che permette di inserire tante colonne quanto è lo spazio disponibile.

android:gravityCon questo attributo possiamo definire la disposizione del contenuto della cella. Nel nostro caso usiamo center che permette di centrare il contenuto sia verticalmente che orizzontalmente. E' possibile unire più valori con il pipe “|”. Altri possibili valori possono essere: right, top, left, bottom, center_vertical, fill_vertical, center_horizontal, fill_horizontal, center, fill.

android:columnWidth

Permette di definire la larghezza fissa di ogni colonna.

android:stretchMode

Permette di definire come va riempito l'eventuale spazio vuoto rimanente. Le opzioni possibili sono: none per disattivare l'allungamento delle celle, spacingWidth per allargare lo spazio tra le celle, columnWidth per allungare le colonne in maniera equa, oppure spacingWidthUniform per allungare lo spazio tra le celle in maniera equa.

Il file GridViewActivity.java viene modificato come segue

package com.example.gridviewloader;

import android.app.Activity;import android.os.Bundle;import android.widget.AdapterView;import android.widget.ArrayAdapter;import android.widget.GridView;import android.widget.TextView;import android.widget.Toast;import android.view.View;import android.widget.AdapterView.OnItemClickListener;

public class GridViewActivity extends Activity { GridView gridView; static final String[] letters = new String[] { "A", "B", "C", "D", "E",

23/78

Dott.Ing.Ivan Ferrazzi

"F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); gridView = (GridView) findViewById(R.id.gridView1);

ArrayAdapter<String> adapter = new ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, letters ); gridView.setAdapter(adapter);

gridView.setOnItemClickListener( new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { Toast.makeText(getApplicationContext(), ((TextView) v).getText(), Toast.LENGTH_SHORT).show(); } } ); }}

Creiamo un ArrayAdapter riempiendolo con l'array numbers che contiene una serie di stringe con

ArrayAdapter<String> adapter = new ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, numbers );

Utilizziamo uno standard layout per elenco, ossia

android.R.layout.simple_list_item_1

Poi uniamo il tutto con

gridView.setAdapter(adapter);

Inoltre aggiungiamo un OnItemClickListener() per catturare i click sui vari elementi all'interno del nostro GridView per mostrare poi all'interno di un Toast il relativo elemento.

24/78

Dott.Ing.Ivan Ferrazzi

Oggetti di outputVediamo quali oggetti possiamo utilizzare per mostrare dei dati all'interno della definizione del layout, quindi nel file XML, di un'attività.

TextView

Struttura XMLQuesto oggetto permette di visualizzare del testo su riga singola o multipla. L'oggetto viene aggiunto al file XML con il seguente tag

<TextView android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:gravity=″left″ android:maxLines=″5″ android:singleLine=″false″ android:lines=″5″ android:textSize=″20sp″ android:textStyle=″bold″ android:text=″@string/text″></TextView>

Questo tag ha come attributi una serie di possibilità che variano da versione a versione. Gli attributi mostrati sono quelli più comuni e vengono utilizzati per:

android:gravity Permette di definire l'allineamento del testo.

android:maxLines Il massimo delle righe che l'oggetto può

25/78

Dott.Ing.Ivan Ferrazzi

raggiungere.android:singleLine Permette di attivare (true) o disattivare

(false) la modalità di riga singola.android:lines Indica il numero di righe che l'oggetto

deve occupare come spazio, anche se il testo è più corto.

android:textSize Definisce la dimensione del testo.android:textStyle Permette di definire lo stile del testo

come bold, italic, bolditalic.android:text Indica il testo da visualizzare.

ImageView

Struttura XMLQuesto oggetto permette di visualizzare un'immagine. L'oggetto viene aggiunto al file XML con il seguente tag

<ImageView android:layout_width="80dp" android:layout_height="40dp" android:src="@drawable/left" android:gravity="left" />

Questo tag ha come attributi una serie di possibilità. Noi ne visualizziamo solo alcuni:

android:gravity Permette di definire l'allineamento dell'immagine all'interno del contenitore.

android:src Permette di indicare l'immagine che vogliamo visualizzare.

26/78

Dott.Ing.Ivan Ferrazzi

Oggetti di inputVediamo quali oggetti possiamo utilizzare all'interno della definizione del layout, quindi nel file XML, di un'attività.

EditText

Struttura XMLQuesto oggetto permette l'inserimento di testo come singola riga o come blocco di testo. L'oggetto viene aggiunto al file XML con il seguente tag

<EditText android:id=″@+id/email_address″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:hint=″@string/email_hint″ android:inputType=″textEmailAddress″ />

Siccome il tag in questione rappresenta un oggetto dal quale sarà possibile recuperare le informazioni inserite è indispensabile fornire un identificatore con il quale poter identificare l'oggetto in attività successive. Con l'attributo android:id definiamo il nome da utilizzare come identificativo dell'oggetto stesso. Il simbolo @ identifica (come già visto precedentemente) l'accesso ad una risorsa di Android, mentre la parola id identifica il come utilizzare il valore che viene dopo lo slash (/). In questo caso diciamo ad Android di utilizzare il valore email_address come identificatore, quindi id dell'oggetto che stiamo creando. Il + viene utilizzato quando inizializziamo per la prima volta l'oggetto.L'attributo android:hint permette di definire il testo che viene inserire nel

27/78

Dott.Ing.Ivan Ferrazzi

campo prima di iniziare a scrivere. Nel momento in cui scriviamo questo testo sparisce lasciando solamente il valore inserito.Per definire il tipo di input da gestire o l'eventuale comportamento dell'oggetto stesso durante l'inserimento utilizziamo l'attributo android:inputType. Come input da gestire possiamo utilizzare:

″text″

Inserimento mediante tastiera normale.″textEmailAddress″

Tastiera normale con simbolo @.″textUri″

Tastiera normale con simbolo /.″number″

Tastiera numerica.″phone″

Tastiera numerica come per telefono.

Il comportamento si definisce con i seguenti valori:

″textCapSentences″

Capitalizza la prima lettera di ogni nuova frase.″textCapWords″

Capitalizza la prima lettera di ogni parola.″textAutoCorrect″

Attiva l'autocorrezione.″textPassword″

I caratteri inseriti diventano dei punti.″textMultiLine″

Permette di inserire del testo con interruzione di riga.

Possiamo combinare più caratteristiche all'interno di questo attributo separando le varie parole chiave con il pipe (|) come segue:

<EditText android:id=″@+id/email_address″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:hint=″@string/email_hint″ android:inputType=″text|textPassword″ />

Android ci permette di sostituire il pulsante INVIO presente nella tastiera con un pulsante di azione a scelta. L'attributo che definiamo è android:imeOptions che possiamo utilizzare con i valori actionSend (appare il pulsante invia), actionSearch (appare il pulsante cerca), oppure actionNone (disattiva l'azione). Vediamo un semplice esempio

<EditText android:id=″@+id/email_address″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:hint=″@string/email_hint″ android:inputType=″text|textPassword″

28/78

Dott.Ing.Ivan Ferrazzi

android:imeOptions=″actionSend″ />

Codice JavaL'azione che si ottiene quando si preme sul pulsante definito con android:imeOptions si può monitorare con un determinato listener, ossia una particolare procedura in Java con la quale si rimane in ascolto di eventuali azioni. Il listener che possiamo utilizzare è OnEditorActionListener in grado di monitorare azioni su un TextEdit. Andiamo a modificare i file come segue

res/values/strings.xml

<?xml version=″1.0″ encoding=″utf-8″?><resources> <string name=″hello″>Hello World!</string> <string name=″app_name″>MainActivity</string> <string name=″password_hint″>Enter password</string></resources>

res/layout/main.xml

<?xml version=″1.0″ encoding=″utf-8″?><LinearLayout xmlns:android=″http://schemas.android.com/apk/res/android″ android:orientation=″vertical″ android:layout_width=″fill_parent″ android:layout_height=″fill_parent″> <TextView android:id=″@+id/show_text″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:text=″@string/hello″ /> <EditText android:id=″@+id/password″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:hint=″@string/password_hint″ android:inputType=″text|textPassword″ android:imeOptions=″actionSend″ /></LinearLayout>

/src/com/example/myfirstapp/MainActivity.java

package com.example.myfirstapp;

import android.app.Activity;import android.os.Bundle;import android.widget.EditText;import android.widget.TextView;import android.widget.TextView.OnEditorActionListener;import android.view.KeyEvent;import android.view.inputmethod.EditorInfo;

public class MainActivity extends Activity {

/** Called when the activity is first called. */ @Override public void onCreate(Bundle savedInstanceState) {

29/78

Dott.Ing.Ivan Ferrazzi

super.onCreate(savedInstanceState); setContentView(R.layout.main);

EditText editText = (EditText) findViewById(R.id.search); editText.setOnEditorActionListener( new OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { boolean handled = false; if (actionId == EditorInfo.IME_ACTION_SEND) { TextView textView = (TextView) findViewById(R.id.show_text); textView.setText(v.getText()); handled = true; } return handled; } }

);

}}

Le parti aggiunte nei vari file sono quelle in grassetto. Abbiamo quindi aggiunto una nuova stringa password_hint nel file res/values/strings.xml, aggiunto un id al TextView e l'intero nuovo oggetto EditText nell'apposito file res/layout/main.xml ed infine modificato il file MainActivity.java.Dopo aver importato le classi necessarie abbiamo inserito un nuovo blocco in grado di ascoltare l'azione su EditText per poi copiare il testo inserito all'interno del TextView. All'interno dell'attività abbiamo a disposizione il metodo findViewById() in grado di recuperare un oggetto in base al nome della risorsa, ossia

R.id.[nome_identificativo]

Questo metodo può essere utilizzato solamente dopo che è stato creato il layout dell'attività in questione. Ecco perché è fondamentale utilizzare il metodo solo dopo il setContentView(). Altrimenti si cercherà di attingere ad una risorsa non ancora definita.Aggiungiamo quindi il listener con

editText.setOnEditorActionListener(...);

e ne definiamo il comportamento subito all'interno con

new OnEditorActionListener() {...}

All'interno di quest'ultima andiamo a sovrascrivere il metodo

onEditorAction(TextView, actionId, KeyEvent)

30/78

Dott.Ing.Ivan Ferrazzi

che ci permetterà di recuperare l'oggetto sul quale è avvenuta l'azione (TextView), cosa a scaturito l'evento (actionId) e l'evento del tasto che abbiamo premuto (KeyEvent).Il listener manda in esecuzione un'azione ogni volta che premiamo un tasto della tastiera. Ecco perché dobbiamo utilizzare l'actionId per verificare che il tasto premuto sia proprio quello definito all'interno dell'oggetto in questione. Questa verifica la facciamo con

if(actionId == EditorInfo.IME_ACTION_SEND) { ...}

Infine recuperiamo l'oggetto show_text e gli passiamo il testo (setText) recuperato dall'oggetto TextView passato al metodo onEditorAction con getText().

Button

Struttura XMLUn pulsante è composto da testo, da un'immagine o da entrambi e deve essere in grado di far eseguire un codice quando l'utente ci clicca sopra. Possiamo creare un pulsante utilizzando tre forme diverse. Per creare un pulsante con all'interno del testo scriviamo

<Button android:layout_width=″wrap_content″ android:layout_height=″wrap_content″ android:text=″@string/button_text″ />

Se abbiamo bisogno di un pulsante con all'interno un'immagine e niente testo scriviamo

<ImageButton android:layout_width=″wrap_content″ android:layout_height=″wrap_content″ android:icon=″@drawable/button_icon″ />

Altrimenti possiamo scrivere il seguente tag per incorporare sia immagine che testo

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

Possiamo posizionare l'immagine con android:drawableLeft, android:drawableRight, android:drawableTop, oppure android:drawableBottom.

31/78

Dott.Ing.Ivan Ferrazzi

All'interno di questi tag possiamo definire l'attributo android:onClick che permette di definire il nome del metodo che verrà richiamato passandogli View come parametro quando l'utente clicca sul pulsante stesso. Se definiamo

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

cliccando sul pulsante Android eseguirà il seguente metodo che deve essere presente all'interno dell'attività

public void showText(View view) { ...}

Codice Java E' possibile reagire alla pressione di un pulsante utilizzando un listener al posto del android:onClick come segue

Button button = (Button) findViewById(R.id.button);button.setOnClickListener( new OnClickListener() { public void onClick(View v) { ... } });

Per far funzionare questo codice è indispensabile creare l'oggetto Button con l'id button

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

e di importare le classi necessarie nel file MyActivity.java con

import android.view.View;import android.view.View.OnClickListener;

Checkbox

Struttura XMLI checkbox possono essere inseriti con il tag

32/78

Dott.Ing.Ivan Ferrazzi

<CheckBox android:id=″@+id/checkbox_1″ android:layout_width=″wrap_content″ android:layout_height=″wrap_content″ android:text=″@string/text_checkbox_1″ android:onClick=″onCheckboxClicked″ />

I vari attributi utilizzati dovrebbero essere ormai chiari.Codice JavaLa funzione che possiamo utilizzare per recuperare le informazioni di un checkbox appena cliccato è la seguente

public void onCheckboxClicked(View view) { boolean checked = ((CheckBox) view).isChecked();

if(view.getId() == R.id.checkbox_1) { if(checked) { ... }else{ ... } }}

Nella riga

boolean checked = ((CheckBox) view).isChecked();

convertiamo l'oggetto view nell'oggetto che interessa a noi, quindi Checkbox, per poter poi utilizzare il rispettivo metodo isChecked() che permette di verificare se il checkbox è attivo o meno.Con il metodo getId() dell'oggetto view riusciamo a recuperare l'id in questione che controlliamo nella riga

if(view.getId() == R.id.checkbox_1) { ... }

Nel caso in cui fosse necessario modificare lo stato del checkbox in fase di visualizzazione (quando si mostrano delle impostazioni salvate precedentemente) possiamo utilizzare i metodi setChecked(boolean) oppure toggle().Anche per questa parte di codice è indispensabile importare la seguente classe

import android.widget.CheckBox;import android.view.View;

33/78

Dott.Ing.Ivan Ferrazzi

RadioGroup e RadioButton

Struttura XMLLa struttura di tag che utilizziamo per definire dei radiobuttons che appartengono allo stesso gruppo è la seguente:

<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RadioButton android:id="@+id/radio_maschio" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/maschio" android:onClick="onRadioButtonClicked" /> <RadioButton android:id="@+id/radio_femmina" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/femmina" android:onClick="onRadioButtonClicked" /></RadioGroup>

Codice JavaLa funzione che possiamo utilizzare per recuperare le informazioni di un radiobotton appena cliccato è la seguente

public void onRadioButtonClicked(View view) { boolean checked = ((RadioButton) view).isChecked();

if(view.getId() == R.id.maschio) { if(checked) { ... } }}

Anche per questa parte di codice è indispensabile importare la seguente classe

import android.widget.RadioButton;import android.view.View;

ToggleButton

Struttura XMLI ToggleBotton sono degli interruttori che possono essere accesi o spenti cliccandoci sopra. Il tag che utilizziamo per definire questo oggetto è

<ToggleButton android:id=″@+id/togglebutton″ android:layout_width=″wrap_content″ android:layout_height=″wrap_content″

34/78

Dott.Ing.Ivan Ferrazzi

android:textOn=″@string/text_on″ android:textOff=″@string/text_off″ android:onClick=″onToggleClicked″ />

Gli attributi android:textOn e android:textOff permettono di definire il testo visualizzato quando il pulsante è acceso e quando è spento.Codice JavaLa funzione che possiamo utilizzare per recuperare le informazioni di un togglebutton appena cliccato è la seguente

public void onToggleClicked(View view) { boolean on = ((ToggleButton) view).isChecked();

if(on) { ... }else{ ... }}

Anche per questa parte di codice è indispensabile importare le seguenti classi

import android.widget.ToggleButton;import android.view.View;

Spinner

Struttura XMLGli Spinner sono dei menu che mettono a disposizione le varie opzioni all'interno di un pannello con gli elementi elencati dall'alto verso il basso. Il tag che utilizziamo per definire questo oggetto è

<Spinner android:id=″@+id/spinner″ android:layout_width=″wrap_content″ android:layout_height=″wrap_content″ />

Per popolare lo Spinner possiamo creare un file risorsa xml che salveremo nella cartella res/values e che possiamo chiamare spinner.xml. Avremo quindi il seguente file res/values/spinner.xml

<?xml version=”1.0” encoding=”utf-8”><resources> <string-array name=”pianeti”> <item>Mercurio</item> <item>Venere</item> <item>Terra</item> <item>Marte</item> </string-array></resources>

35/78

Dott.Ing.Ivan Ferrazzi

Gli elementi di questo file vanno importati direttamente dal codice Java.Codice JavaCome prima cosa dobbiamo importare gli elementi dal file res/values/spinner.xml. All'interno del metodo onCreate() inseriamo quindi le seguenti righe:

Spinner spinner = (Spinner)findViewById(R.id.spinner);ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.pianeti, android.R.layout.simple_spinner_item);adapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item);spinner.setAdapter(adapter);

Il contenuto del file xml viene estratto ed inserito all'interno di un ArrayAdapter. L'estrazione avviene tramite ArrayAdapter.crerateFromResource() al quale passiamo il Context (this), la risorsa relativa all'array che interessa a noi (R.array.pianeti) e una risorsa di default di Android da utilizzare per la visualizzazione del nostro Spinner (android.R.layout.simple_spinner_item). Aggiungiamo anche il layout da utilizzare come Drop/Down con

adapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item);

per poi assemblare il tutto con la riga

spinner.setAdapter(adapter);

Per poter recuperare l'evento onClick aggiungiamo il seguente codice

spinner.setOnItemSelectedListener( new OnItemSelectedListener() {

public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { … } } }

Anche per questa parte di codice è indispensabile importare le seguenti classi

import android.widget.Spinner;import android.widget.ArrayAdapter;import android.view.View;

36/78

Dott.Ing.Ivan Ferrazzi

Le Activity in dettaglioLe Activity, come descritto precedentemente, vengono gestite dall'Activity Manager di Android che ne stabilisce lo, ossia starting, running, paused, stopped, oppure destroyed. Il passaggio da uno stato all'altro ha come conseguenza l'esecuzione di determinati metodi presenti all'interno dell'Activity in questione.

I metodi per il cambio degli stati

Quando passiamo da uno stato all'altro di un'Activity il sistema esegue in sequenza una serie di metodi. Vediamo qui di seguito quali sono.Da starting a running(1) onCreate(Bundle)(2) onStart()

(3) onRestoreInstanceState(Bundle) (optionale)(4) onResume()Da running a paused(1) onSaveInstanceState(Bundle) (optionale)(2) onPause()

Da paused a running(1) onResume()

Da paused a stopped(1) onSaveInstanceState(Bundle) (optionale)(2) onStop()Da stopped a running(1) onRestart()(2) onStart()

(3) onResume()

37/78

Dott.Ing.Ivan Ferrazzi

Da paused a destroyed(1) il processo viene distruttoDa stopped a destroyed(1) onDestroy() oppure il processo viene distrutto

Vediamo una breve descrizione dei vari metodi utilizzati:

onCreate(Bundle) Viene richiamata quanto l'Activity viene avviata per la prima volta. Il parametro passato è nullo nel caso del primo avviamo, altrimenti può contenere le informazioni salvate precedentemente con onSaveInstanceState().

OnStart() L'Activity sta per essere visualizzata.OnResume() Nel momento in cui l'Activity inizia ad

interagire con l'utente viene richiamata questo metodo. Elementi multimediali (musica, ecc.) vengono normalmente gestiti all'interno di questo metodo.

OnPause() Questo metodo viene richiamato quando l'attuale Activity sta per essere messa in background da un'altra Activity. Eventuali dati persistenti vanno salvati all'interno di questo metodo.

OnStop() Quando l'Activity non viene più utilizzata dall'utente l'Activity Manager richiama questo metodo se la memoria è sufficiente, altrimenti il processo verrà completamente distrutto.

OnRestart() Quando l'Activity sta per essere ridisegnata viene effettuato un richiamo a questo metodo.

OnDestroy() Questo metodo viene richiamato prima che l'Activity venga completamente distrutta.

OnSaveInstanceState(Bundle) Android utilizza questo metodo per memorizzare tutte le informazioni relative all'attuale Activity. Questo metodo va sovrascritto nel caso in cui si debba salvare delle informazioni aggiuntive.

onRestoreInstanceState(Bundle) Questo metodo viene richiamato solamente se precedentemente sono state salvate delle informazioni con onSaveInstanceState().

38/78

Dott.Ing.Ivan Ferrazzi

I View

Come già definito un'Activity non è altro che un contenitore che può contenere determinati elementi. Eventuali elementi grafici non vengono inseriti direttamente all'interno dell'Activity, ma all'interno di View che possono essere disposti singolarmente, oppure all'interno di layout (ViewGroup) per visualizzazioni più complicate e composte.Una struttura View e/o ViewGroup può essere simile alla seguente:

Come abbiamo visto precedentemente un layout composto da View e/o ViewGroup può essere definito tramite file XML (ad esempio con il file res/layout/main.xml) oppure direttamente da codice.

Esempio: utilizzo Activity

Nel seguente esercizio cercheremo di partire dall'esempio base HelloWorld e lo modificheremo aggiungendo un pulsante che ci permetterà di passare ad un'altra Activity.Come prima cosa creiamo il nuovo progetto e lo chiamiamo ActivityTest come segue. Nel nostro caso scriviamo quindi su Linux:

android create project --target 1 --name ActivityTest \ --path /home/user/android/workspace/ActivityTest \ --activity ActivityTest --package com.example.activitytest

su Windows

android create project --target 1 --name ActivityTest \ --path c:\Users\user\android\workspace\ActivityTest \ --activity ActivityTest --package com.example.activitytest

Poi andiamo a modificare il layout res/layout/main.xml per aggiungere il pulsante che ci permetterà di passare alla seconda Activity. Il nuovo codice sarà il seguente:

39/78

Dott.Ing.Ivan Ferrazzi

<?xml version=″1.0″ encoding=″utf-8″?><LinearLayout xmlns:android=″http://schemas.android.com/apk/res/android″ android:orientation=″vertical″ android:layout_width=″fill_parent″ android:layout_height=″fill_parent″> <TextView android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:text=″@string/hello″ />

<Button android:layout_height=″wrap_content″ android:text=″@string/bnt_start_second_activity_text″ android:layout_width=″fill_parent″ android:id=″@+id/bntStartSecondActivity″></Button></LinearLayout>

Diamo ora un'occhiata al file principale del nostro esempio, ossia src/com/example/activitytest/ActivityTest.java.

package com.example.activitytest;

import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.Button;

public class ActivityTest extends Activity {

/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState); setContentView(R.layout.main);

Button bnt = (Button)findViewById(R.id.bntStartSecondActivity); bnt.setOnClickListener( new View.OnClickListener() { public void onClick(View view) { //da qui avviamo la seconda activity } } ); }}

Con la riga

Button bnt = (Button)findViewById(R.id.bntStartSecondActivity);

andiamo alla ricerca dell'oggetto che interessa a noi e ne creiamo il relativo oggetto. Poi aggiungiamo il listener in grado di rimanere in ascolto sull'oggetto stesso per captare ogni attività relativa al click su di esso.

bnt.setOnClickListener(...);

40/78

Dott.Ing.Ivan Ferrazzi

Il pulsante fa a sua volta parte di un View che Android passerà alla relativa funzione nel momento in cui si cliccherà sul pulsante. Ecco che dobbiamo creare un nuovo OnClickListener all'interno del quale si trova il metodo onClick() che serve a noi

new View.OnClickListener() { public void onClick(View view) { //da qui avviamo la seconda activity }}

Il metodo onClick() lo modifichiamo come segue per riuscire ad aprire la nuova Activity:

public void onClick(View view) { Intent i = new Intent(view.getContext(), SecondActivity.class); view.getContext().startActivity(i);}

Siccome utilizziamo la classe Intent è indispensabile importarne il codice con

import android.content.Intent;

In questo caso aggiungiamo l'intenzione (Intent – descritto precedentemente) di aprire una nuova Activity.Ora dobbiamo creare la nuova Activity con il relativo file di layout. Creiamo quindi il file src/com/example/activitytest/SecondActivity.java e associamo ad esso il layout res/layout/second.xml come segue:

package com.example.activitytest;

import android.app.Activity;import android.os.Bundle;

public class SecondActivity extends Activity {

/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState); setContentView(R.layout.second);

}}

Il contenuto del layout res/layout/second.xml sarà il seguente:

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

41/78

Dott.Ing.Ivan Ferrazzi

android:layout_width=″fill_parent″ android:layout_height=″fill_parent″> <TextView android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:text=″@string/hello″ /></LinearLayout>

Dobbiamo ricordarci, inoltre, di verificare il contenuto del file res/values/strings.xml che deve avere al suo interno tutti i riferimenti di stringa utilizzati:

<?xml version=″1.0″ encoding=″utf-8″?><resources> <string name=″hello″>Hello World!</string> <string name=″app_name″>ActivityTest</string> <string name=″bnt_start_second_activity_text″>Apri Activity</string></resources>

E come ultima cosa, ma sicuramente non meno importante, è la registrazione della nuova Activity all'interno del file AndroidManifest.xml, altrimenti Android non riconoscerà l'Activity che vogliamo aprire.

<?xml version=″1.0″ encoding=″utf-8″?><manifest xmlns:android=″http://schemas.android.com/apk/res/android″ package=″com.example.activitytest″ android:versionCode=″1″ android:versionName=″1.0″> <application android:label=″@string/app_name″> <activity android:name=″ActivityTest″ 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″ android:label=″@string/app_name″> </activity> </application></manifest>

Facendo partire il programma Android avvierà la prima Activity dalla quale possiamo passare alla seconda. Premendo il tasto per tornare indietro ritorniamo all'Activity precedente.

Esempio: passaggio informazioni tra Activity

Cerchiamo ora di ampliare il precedente esempio come segue: aggiungiamo al layout della prima Activity due campi di input all'interno dei quali andremo ad inserire due numeri che passeremo alla seconda Activity. La seconda calcolerà la somma dei due numeri tramite un pulsante ″calcola″ che terminerà l'attività stessa e ritornerà il risultato

42/78

Dott.Ing.Ivan Ferrazzi

alla prima.Come prima cosa modifichiamo i file main.xml e second.xml come segue. Il file main.xml diventerà:

<?xml version=″1.0″ encoding=″utf-8″?><LinearLayout xmlns:android=″http://schemas.android.com/apk/res/android″ android:orientation=″vertical″ android:layout_width=″fill_parent″ android:layout_height=″fill_parent″> <TextView android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:text=″@string/hello″ />

<LinearLayout xmlns:android=″http://schemas.android.com/apk/res/android″ android:orientation=″horizontal″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″> <TextView android:layout_width=″wrap_content″ android:layout_height=″wrap_content″ android:text=″@string/value_1″ /> <EditText android:id=″@+id/EditText01″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:inputType=″number″ /> </LinearLayout>

<LinearLayout xmlns:android=″http://schemas.android.com/apk/res/android″ android:orientation=″horizontal″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″> <TextView android:layout_width=″wrap_content″ android:layout_height=″wrap_content″ android:text=″@string/value_2″ /> <EditText android:id=″@+id/EditText02″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ android:inputType=″number″ /> </LinearLayout>

<Button android:layout_height=″wrap_content″ android:text=″@string/bnt_start_second_activity_text″ android:layout_width=″fill_parent″ android:id=″@+id/bntStartSecondActivity″></Button>

<TextView android:id=″@+id/TextView03″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ /></LinearLayout>

mentre second.xml diventerà:

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

43/78

Dott.Ing.Ivan Ferrazzi

android:text=″@string/hello_second″ />

<TextView android:id=″@+id/TextView01″ android:layout_width=″fill_parent″ android:layout_height=″wrap_content″ />

<Button android:layout_height=″wrap_content″ android:text=″@string/bnt_calculate″ android:layout_width=″fill_parent″ android:id=″@+id/bntCalculate″></Button></LinearLayout>

Attenzione! Va modificato di conseguenza anche il file strings.xml come segue:

<?xml version=″1.0″ encoding=″utf-8″?><resources> <string name=″hello″>Hello World!</string> <string name=″app_name″>MainActivity</string> <string name=″value_1″>Valore 1</string> <string name=″value_2″>Valore 2</string> <string name=″bnt_start_second_activity_text″>Passa i valori</string> <string name=″hello_second″>Seconda Activity!</string> <string name=″bnt_calculate″>Calcola</string></resources>

Inoltre dobbiamo modificare i file ActivityTest.java e SecondActivity.java come segue:

package com.android.activitytest;

import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;

public class ActivityTest extends Activity implements OnClickListener {

public static String VALORE_1 = ″VALORE_1″; public static String VALORE_2 = ″VALORE_2″; public static final int SOMMA = 10;

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

Button bnt = (Button)findViewById(R.id.bntStartSecondActivity); bnt.setOnClickListener(this); }

@Override

44/78

Dott.Ing.Ivan Ferrazzi

public void onClick(View v) { // creo l’intent per richiamare la seconda activity Intent i = new Intent(this, SecondActivity.class);

// recupero i valori impostati nei 2 field EditText etxt1 = (EditText)findViewById(R.id.EditText01); EditText etxt2 = (EditText)findViewById(R.id.EditText02); String val1str = etxt1.getText().toString(); String val2str = etxt2.getText().toString(); int val1 = Integer.parseInt(val1str); int val2 = Integer.parseInt(val2str); i.putExtra(VALORE_1, val1); i.putExtra(VALORE_2, val2);

// avvio la seconda activity startActivityForResult(i, SOMMA); }

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // TODO Auto-generated method stub super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_OK) { Bundle extras = data.getExtras(); int risultato = extras.getInt(SecondActivity.RISULTATO); if (requestCode == SOMMA) { TextView t = (TextView)this.findViewById(R.id.TextView03); t.setText(″risultato = ″+ risultato); } } }}

il secondo file diventa

package com.android.activitytest;

import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;

public class SecondActivity extends Activity implements OnClickListener{

public static String RISULTATO = ″RISULTATO″; private int val1; private int val2;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.second);

Button bnt = (Button)findViewById(R.id.bntCalculate);

45/78

Dott.Ing.Ivan Ferrazzi

bnt.setOnClickListener(this);

TextView t = (TextView)findViewById(R.id.TextView01);

Bundle extras = getIntent().getExtras(); val1 = extras.getInt(ActivityTest.VALORE_1); val2 = extras.getInt(ActivityTest.VALORE_2); t.setText(″Ho ricevuto i valori ″+val1+″ e ″+val2); }

@Override public void onClick(View v) { int risultato = val1 + val2; Bundle bundle = new Bundle(); bundle.putInt(RISULTATO, risultato);

Intent mIntent = new Intent(); mIntent.putExtras(bundle); setResult(RESULT_OK, mIntent);

finish(); }}

Cerchiamo di analizzare i due codici partendo dal primo. La prima cosa che notiamo (così come anche nel secondo file) è l'utilizzo di implements

implements OnClickListener

che permette di derivare il codice a partire dalla struttura di un'interfaccia, oppure di una classe, per sovrascriverne i metodi. Nel nostro caso implementiamo la classe OnClickListener in maniera da poter gestire il metodo onClick direttamente all'interno del proprio codice.Poi troviamo la dichiarazione delle seguenti variabili

public static String VALORE_1 = ″VALORE_1″;public static String VALORE_2 = ″VALORE_2″;

public static final int SOMMA = 10;

che utilizziamo all'interno della classe. La parola chiave public fa sì che la variabile diventi pubblica, mentre lo static indica la possibilità di avere una sola variabile con questo valore. Nel caso in cui venisse ricreata una nuova istanza della classe queste variabili non verrebbero inizializzate nuovamente. Con la parola final possiamo definire delle costanti, ossia delle variabili che non potranno modificare il valore durante l'esecuzione dell'intera classe.All'interno del metodo onClick troviamo il necessario per passare le variabili alla prossima Activity grazie all'intento

Intent i = new Intent(this, SecondActivity.class);i.putExtra(VALORE_1, val1);i.putExtra(VALORE_2, val2);startActivityForResult(i, SOMMA);

46/78

Dott.Ing.Ivan Ferrazzi

Con le righe

i.putExtra(nome_etichetta,valore);

possiamo definire le informazioni che vogliamo passare, mentre con

startActivityForResult(Intent, codice_riconoscimento);

facciamo partire l'Activity in attesa di un determinato risultato (indicato dal codice di riconoscimento).Infine creiamo il metodo onActivityResult() che viene richiamata con il codice di riconoscimento inviato con startActivityForResult() (requestCode), il codice di riconoscimento per il risultato (resultCode) e l'oggetto con le varie informazioni (data).

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { ...}

La prima cosa che facciamo all'interno di questo metodo è passare i vari dati al processo base con

super.onActivityResult(requestCode, resultCode, data);

Poi controlliamo se il codice del risultato è quello che ci aspettiamo con

if (resultCode == RESULT_OK) { ...}

recuperiamo il risultato recuperato con

Bundle extras = data.getExtras();int risultato = extras.getInt(SecondActivity.RISULTATO);

dove getInt() viene utilizzato per recuperare un valore intero, ed infine, controlliamo che il codice di richiesta sia quello nostro con

if (requestCode == SOMMA) { ...}

Nel secondo file la parte più interessante si trova all'interno del metodo onClick() ed è la seguente:

int risultato = val1 + val2;Bundle bundle = new Bundle();bundle.putInt(RISULTATO, risultato);

Intent mIntent = new Intent();

47/78

Dott.Ing.Ivan Ferrazzi

mIntent.putExtras(bundle);setResult(RESULT_OK, mIntent);

Come prima cosa recuperiamo il risultato che vogliamo restituire con

int risultato = val1 + val2;

creiamo il Bundle all'interno del quale depositiamo il risultato con

bundle.putInt(RISULTATO, risultato);

Il Bundle viene poi integrato all'interno di un nuovo Intent con

mIntent.putExtras(bundle);

e restituito come risultato definendo un'appropriato codice di riconoscimento RESULT_OK con

setResult(RESULT_OK, mIntent);

48/78

Dott.Ing.Ivan Ferrazzi

Salvare e recuperare i datiIn questo capitolo andremo ad analizzare una delle possibilità che Android mette a disposizione per salvare i dati e mantenerli anche al prossimo riavvio della nostra applicazione.

Salvare e recuperare copie di dati

Il metodo che abbiamo a disposizione è il salvataggio di copie di dati. I dati vengono salvati all'interno di un file gestito da Android con un nome che li identifica. All'interno di un'Activity possiamo scrivere come segue

SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putString("COGNOME", "Ferrazzi"); editor.commit();

Come prima cosa creiamo un SharedPreferences in modalità privata (Context.MODE_PRIVATE), questo vuol dire che solamente l'applicazione può accedere al contenuto dei dati creati

SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE);

Poi creiamo un editor con

SharedPreferences.Editor editor = sharedPref.edit();

che ci permette di scrivere i dati. Nel nostro caso registriamo il valore “Ferrazzi” con l'etichetta “COGNOME” con

49/78

Dott.Ing.Ivan Ferrazzi

editor.putString("COGNOME", "Ferrazzi");

per poi scrivere i dati con

editor.commit();

Per leggere dati salvati precedentemente scriviamo sempre all'interno di un'Activity

SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE); String COGNOME = sharedPref.getString("COGNOME","");

Partendo sempre dallo stesso SharedPreferences utilizziamo

String valore = getString(“{etichetta}”,”{valore_di_default}”);

per recuperare una stringa, opure

int valore = getInt(“{etichetta}”,{valore_di_default});

Il {valore_di_default} è il valore che viene assegnato nel caso in qui non si riesca a trovare l'etichetta richiesta.Per far funzionare le nuove classi aggiunte bisogna ricordarsi di importare le seguenti risorse

import android.content.SharedPreferences; import android.content.Context;

Utilizzare SQLite

(…) da fareClasse per la configurazione del db

public class MyDatabase { SQLiteDatabase mDb; DbHelper mDbHelper; Context mContext; private static final String DB_NAME="tutorialdb";//nome del db private static final int DB_VERSION=1; //numero di versione del nostro db public MyDatabase(Context ctx) { mContext=ctx; //quando istanziamo questa classe, istanziamo anche l'helper (vedi sotto) mDbHelper=new DbHelper(ctx, DB_NAME, null, DB_VERSION); } public void open() { //il database su cui agiamo è leggibile/scrivibile

50/78

Dott.Ing.Ivan Ferrazzi

mDb=mDbHelper.getWritableDatabase(); }

public void close(){ //chiudiamo il database su cui agiamo mDb.close(); }

//i seguenti 2 metodi servono per la lettura/scrittura del db. aggiungete e modificate a discrezione // consiglio:si potrebbe creare una classe Prodotto, i quali oggetti verrebbero passati come parametri dei seguenti metodi, rispettivamente ritornati. Lacio a voi il divertimento

public void insertProduct(String name,int price){ //metodo per inserire i dati ContentValues cv=new ContentValues(); cv.put(ProductsMetaData.PRODUCT_NAME_KEY, name); cv.put(ProductsMetaData.PRODUCT_PRICE_KEY, price); mDb.insert(ProductsMetaData.PRODUCTS_TABLE, null, cv); }

public Cursor fetchProducts(){ //metodo per fare la query di tutti i dati return mDb.query(ProductsMetaData.PRODUCTS_TABLE, null,null,null,null,null,null); }

static class ProductsMetaData { // i metadati della tabella, accessibili ovunque static final String PRODUCTS_TABLE = "products"; static final String ID = "_id"; static final String PRODUCT_NAME_KEY = "name"; static final String PRODUCT_PRICE_KEY = "price"; }

private static final String PRODUCTS_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS " //codice sql di creazione della tabella + ProductsMetaData.PRODUCTS_TABLE + " (" + ProductsMetaData.ID+ " integer primary key autoincrement, " + ProductsMetaData.PRODUCT_NAME_KEY + " text not null, " + ProductsMetaData.PRODUCT_PRICE_KEY + " integer not null);";

private class DbHelper extends SQLiteOpenHelper { //classe che ci aiuta nella creazione del db

public DbHelper(Context context, String name, CursorFactory factory,int version) { super(context, name, factory, version); }

@Override public void onCreate(SQLiteDatabase _db) { //solo quando il db viene creato, creiamo la tabella _db.execSQL(PRODUCTS_TABLE_CREATE); }

@Override public void onUpgrade(SQLiteDatabase _db, int oldVersion, int newVersion) {

51/78

Dott.Ing.Ivan Ferrazzi

//qui mettiamo eventuali modifiche al db, se nella nostra nuova versione della app, il db cambia numero di versione }

}}

Codice dell'activity

public class Demo extends Activity {

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

TextView productsTv=(TextView)findViewById(R.id.productsTv); productsTv.setTypeface(Typeface.createFromAsset(getAssets(),"kberry.ttf")); //caricamento di un font esterno, piazzato nella cartella assets ListView productsLv=(ListView)findViewById(R.id.productsLv); MyDatabase db=new MyDatabase(getApplicationContext()); db.open(); //apriamo il db

if(db.fetchProducts().getCount()==0){//inserimento dati, solo se il db è vuoto db.insertProduct("Telefono", 400); db.insertProduct("Scarpe", 100); db.insertProduct("PC", 500); db.insertProduct("Pane", 2); db.insertProduct("Patente guida", 100); //lol }

Cursor c=db.fetchProducts(); // query startManagingCursor(c);

SimpleCursorAdapter adapter=new SimpleCursorAdapter( //semplice adapter per i cursor this, R.layout.product, //il layout di ogni riga/prodotto c, new String[] { MyDatabase.ProductsMetaData.PRODUCT_NAME_KEY, MyDatabase.ProductsMetaData.PRODUCT_PRICE_KEY },//questi colonne new int[]{R.id.nameTv,R.id.priceTv});//in queste views productsLv.setAdapter(adapter); //la listview ha questo adapter

//qui vediamo invece come reperire i dati e usarli, in questo caso li stampiamo in una textview int nameCol = c.getColumnIndex(MyDatabase.ProductsMetaData.PRODUCT_NAME_KEY); //indici delle colonne int priceCol = c.getColumnIndex(MyDatabase.ProductsMetaData.PRODUCT_PRICE_KEY);

52/78

Dott.Ing.Ivan Ferrazzi

if(c.moveToFirst()){ //se va alla prima entry, il cursore non è vuoto do { productsTv.append("Product Name:"+c.getString(nameCol)+", Price:"+c.getInt(priceCol)+"\n"); //estrazione dei dati dalla entry del cursor } while (c.moveToNext());//iteriamo al prossimo elemento } db.close();

getWindow().setFormat(PixelFormat.RGBA_8888); //visto che usiamo i gradient, usiamo questo trick (vedi snippet forum) getWindow().addFlags(WindowManager.LayoutParams.FLAG_DITHER);

productsLv.setBackgroundDrawable( new GradientDrawable( GradientDrawable.Orientation.BOTTOM_TOP, new int[] {Color.RED,Color.parseColor("#f2bf26")} ) ); productsTv.setBackgroundDrawable( new GradientDrawable( GradientDrawable.Orientation.TOP_BOTTOM, new int[] {Color.RED,Color.parseColor("#f2bf26")} ) ); //definizione ed uso di gradient in modo programmatico

//animazioni in modo programmatico (vedi snippet forum) Animation a1 = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_PARENT, 1.0f, Animation.RELATIVE_TO_SELF, 0.0f ); a1.setDuration(1000); a1.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.decelerate_interpolator)); productsLv.startAnimation(a1); //entra da sotto Animation a2 = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_PARENT, -1.0f, Animation.RELATIVE_TO_SELF, 0.0f ); a2.setDuration(1000); a2.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.decelerate_interpolator)); productsTv.startAnimation(a2); //entra da sopra }}

Codice main.xml

53/78

Dott.Ing.Ivan Ferrazzi

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

<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/productsTv" android:textColor="#000000" android:textSize="25dp" android:textStyle="bold" />

<ListView android:id="@+id/productsLv" android:layout_width="fill_parent" android:layout_height="fill_parent" android:cacheColorHint="#00000000" android:dividerHeight="2dp" android:divider="@drawable/divider" ></ListView></LinearLayout>

Layout degli elementi della ListView

<?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="horizontal" android:weightSum="10">

<TextView android:id="@+id/nameTv" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="8" android:textSize="15dp" android:textStyle="bold" android:textColor="#000000"> </TextView> <TextView android:id="@+id/priceTv" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:textSize="15dp" android:textStyle="bold" android:textColor="#000000"> </TextView></LinearLayout>

Gradiente utilizzato come separatore tra i vari elementi

54/78

Dott.Ing.Ivan Ferrazzi

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">

<gradient android:startColor="#FFFF0000" android:centerColor="#FF000000" android:endColor="#FFFF0000" android:angle="0" />

</shape>

55/78

Dott.Ing.Ivan Ferrazzi

Animazione 2DIn questo capitolo analizzeremo le parti necessarie alla programmazione di animazioni 2D.

Nuovo progetto TestGame

Come prima cosa creiamo un nuovo progetto come segue

android create project --target 1 --name TestGame \ --path /home/user/android/workspace/TestGame \ --activity TestGameActivity --package com.example

All'interno del nostro spazio di lavoro troviamo la nuova cartella TestGame con la struttura base per il sistema Android. Il file TestGameActivity.java presente all'interno della cartella TestGame/src/com/example dovrebbe avere il seguente contenuto:

package com.example;

import android.app.Activity; import android.os.Bundle;

public class TestGameActivity extends Activity {

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

56/78

Dott.Ing.Ivan Ferrazzi

}

Come prima cosa dobbiamo creare un nuovo pannello view da utilizzare con setContentView().

Nuova superficie di visualizzazione

Quando pensiamo ad un gioco la superficie che viene utilizzata per visualizzare il contenuto deve eseguire delle operazioni in maniera ciclica. Le tre fasi che si hanno sono: recuperare l'azione di input (ad esempio quando si tocca il touch screen), ricalcolare il dovuto aggiornamento della grafica e presentare il tutto all'interno della superficie di visualizzazione.La via più semplice per ottenere una superficie che fa al caso nostro è quella di estendere l'oggetto presente all'interno dell'ambiente Android SurfaceView come segue:

package com.example;

import android.app.Activity; import android.content.Context; import android.graphics.Canvas;import android.view.SurfaceHolder; import android.view.SurfaceView;import android.view.MotionEvent; public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {

public MainGamePanel(Context context) { super(context); getHolder().addCallback(this); setFocusable(true); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }

57/78

Dott.Ing.Ivan Ferrazzi

@Override protected void onDraw(Canvas canvas) { }}

Salviamo il file con il nome MainGamePanel.java nella cartella TestGame/src/. All'interno del costruttore della classe troviamo la riga

getHolder().addCallback(this);

con la quale aggiungiamo lo stesso oggetto della superficie (this) come gestore degli eventi che avvengono sulla propria superficie. Per poter gestire i vari eventi è indispensabile attivare la possibilità di focus sulla superficie che si ottiene con la riga

setFocusable(true);

Inoltre sovrascriviamo i metodi nativi dell'oggetto

public void surfaceChanged();public void surfaceCreated();public void surfaceDestroyed();public boolean onTouchEvent();protected void onDraw();

lasciandoli attualmente vuoti (tranne onTouchEvent che restituisce l'evento generato dall'oggetto padre – super).Creata la nuova superficie di visualizzazione dobbiamo integrarla all'interno del file TestGameActivity.java

package com.example;

import android.app.Activity; import android.os.Bundle;import android.view.Window; import android.view.WindowManager;

public class TestGameActivity extends Activity {

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

requestWindowFeature(Window.FEATURE_NO_TITLE);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

setContentView(new MainGamePanel(this)); }

@Override protected void onDestroy() { super.onDestroy();

58/78

Dott.Ing.Ivan Ferrazzi

}

@Override protected void onStop() { super.onStop(); }

}

Dopo aver importato le dovute classi definiamo le proprietà dello schermo che dovrà presentarsi senza barra dei titoli

requestWindowFeature(Window.FEATURE_NO_TITLE);

e dovrà essere visualizzato a schermo intero

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

Infine aggiungiamo all'interno dello schermo creato la superficie di visualizzazione MainGamePanel con

setContentView(new MainGamePanel(this));

Inoltre inseriamo anche i metodi onDestroy() e onStop().

Gestione del ciclo di gioco

Arrivati a questo punto possiamo creare la classe che gestirà il ciclo di gioco. L'oggetto che estendiamo per realizzare quanto server è il thread sovrascrivendo il metodo run come visualizzato qui di seguito. Creiamo quindi il seguente file MainThread.java

package com.example;

public class MainThread extends Thread { //flag per mantenere lo stato di gioco private boolean running; public void setRunning(boolean running) { this.running = running; } @Override public void run() { while (running) { } } }

Notiamo la riga

59/78

Dott.Ing.Ivan Ferrazzi

private boolean running;

che utilizziamo per definire la variabile running che ci permetterà di attivare (true) o meno (false) il ciclo infinito del gioco.Una volta creato il ciclo di gioco dobbiamo però anche farlo utilizzare dal nostro programma. Andiamo quindi a combinare il ciclo thread con la superficie di gioco all'interno del file MainGamePanel.java come segue

package com.example;

import android.app.Activity; import android.content.Context; import android.graphics.Canvas;import android.view.SurfaceHolder; import android.view.SurfaceView; public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {

private MainThread thread;

public MainGamePanel(Context context) { super(context); getHolder().addCallback(this);

thread = new MainThread();

setFocusable(true); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event);

60/78

Dott.Ing.Ivan Ferrazzi

}

@Override protected void onDraw(Canvas canvas) { }}

Con

private MainThread thread;

definiamo la variabile thread che dovrà contenere l'oggetto MainThread mentre andiamo poi a creare l'oggetto all'interno del costruttore con

thread = new MainThread();

Il metodo surfaceCreated() viene richiamato quando il sistema ha completato l'elaborazione del costruttore MainGamePanel() e quindi quando si è completata anche la creazione dell'oggetto thread. Ecco perché facciamo partire da qui il ciclo del nostro gioco con le righe

thread.setRunning(true);thread.start();

La prima setta la variabile running a true, mentre la seconda farà partire il nostro ciclo di gioco. Naturalmente non basta pensare a come far partire il nostro gioco, ma dobbiamo anche preoccuparci del fermare il tutto in maniera sicura.Ecco che inseriamo quindi nel metodo surfaceDestroyed() il seguente blocco che dovrà occuparsi di fermare il thread in maniera appropriata

boolean retry = true;while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { }}

Il nostro thread viene interrotto con la riga thread.join(). Il join ha il compito di avviare le procedure che permetteranno di terminare il thread e attende che questo succeda. In alcuni casi questo potrebbe causare degli errori. Ecco perché incapsuliamo il tutto all'interno del blocco

try {} catch (InterruptedException e) {}

che, in caso di errore, termina l'esecuzione del blocco try per passare al contenuto del blocco catch. Il tutto viene messo all'interno di un while che termina il suo ciclo solo quando la variabile retry diventa false, ossia

61/78

Dott.Ing.Ivan Ferrazzi

quando il contenuto del blocco try viene eseguito senza errori.

Aggiungiamo l'iterazione con lo schermo

Proseguiamo con l'aggiunta di un'iterazione con lo schermo. Lo schermo rimarrà in ascolto per identificare l'eventuale pressione sullo schermo da parte dell'utente. Dobbiamo quindi modificare il nostro thread in maniera tale che sia in grado di recuperare sia l'evento della pressione su schermo che la superficie di gioco. Il file MainThread verrà quindi modificato come segue:

package com.example;

import android.view.SurfaceHolder;

public class MainThread extends Thread { //flag per mantenere lo stato di gioco private boolean running;

private SurfaceHolder surfaceHolder; private MainGamePanel gamePanel;

public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) { super(); this.surfaceHolder = surfaceHolder; this.gamePanel = gamePanel; } @Override public void run() { while (running) { } } }

Importiamo la classe SurfaceHolder con

import android.view.SurfaceHolder;

e creiamo le due variabili che dovranno mantenere il SurfaceHolder ed il relativo pannello di gioco

private SurfaceHolder surfaceHolder; private MainGamePanel gamePanel;

Poi aggiungiamo il costruttore che dovrà recuperare dalla classe superiore le due informazioni necessarie.Ora dobbiamo modificare il costruttore all'interno del file MainGamePanel.java per richiamare l'oggetto MainThread utilizzando i nuovi parametri:

62/78

Dott.Ing.Ivan Ferrazzi

public MainGamePanel(Context context) { super(context); getHolder().addCallback(this);

thread = new MainThread(getHolder(), this);

setFocusable(true); }

Infine dobbiamo definire il comportamento del nostro programma nel momento in cui qualcuno preme all'interno del nostro pannello di gioco (il nostro SurfaceHolder). Modifichiamo quindi il metodo onTouchEvent() all'interno del file MainGamePanel.java come segue:

public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (event.getY() > getHeight() - 50) { thread.setRunning(false); ((Activity)getContext()).finish(); } } return super.onTouchEvent(event); }

All'interno di questo metodo controlliamo se l'azione effettuata dall'utente è ″premere sullo schermo″ (MotionEvent.ACTION_DOWN) con

event.getAction() == MotionEvent.ACTION_DOWN event.getY() recupera la posizione Y della posizione sullo schermo. Nel caso in cui si prema nell'area di 50 pixel presente nella parte bassa dello schermo il thread e l'intera activity verrebbero terminati.Ricordiamoci di importare anche le seguenti classi

import android.view.MotionEvent;import android.app.Activity;

Aggiungiamo un'immagine

Creiamo un'immagine con le dimensioni inferiori a 320x240 pixel che chiameremo img0.png. Copiamo l'immagine all'interno della cartella res/drawable-mdpi. L'immagine verrà identificata automaticamente con il percorso alla risorsa

R.drawable.img0

Modifichiamo il metodo onDraw() all'interno del nostro MainGamePanel come segue:

63/78

Dott.Ing.Ivan Ferrazzi

@Override protected void onDraw(Canvas canvas) { canvas.drawBitmap( BitmapFactory.decodeResource(getResources(), R.drawable.img0), 10, 10, null); }

Per far funzionare il nuovo codice è importante importare anche le dovute classi

import android.graphics.BitmapFactory;

Questa però non è l'unica modifica necessaria, ma è indispensabile modificare anche il comportamento del nostro thread. Il file MainThread verrà quindi modificato come segue

package com.example;

import android.view.SurfaceHolder; import android.graphics.Canvas;

public class MainThread extends Thread { //flag per mantenere lo stato di gioco private boolean running;

private SurfaceHolder surfaceHolder; private MainGamePanel gamePanel;

public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) { super(); this.surfaceHolder = surfaceHolder; this.gamePanel = gamePanel; }

public void setRunning(boolean running) { this.running = running; } @Override public void run() { Canvas canvas;

while (running) { canvas = null;

try { canvas = this.surfaceHolder.lockCanvas(); synchronized (surfaceHolder) { this.gamePanel.onDraw(canvas); } }finally{ if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } }

64/78

Dott.Ing.Ivan Ferrazzi

} } }

All'interno del metodo run() cerchiamo di creare un nuovo canvas che utilizziamo per il recupero dell'attuale pannello di gioco. Con

canvas = this.surfaceHolder.lockCanvas();

blocchiamo l'attuale contenitore del pannello di gioco per recuperare il canvas. Una volta bloccato possiamo effettuare le dovute modifiche con

synchronized (surfaceHolder) { this.gamePanel.onDraw(canvas); }

ed infine sblocchiamo nuovamente il contenitore per visualizzare il nuovo contenuto aggiornato con

surfaceHolder.unlockCanvasAndPost(canvas);

Il blocco synchronized permette di bloccare l'oggetto surfaceHolder per sincronizzarne gli accessi alle proprie risorse. Questo evita che vengano effettuati più accessi contemporaneamente e di avere quindi dei dati incompleti. Attenzione! In caso di oggetto surfaceHolder nullo verrebbe generato un'eccezione NullPointerException.Il tutto lo includiamo all'interno del blocco

try {}finally{}

che esegue le istruzioni presenti all'interno del try. Le istruzioni nel finally vengono eseguite in ogni caso, sia che il try restituisce un errore, sia che riesca a terminare correttamente.

Animiamo l'immagine

Cerchiamo ora di muovere l'immagine inserita. Faremo in modo che l'immagine scorra sul nostro schermo dall'alto verso il basso. Con i telefoni di ultima generazione è quasi indispensabile inserire un freno all'interno della nostra applicazione che possiamo variare in base all'esigenza. Modifichiamo il metodo run() all'interno del file MainThread.java come segue

@Override public void run() { Canvas canvas;

65/78

Dott.Ing.Ivan Ferrazzi

while (running) { canvas = null;

try { sleep(100);

canvas = this.surfaceHolder.lockCanvas(); synchronized (surfaceHolder) { this.gamePanel.onDraw(canvas); } }catch(InterruptedException e) { }finally{ if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } }

} }

Il freno che abbiamo inserito è la funzione sleep() che permette di settare il numero dei millisecondi per i quali l'elaborazione del nostro programma deve rimanere fermo. Questo metodo può essere utilizzato solamente all'interno di un

try { sleep(100);}catch(InterruptedException e) {}

E' fondamentale ricordarsi di importare la corrispettiva classe

import java.lang.InterruptedException;

Poi modifichiamo il MainGamePanel.java come segue per aggiungere la nostra semplice animazione

package com.example;

import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.graphics.BitmapFactory; import android.view.MotionEvent; import android.graphics.Paint; import android.graphics.Color; public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {

private MainThread thread; private int move = 10;

public MainGamePanel(Context context) {

66/78

Dott.Ing.Ivan Ferrazzi

super(context); getHolder().addCallback(this);

thread = new MainThread(getHolder(), this);

setFocusable(true); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }

@Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLACK); canvas.drawRect(0,0,640,480,paint);

move += 5; if(move > 300) move = -10;

canvas.drawBitmap( BitmapFactory.decodeResource(getResources(), R.drawable.alien), 10, move, null); } }

Vediamo in grassetto tutte le modifiche apportate. Come prima cosa importiamo le dovute classi con

import android.graphics.Paint; import android.graphics.Color;

Poi definiamo la variabile che verrà incrementata ad ogni passaggio

67/78

Dott.Ing.Ivan Ferrazzi

private int move = 10;

Ed infine le modifiche al metodo onDraw(), ossia

Paint paint = new Paint(); paint.setColor(Color.BLACK); canvas.drawRect(0,0,640,480,paint);

per colorare di nero lo sfondo ad ogni passaggio

move += 5;

per incrementare la variabile di 5 pixel e

if(move > 300) move = -10;

per ripartire nuovamente dall'alto una volta superati i 300 pixel.

Un'oggetto per lo sprite

Uno sprite nel mondo dei videogiochi è un'immagine che si muove sullo schermo, esattamente come la nostra immagine che cade dall'alto verso il basso. Quello che andremo a fare ora è creare una classe appositamente per il nostro oggetto in movimento.Creiamo una nuova cartella che possiamo chiamare sprites all'interno della cartella base dei sorgenti, ossia src/com/example, e creiamo la nuova classe come segue

package com.example.sprites; import android.graphics.Bitmap; import android.graphics.Canvas;

public class Enemy { private Bitmap sprite; private float x; private float y; public Enemy(Bitmap sprite, float x, float y) { this.sprite = sprite; this.x = x; this.y = y; }

public void setY(float y) { this.y = y; }

public void draw(Canvas canvas) { canvas.drawBitmap(sprite, x, y, null);

68/78

Dott.Ing.Ivan Ferrazzi

}}

Utilizziamo il metodo getY() per impostare la posizione sull'asse verticale Y ed il metodo draw() che si occupa di aggiornare il canvas del gioco.Dopo aver creato la classe la importiamo all'interno del nostro progetto, ossia all'interno del file MainGamePanel.java, come segue

package com.example;

import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.graphics.BitmapFactory; import android.view.MotionEvent; import android.graphics.Paint; import android.graphics.Color; import com.example.sprites.Enemy; public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {

private MainThread thread; private int move = 10; private Enemy enemy;

public MainGamePanel(Context context) { super(context); getHolder().addCallback(this);

thread = new MainThread(getHolder(), this);

enemy = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.alien), 10, move);

setFocusable(true); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; while (retry) { try { thread.join(); retry = false;

69/78

Dott.Ing.Ivan Ferrazzi

} catch (InterruptedException e) { } } } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }

@Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLACK); canvas.drawRect(0,0,640,480,paint);

move += 5; if(move > 300) move = -10;

enemy.setY(move); enemy.draw(canvas); } }

Importiamo l'oggetto con

import com.example.sprites.Enemy;

per poi creare la variabile all'interno della quale memorizzeremo l'oggetto in questione con

private Enemy enemy;

Poi inizializziamo l'oggetto con

enemy = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.alien), 10, move);

dove definiamo l'immagine da utilizzare e le posizioni di partenza x e y. Ad ogni ciclo definiamo la nuova posizione verticale dell'oggetto per poi aggiornare i contenuti grafici con

enemy.setY(move);enemy.draw(canvas);

Colpire il nemico

Per verificare se cliccando sullo schermo abbiamo o meno colpito il nostro nemico, utilizziamo il metodo onTouchEvent(). Modifichiamo quindi questo metodo come segue:@Override public boolean onTouchEvent(MotionEvent event) {

70/78

Dott.Ing.Ivan Ferrazzi

if(event.getAction() == MotionEvent.ACTION_DOWN) { ... }

return super.onTouchEvent(event); }

Il nuovo blocco if che inseriamo permette di verificare se abbiamo cliccato sullo schermo. Ora dobbiamo verificare dove l'utente ha cliccato sullo schermo e se il punto in questione coincide con la posizione del nostro nemico. Se il nostro sprite ha una dimensione di 38x38 pixel il controllo è il seguente:

@Override public boolean onTouchEvent(MotionEvent event) {

if(event.getAction() == MotionEvent.ACTION_DOWN) { if((event.getX() >= 10) && (event.getX() <= 10 + 38) && (event.getY() >= move) && (event.getY() <= move + 38)) { COLPITO = true; } }

return super.onTouchEvent(event); }

Utilizziamo event.getX() e event.getY() per recuperare le coordinate dello schermo relative al punto di pressione dell'utente sullo schermo. Per fermare la caduta del nostro nemico nel momento in cui ci clicchiamo sopra modifichiamo il codice di MainGamePanel.java come segue:

package com.example;

import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.graphics.BitmapFactory; import android.view.MotionEvent; import android.graphics.Paint; import android.graphics.Color; import com.example.sprites.Enemy; public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {

private MainThread thread; private int move = 10; private Enemy enemy; private boolean COLPITO = false;

public MainGamePanel(Context context) { super(context); getHolder().addCallback(this);

71/78

Dott.Ing.Ivan Ferrazzi

thread = new MainThread(getHolder(), this);

enemy = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.alien), 10, move);

setFocusable(true); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } } @Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN) { if((event.getX() >= 10) && (event.getX() <= 10 + 38) && (event.getY() >= move) && (event.getY() <= move + 38)) { COLPITO = true; } } return super.onTouchEvent(event); }

@Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLACK); canvas.drawRect(0,0,640,480,paint);

if(!COLPITO) { move += 5; if(move > 300) move = -10; }

enemy.setY(move); enemy.draw(canvas); } }

72/78

Dott.Ing.Ivan Ferrazzi

Effetti sonori

Come ultima cosa aggiungiamo l'effetto sonoro. Prendiamo l'effetto sonoro di un'esplosione che chiameremo explosion.wav e copiamolo all'interno della cartella /res/raw (createla se non esiste). Poi modifichiamo il codice di MainGamePanel.java come segue:

package com.example;

import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.graphics.BitmapFactory; import android.view.MotionEvent; import android.graphics.Paint; import android.graphics.Color; import com.example.sprites.Enemy;import android.media.SoundPool;import android.media.AudioManager; public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {

private MainThread thread; private int move = 10; private Enemy enemy; private boolean COLPITO = false; private SoundPool sounds; private int sExplosion;

public MainGamePanel(Context context) { super(context); getHolder().addCallback(this);

thread = new MainThread(getHolder(), this);

enemy = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.alien), 10, move);

setFocusable(true); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) {

73/78

Dott.Ing.Ivan Ferrazzi

boolean retry = true; while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } } @Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN) { if((event.getX() >= 10) && (event.getX() <= 10 + 38) && (event.getY() >= move) && (event.getY() <= move + 38)) { COLPITO = true;

sounds.play(sExplosion, 1, 1, 0, 0, 1); } } return super.onTouchEvent(event); }

@Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLACK); canvas.drawRect(0,0,640,480,paint);

if(!COLPITO) { move += 5; if(move > 300) move = -10; }

enemy.setY(move); enemy.draw(canvas); } }

Come prima cosa importiamo le classi necessarie con

import android.media.SoundPool;import android.media.AudioManager;

per poi aggiungere le variabili necessarie

private SoundPool sounds;private int sExplosion;

Ora dobbiamo creare un SoundPool in grado di contenere gli effetti sonori che ci interessano. Inizializziamo il tutto con la riga

sounds = new SoundPool(10, AudioManager.STREAM_MUSIC,0);

dove il primo parametro identifica il numero di effetti paralleli (10) che

74/78

Dott.Ing.Ivan Ferrazzi

SoundPool può far partire, il tipo di stream che vogliamo utilizzare (STREAM_MUSIC) e la priorità, attualmente non utilizzata, messa al valore di default.Poi carichiamo l'effetto all'interno del SoundPool con

sExplosion = sounds.load(context, R.raw.explosion, 1);

Ed infine decidiamo dove far partire l'effetto sonoro inserendo la seguente riga

sounds.play(sExplosion, 1, 1, 0, 0, 1);

I parametri identificano l'id dell'audio registrato precedentemente (sExplosion), il volume sinistro dell'effetto (valori possibili da 0 a 1), il volume destro dell'effetto (valori possibili da 0 a 1), la priorità (dove 0 è la priorità minore), la modalità di ripetizione (0 – non ripetere, 1 – ripeti all'infinito) ed il bitrate di riproduzione (1.0 è il valore base, mentre i valori possibili vanno da 0.5 a 2.0).

75/78

Dott.Ing.Ivan Ferrazzi

Google PlayIn questo capitolo cercheremo di comprendere i passaggi fondamentali per la pubblicazione della nostra applicazione.

Registrazione sviluppatore

Prima di poter pubblicare la nostra applicazione è indispensabile registrarsi come sviluppatore Android. La quota di partecipazione attuale è pari a $ 25. Che si pagano mediante carta di credito al momento della registrazione. Google ci metterà all'incirca 48 ore per verificare l'avvenuto pagamento. Nel frattempo abbiamo comunque il back office da sviluppatore a disposizione per la preparazione della scheda relativa alla nostra applicazione. La registrazione può essere effettuata utilizzando il seguente link

https://play.google.com/apps/publish/v2/

Nel back office ci verrà chiesto del materiale da uploadare come screenshots della nostra applicazione in vari formati (7”, 10”, ecc.), descrizione e titolo nelle varie lingue, logo in alta risoluzione, ecc.

Certificare le nostre applicazioni

Fino ad ora si utilizzava il DEBUG MODUS per compilare la nostra applicazione. Infatti, con il comando

76/78

Dott.Ing.Ivan Ferrazzi

ant debug

si riusciva a creare una versione debug dell'applicazione che potevamo inviare ed installare direttamente sul nostro dispositivo. Questa versione può però essere utilizzata solamente in fase di test, non può quindi essere utilizzata come versione ufficiale da pubblicare sul Play Store di Google.Creare la nostra chiave privataQuello che dobbiamo fare è creare una versione di release (rilascio) del nostro programma e per fare questo abbiamo bisogno di una chiave privata che ci identifichi. Google non ci obbliga di andare ad un CA (Certification Authority) per creare la nostra chiave, ci permette di utilizzare anche chiavi private create da noi.Per creare una nostra chiave privata possiamo utilizzare il comando keytool come segue

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

I vari parametri sono:-genkey Generare una coppia di chiavi pubblica e

privata.-v Attiva la visualizzazione su schermo delle

operazioni (verbose mode).-alias Permette di inserire un nome da utilizzare

come alias per la chiave creata. Verranno prese in considerazione solamente i primi 8 caratteri.

-keyalg Permette di indicare l'algoritmo da utilizzare per generare la chiave. In questo caso è supportato sia DSA che RSA.

-keysize La dimensione in bit della chiave generata. Se non definito viene utilizzata una dimensione pari a 1024 bit. Consigliamo di utilizzare un valore pari o superiore a 2048.

-validity Il periodo di validità della nostra chiave definita in giorni. Consigliamo un valore pari o superiore a 10000.

-keystore Permette di indicare il nome del file che conterrà la chiave generata.

Una volta confermato il comando ci verranno fatte una serie di domande. Tra le altre cosi ci chiederà di definire una password da utilizzare per il nostro alias e per il nostro keystore. Queste password sono importanti perché verranno richieste dall'ambiente Android in fase di compilazione del nostro codice.Configurazione del progettoCreata la nostra chiave privata dobbiamo configurare il progetto in

77/78

Dott.Ing.Ivan Ferrazzi

maniera tale che riconosca la chiave e la utilizzi durante la compilazione del nostro codice.Copiamo il file generato da keytool con all'interno la nostra chiave che per comodità chiameremo progetto.keystore all'interno della cartella root del nostro progetto. Per capire meglio le prossime righe utilizziamo progetto_alias come alias per il nostro progetto (definito sempre mediante comando keytool).Apriamo il file ant.properties e aggiungiamo le seguenti righe

key.store=progetto.keystorekey.alias=progetto_alias

Il valore di key.store contiene il percorso al file in questione partendo dalla cartella root del nostro progetto.Creare la nostra releaseOra possiamo compilare il nostro progetto con il seguente comando

ant release

In fase di compilazione ci verrà richiesta la password del nostro key.store e dell'alias. Attenzione! La password viene inserita in chiaro! Chi si trova con voi davanti allo schermo potrà quindi leggere le password che inserite!A compilazione completata troviamo nella cartella bin il file <nome_progetto>-release.apk. Ora siamo in possesso di un file pronto per l'installazione ufficiale oppure per la distribuzione.

78/78