Primi esempi di interfacce grafiche con Android
Docente: Gabriele Lombardi
[email protected]
© 2012 - CEFRIEL
The present original document was produced by CEFRIEL and the Teacher for the benefit and internal use of this
course, and nobody else may claim any right or paternity on it. No right to use the document for any purpose other than
the Intended purpose and no right to distribute, disclose, release, furnish or disseminate it or a part of it in any way or
form to anyone without the prior express written consent of CEFRIEL and the Teacher.
© copyright Cefriel and the Teacher-Milan-Italy-23/06/2008. All rights reserved in accordance with rule of law and
international agreements.
© 2012 - CEFRIEL
Sommario
SLIDE
CONTENUTO
MVC pattern
Perché preoccuparsene, come implementarlo
Tutto in XML
Come descrivere interfacce grafiche in XML: basi
Widget e container
Esempi di utilizzo di widget e container in XML
Interagire con l’utente
Esempi di eventi e accesso ai dati della UI
Logging & Debugging
Loggare per sapere, debuggare le proprie app
© 2012 - CEFRIEL
I design pattern (GRASP, GoF, altri)

Cosa sono i design pattern?
– Soluzioni efficaci a problemi frequenti;
– le trovate «preconfezionate» in libri (GoF), su forum;
– operano a diversi livelli (classe, collaborazione, framework,
componenti).

GRASP:
– sono più linee guida che design pattern;
– low copuling & high coesion sono i più importanti.

GoF:
– dal libro «Design patterns» della gang of four;
– si dividono in strutturali, creazionali, comportamentali;
– descrivono soluzioni a livello di collaborazione.


MVC (Model View Control),
MVP (Model View Presenter):
– definiscono una architettura astratta in vengono separati:
• il modello dei dati su cui l’applicazione opera;
• la/le viste dei dati da mostrare all’utente;
• le operazioni da svolgere in risposta agli eventi.
– Non comprende la logica di business!!!
© 2012 - CEFRIEL
MVC e MVP

A programmare seguendo l’istinto:
– si finisce per ottenere l’anti-pattern SmartGUI;
– l’applicazione cresce attorno all’interfaccia grafica;
– alla fine non saremo più in grado di staccare le funzionalità
dall’interfaccia (se non con molto sforzo).

Model e logica di business:
– l’applicazione dovrebbe nascere sulla carta in UML;
– il modello concettuale (dei dati) dovrebbe guidarci;
– ("Model Driven Design", Evans)
– la logica di business dovrebbe venir realizzata PRIMA
dell’interfaccia di interazione con l’utente;
– realizzare sia una GUI che una CLI aiuta a separare.

MVC e MVP vengono dopo:
– sono pattern da applicare quando il core esiste già;
– servono per costruire l’interfaccia (grafica);
© 2012 - CEFRIEL
MVC e MVP

MVC:
– il modello rappresenta i dati,
eventualmente da DB;
– le viste mostrano dati e
strumenti di interazione;
– i controller interagiscono con
le viste e i dati per richiamare
la logica di business.

Model
DB
View
Controller
Logica di
business
MVP (view attiva/passiva):
– riguarda la presentazione dei
dati (non l’interazione);
– arricchisce l’interazione tra
modello e vista;
– il presenter manipola i dati
perché possano essere
presentati correttamente;
© 2012 - CEFRIEL
View
Presenter
Model
MVC in Android
Meglio se generata
da file XML in
/res/layout
DB
Model
View
JavaBeans
android.view.View
Controller
Contengono dati
accessibili come
property (vedi dopo)
Activity
© 2012 - CEFRIEL
Un primo esempio con layout in XML

Scopo:
– realizzare una semplice calcolatrice che effettui i calcoli su
espressioni contenenti solo interi relativi e frazioni,
esprimendo il risultato come una frazione.

Architettura:
– decidiamo di dividere in due parti il «progetto»:
• libreria core di calcolo (in grado di parsare una
stringa, ed effettuare i calcoli);
• applicazione Android con una semplice
interfaccia classica da calcolatrice.
– L’interfaccia comprenderà:
• un layout XML in cui vengono descritti i widget
da includere nell’interfaccia;
• un’attività che esegue le azioni richieste dagli
eventi (aggiungere caratteri, fare conti).
© 2012 - CEFRIEL
Un layout da calcolatrice
<?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">
<EditText android:id="@+id/display"
android:text="0" android:textColor="#000000"
android:maxLines="5"
android:enabled="false" android:editable="false"
android:layout_width="fill_parent" android:layout_height="wrap_content"/>
<TableLayout
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:stretchColumns="0,1,2,3">
<TableRow>
<Button android:id="@+id/canc" android:text="&lt;="/>
<Button android:id="@+id/clear" android:text="C"/>
<Button android:id="@+id/compute"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_span="2" android:text="="/>
</TableRow>
</TableLayout>
</LinearLayout>
© 2012 - CEFRIEL
Ascoltare gli eventi
@Override
public void onCreate(Bundle savedInstanceState)
{
// Initialization:
super.onCreate(savedInstanceState);
Scelgo il layout XML da
setContentView(R.layout.main);
utilizzare in questa attività
// Connecting the event listeners:
((Button)findViewById(R.id.compute)).setOnClickListener(onCompute);
((Button)findViewById(R.id.num_0)).setOnClickListener(onAddChar);
…
((Button)findViewById(R.id.par_close)).setOnClickListener(onAddChar);
((Button)findViewById(R.id.canc)).setOnClickListener(onCanc);
((Button)findViewById(R.id.clear)).setOnClickListener(onClear);
}
Si osservi come
vengono reperiti i
widget dalla vista
Connetto gli eventi utilizzando
dei miei listener (vedere dopo)
© 2012 - CEFRIEL
Un ascoltatore
private OnClickListener onCompute = new OnClickListener() {
public void onClick(View button) {
Se non ci sono
try {
errori il testo è nero
// No errors up to now:
((EditText)findViewById(R.id.display)).setTextColor(Color.BLACK);
// Obtaining the expression:
CharSequence expr = ((EditText)findViewById(R.id.display)).getText();
// Computing:
Fraction res = Calculator.compute(new StringBuilder(expr).toString());
// Setting the text result:
((EditText)findViewById(R.id.display)).setText(res.toString());
isInitial = true;
// Done:
Se ci sono errori il
testo è rosso
}
catch (Throwable t) {
((EditText)findViewById(R.id.display)).setTextColor(Color.RED);
}
} };
© 2012 - CEFRIEL
Proviamola

Provate a compilare ed installare
(nell’emulatore o anche nel
dispositivo reale);
– su emulatore provate il debuger;
– osservate cosa avviene sulla vista.

Esercizio:
– provate a modificare gli attributi dei
widgetper vedere che effetto hanno.

Esercizio:
– aggiungete una checkbox con cui
decidere se mostrare il risultato
come frazione o decimale;
– per farlo dovete utilizzare:
• il widget CheckBox;
• OnCheckedChangeListener
© 2012 - CEFRIEL
Per la soluzione vedere in
FractionCalc2
A grande successo.. nuova release!

Associare un’icona all’app:
– creare l’icona a risoluzioni diverse:
• in res/drawable
• in res/drawable-ldpi
• in res/drawable-hdpi
 48x48
 36x36
72x72
– modificare AndroidManifest.xml:
– <application android:label="@string/app_name"
–
android:icon="@drawable/fraction_calc_icon">
– NOTA: nei nomi di risorse solo [a-z], [0-9], ‘_’ o ‘.’.

Modificare versione mostrata ed effettiva (codice):
– modificare AndroidManifest.xml:
– <manifest … package="com.gab.tests.android.fractioncalc"
–
android:versionCode="2" android:versionName="1.1">
– 1.1 verrà mostrato (nel market), 2 è un intero incrementale
© 2012 - CEFRIEL
Ho i tasti sul telefono.. Perché non usarli?

Ogni attività può ricevere tanti eventi!
– Il tocco dello schermo viene usato di frequente…
– …ma se ci sono tasti VERI vengono usati volentieri!
@Override public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_0:
onAddChar.onClick(findViewById(R.id.num_0)); break;
case KeyEvent.KEYCODE_9:
onAddChar.onClick(findViewById(R.id.num_9)); break;
case KeyEvent.KEYCODE_PLUS:
onAddChar.onClick(findViewById(R.id.op_PLUS)); break;
case KeyEvent.KEYCODE_EQUALS:
onCompute.onClick(findViewById(R.id.compute)); break;
case KeyEvent.KEYCODE_BACK:
onCanc.onClick(findViewById(R.id.canc)); break;
default: return super.onKeyUp(keyCode, event);
}
return true;
}
© 2012 - CEFRIEL
Inseriamo un’icona sul pulsante <=

Soluzione 1:
– su ogni widget possiamo definire la proprietà
«background» con una risorsa «drawable»;
– <Button android:id="@+id/canc"
–
android:layout_width="fill_parent"
–
android:layout_height="fill_parent"
–
android:background="@android:drawable/ic_input_delete"/>
– NOTA: invece che riferirci a un drawable “nostro” ci siamo
riferiti a uno predefinito:
• @android:drawable/ic_input_delete
• per un elenco si vedano nell’SDK:
– android-sdk\platforms\android-10\data\res\drawable-ldpi
– android-sdk\platforms\android-10\data\res\drawable-hdpi

Problemi con questa soluzione:
– immagine adattata alle dimensioni del pulsante (provate);
– noi vorremmo sovrapporre l’icona sul pulsante.
© 2012 - CEFRIEL
Inseriamo un’icona sul pulsante <=

Soluzione 2:
– al posto del pulsante inseriamo un layout relativo;
– all’interno del layout inseriamo:
• il pulsante (riempiendo tutto lo spazio);
• l’immagine (centrata ma non adattata).
– Ne risulta:
<RelativeLayout
android:layout_width="fill_parent" android:layout_height="fill_parent">
<Button android:id="@+id/canc"
android:layout_width="fill_parent" android:layout_height="fill_parent"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_input_delete"
android:layout_centerInParent="true"/>
</RelativeLayout>

Soluzione 3:
– utilizzare un ImageButton (con src il drawable).
© 2012 - CEFRIEL
Ruotare il dispositivo e…

…il layout non va più bene!
– (nell’emulatore provare con
CTRL+F12 (sinistro)

Per orientazioni diverse:
– non serve cambiare controller;
– servono view differenti.

Creiamo la cartella per i layout
XML in versione landscape:
– res/layout-land

Creiamoci dentro un layout
alternativo per il landscape:
– tabella 5x4

Giochiamo con l’orientazione
© 2012 - CEFRIEL
Layout alternativo

Altri strumenti esistono per livelli di API più alti:
– dalle API14 esiste il GridLayout:
– vedi in altriEsempi/xml/main_conGrid.xml
– occhio al range di dispositivi che volete supportare;

L’esempio con GridLayout:
<GridLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:columnCount="5"
android:rowCount="4">
…
<Button android:id="@+id/compute"
android:layout_rowSpan="2"
android:layout_gravity="fill"
android:text="="/>
…
</GridLayout>
© 2012 - CEFRIEL
Essendo noti sfruttiamo
numero righe/colonne
Usiamo rowSpan per
riempire 2 righe, gravity fill
per allargare il Button
Se sappiamo cos’è successo.. diciamolo!

Mostriamo una dialog di errore:
– se c’è una divisione per zero;
– negli altri casi (problema di sintassi);

Più modi per mostrare una dialog:
– utilizzando l’XML per descriverne
una, ed eseguendo showDialog;
– istanziandola ed invocando show;
– con DialogFragment.


Il primo è deprecato (non usare);
Vediamo il secondo:
final AlertDialog dialog = new AlertDialog.Builder(this).create();
dialog.setButton("Chiudi",
new android.content.DialogInterface.OnClickListener() {
public void onClick(DialogInterface dlgInt, int button) { dialog.hide(); }
});
dialog.setTitle(title);
dialog.setMessage(message);
dialog.show();
© 2012 - CEFRIEL
Logging per sapere cosa succede

Come loggare:
– da specificare le seguenti informazioni:
– livello del log (verbose, debug, info, warning, error, wtf(!!!)…);
– CHI genera il log (classe? Package? Stringa a scelta);
– COSA è successo (messaggio di log).
– utilizzare la classe Log (metodi statici):

• se bastano i metodi v, d, i, w, e, wtf;
• println è il metodo «raw» per scrivere log;
Come loggare in maniera furba:
–
–
–
–

la generazione di log è fastidiosa e onerosa;
centralizzandola la si può controllare!
si veda la classe Logger in FractionCalc2.
Il compilatore e l’ottimizzatore SANNO il fatto loro!
E per leggere i log?
– adb logcat FractionCalc:i *:s
© 2012 - CEFRIEL
(filtrato, livelli [v,d,i,w,e,f,s])
Logging: cosa succede?
© 2012 - CEFRIEL
Debugging: step-by-step, monitor

Per poter eseguire il debug su dispositivo:
– abilitarlo sul dispositivo da impostazioniapplicazioni
– abilitarlo nell’applicazione (attributo del manifest):
– <application android:label="@string/app_name"
–
android:icon="@drawable/fraction_calc_icon"
–
android:debuggable="true">
– in NetBeans, build e debug:
•  per l’emulatore
– se non abbiamo ancora provato… è l’ora di farlo!
•  per il dispositivo fisico
– richiede la presenza del dispositivo, ma volte è più rapido.

Un monitor con cui accedere al device:
– nell’sdk (dir tools, deve essere nel path) troviamo:
• DDMS (Dalvik Debug Monitor Service)…
• …proviamo a lanciarlo dalla linea di comando!
© 2012 - CEFRIEL
DDMS

DDMS comunica col device tramite:
– servizio demone adb-server;
– TCP/IP con forward su USB

DDMS permette di eseguire:
– monitoring di ram, processi, thread,
allocazione, gc, radio, gps, …;
– cattura di screen-shot;
– interazione con il FS.
© 2012 - CEFRIEL

Debug delle interfacce grafiche
Analizzare l’albero delle view:
– ci viene fornito lo strumento hierarchyviewer:
• cattura un’istantanea dello stato dell’albero;
• ci permette di vedere:
• come è strutturato
l’albero delle view;
• per ogni nodo:
posizione e
dimensioni;
• uno sketch
dell’interfaccia
grafica;
• lo screen-shot nodo
per nodo;
• Immagine del tutto.
© 2012 - CEFRIEL
Testing

Lo unit testing serve a testare in automatico e
ripetibilmente la propria applicazione:
– simulare dei casi d’uso dell’applicazione (scimmia);
– testare una porzione di applicazione (componenti).

Facciamo correre la scimmia:
– in modalità pseudo-random:
– adb shell monkey -p com.gab.tests.android.fractioncalc 1000
– ammaestrandone una col pitone:
– comando monkeyrunner per eseguire uno script python;
– un esempio di seguito, ma si può fare di tutto:
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
Ottengo il device
device = MonkeyRunner.waitForConnection()
package = 'com.gab.tests.android.fractioncalc'
activity = package + '.FractionCalc'
device.startActivity(component=package + '/' + activity)
device.touch(50,200,MonkeyDevice.DOWN_AND_UP)
device.touch(50,300,MonkeyDevice.DOWN_AND_UP)
…
© 2012 - CEFRIEL
Avvio l’applicazione
#1
#7
Simulazione tocco
Come funziona l’Android Unit Testing
Nostra applicazione
in esecuzione
Tools per accedere
agli internals
Pacchetti di test che
usano gli strumenti
© 2012 - CEFRIEL
Unit Testing

Creazione di un progetto di test:
– utilizziamo questa volta i tool da riga di comando:
• dalla directory radice del progetto da testare:
– android create test-project -m ../../FractionCalc2
-n FractionCalc2Test -p tests
– viene generata una dir tests con un progetto di testing:
• vengono creati per noi (tra il resto):
– AndoridManifest.xml  vediamo cosa contiene
– FractionCalcTest.java  vediamo cosa contiene

Esecuzione dei test:
– prima di poter eseguire con successo dei test:
• dobbiamo strumentare i test…;
• … e scrivere le unità di testing automatico.
– per strumentare ed eseguire i nostri test:
• ant/bin deve essere nel PATH
© 2012 - CEFRIEL
Unit Testing

Preparare ed eseguire i test:
– dobbiamo prima strumentare l’applicazione di test:
– ant instrument install
– poi possiamo eseguire una prima prova:
– adb shell am instrument -w -e class
com.gab.tests.android.fractioncalc.FractionCalcTest
com.gab.tests.android.fractioncalc.tests/android.test.Instrument
ationTestRunner

• NOTA: fallisce perché non ci sono unità.
Unità di testing:
–
–
–
–
Android testing framework estende JUnit;
esistono unità di test (foglie) e suite di test (rami);
è possibile definire una vera e propria foresta di test;
ogni unità di test comprende più test più le fixtures.
© 2012 - CEFRIEL
Preparare le unità di test

Ci è stata preparata la classe FractionCalcTest:
– implementato per Android, eredita da JUnit:
– ActivityInstrumentationTestCase2<FractionCalc>
–  ActivityTestCase  InstrumentationTestCase  TestCase
– si prepara per lavorare con la nostra attività main:
– public FractionCalcTest() {
super("com.gab.tests.android.fractioncalc",
FractionCalc.class); }
– dobbiamo aggiungere i test e ciò che ci sta attorno:
Activity frac;
public void setUp() { frac = getActivity(); }
public void tearDown() { frac.finish(); }
public void testExample() {
sendKeys("1 7 SLASH 2 MINUS 2 SLASH 4 EQUALS");
CharSequence text = ((EditText)frac.findViewById(R.id.display)).getText();
assertEquals("Result of 17/2-2/4=", "8", new StringBuilder(text).toString());
}
© 2012 - CEFRIEL
L’esecuzione dei test

Vanno installati ogni volta:
– ant instrument install
– ant test
– …
– -test-project-check:
– test:
–
[echo] Running tests ...
–
[exec]
–
[exec] com.gab.tests.android.fractioncalc.FractionCalcTest:.
–
[exec] Test results for InstrumentationTestRunner=.
–
[exec] Time: 3.263
–
[exec]
–
[exec] OK (1 test)
–
[exec]
–
[exec]
– BUILD SUCCESSFUL
– Total time: 8 seconds

Provate ad osservare l’emulatore durante il test!!! 
© 2012 - CEFRIEL
Da qui dove si va?

Cosa abbiamo visto sino ad ora?
–
–
–
–

Abbiamo «giocato» con una prima applicazione;
abbiamo costruito una GUI e ascoltato degli eventi;
abbiamo provato ad estenderla (versione 2);
abbiamo scoperto come fare testing automatico e
debugging della nostra applicazione (strumenti).
Cosa manca?
– Non abbiamo formalizzato COSA una attività sia;
– non abbiamo esplorato l’universo dei widget;
– in Android non esistono mica solo le GUI!!!

Continuiamo a giocare! 
© 2012 - CEFRIEL
Scarica

01_BaseGUI