Il 0% ha trovato utile questo documento (0 voti)
9 visualizzazioni27 pagine

Android Studio

Caricato da

mclucente98
Copyright
© All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
9 visualizzazioni27 pagine

Android Studio

Caricato da

mclucente98
Copyright
© All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd

EREDITARIETA’

Per la creazione di classi, possiamo sia sfruttare un comportamento “stand alone”, oppure avvalerci
di altre classi esistenti, tramite l’ereditarietà. Questo funziona diversamente da quanto avevamo in
Java, infatti derivano dalla classe [Link] (in modo da rimuovere alcuni metodi non importanti).
È una classe semplice, con vari metodi utili ad effettuare confronti tra indirizzi. Spesso questi
metodi vengono sovrascritti. La parola chiave open è necessaria per abilitare l’ereditarietà. In
Kotlin, per definizione, le classi non sono estendibili. Per definire una classe che non può avere
sottoclassi, bisogna inserire la parola chiave final (la classe non può avere sottoclassi). In caso
contrario, avendo inserito open, bisognerà, dopo i due punti si scrive la classe “madre”, con
all’interno delle parentesi il costruttore primario, richiamato.

L’ereditarietà permette di avere, nella classe derivata, tutti i metodi e gli attributi della classe
genitrice, ma permette anche di sovrascrivere alcuni metodi/attributi della classe genitrice. Perché
questo sia possibile è necessario dichiarare i metodi che si vuole sovrascrivere come “open” (nella
classe madre), e nella classe derivata bisogna precederli da “override” per sovrascriverli. Questa
capacità viene detta polimorfismo.

A volte è necessario costruire una classe destinata a memorizzare informazioni, senza inserire
ulteriore semantica. Kotlin offre quindi una sintassi semplificata: ci permette di dichiarare una class
preceduta dalla parola data. Deve essere dotata di un costruttore primario con almeno un
parametro. È destinata a rappresentare informazioni che vengono inserite direttamente in fase di
costruzione. Tutti i parametri devono essere etichettati con val/var. Non possono essere precedute
da altri modificatori. È un modo semplice per contenere dei dati.

Smart cast: ci sono situazioni in cui abbiamo bisogno di sapere se in una certa variabile di tipo
generico è presente un sottotipo più specifico. Ad esempio nella variabile generica view, che
contiene moltissime sottoclassi (button, textView…), per valutare il tipo effettivo dell’oggetto, si usa
l’operatore is. È un operatore booleano che prende da una parte un’espressione e dall’altra il nome
della classe (e ritorna true se è verificata la condizione). L’operatore, sa che eseguirà ciò che viene
detto nell’if soltanto se la condizione è verificata, e quindi annota view non come generico
elemento della classe, ma specificatamente come l’espressione specificata. Questo viene chiamato
smart cast.

LE ATTIVITA’

Un’applicazione Android è costituita da uno o più componenti, che sono definiti nel file manifest.
Un componente può essere di tipo:

• Attività: servono ad avere un’interazione con l’utente;

• Servizio: svolti in background, non hanno interfacce grafiche con l’utente, sono di supporto
alle attività;

• Ricevitore di messaggi broadcast: oggetti chiamati in causa quando accadono cose


particolari (es. sta per finire la memoria, è arrivata una notifica…);
• Content provider: chiamati in causa quando un’altra applicazione ci chiede in modo formale
alcune cose (es. scatto una foto e la condivido su Whatsapp → gestito da questi).

I componenti vengono creati automaticamente dal sistema operativo in risposta alle richieste
contenenti un intent (messaggi che arrivano dalle varie applicazioni). Le activity vengono
create/distrutte a piacimento di Android, a meno che non si impongano delle condizioni per la
gestione delle activity.

[Link] → la classe base che fornisce una GUI con cui gli utenti possono interagire al
fine di svolgere un dato compito operativo. Le applicazioni estendono tale classe per implementare
specifiche funzionalità (noi creiamo un’estensione di questa classe). Il nome della sottoclasse viene
indicato nel manifest file. È il sistema operativo che crea per noi l’activity, non siamo noi a creare
un’istanza di essa. Lo fa invocando alcuni metodi specifici, che gestiscono anche il ciclo di vita.

La classe activity permette di gestire l’interazione dell’utente. Deve acquisire le risorse necessarie
(es. sensori, caricare i contenuti) per fare ciò, poi costruisce e configura un’interfaccia grafica che
presenti questi contenuti, per poi prepararsi a reagire agli eventi scatenati dall’interazione con i
diversi widget, per implementare il comportamento voluto. Inoltre gestisce le notifiche del ciclo di
vita, occupandosi di salvare i dati raccolti e rilasciare le risorse quando il sistema notifica che
l’attività deve terminare.

Un’attività presenta di solito una singola interfaccia utente visuale, che permette un certo numero
di interazioni. All’interno di un’applicazione spesso sono dichiarate più attività. Ogni activity
supporta un task specifico (leggere un messaggio ricevuto, creare un nuovo messaggio…). Una di
queste activity sarà contrassegnata come la prima che deve essere presentata all’utente quando
l’applicazione viene lanciata.

Per passare da un’attività ad un’altra si sfruttano gli oggetti di tipo Intent: si passa al sistema
operativo questo messaggio, in modo che il sistema operativo possa caricare l’activity successiva.
Questo non deve per forza accadere tutto all’interno della stessa applicazione, ma possono anche
richiamare app diverse (es. Google Maps). Deve essere possibile, tornando indietro quando è stata
chiamata una diversa applicazione, tornare indietro all’applicazione precedente, e non alla home.
Questo succede perché Android crea uno stack delle attività, soltanto l’attività in cima allo stack è
visibile ed interattiva, mentre quelle precedenti rimangono in background finché esiste la nuova
attività. Quando termina l’attività in cima allo stack, quella precedente torna ad essere visibile ed
interattiva. L’ultima attività presente nello stack può essere distrutta, se le altre attività usano
troppe risorse.
CICLO DI VITA DI UN'ATTIVITÀ

Un’attività possiede tutta una serie di metodi per il suo funzionamento, che cominciano con “on”
(onCreate, onStart, onResume…). Questi metodi vengono chiamati da Android, mai da noi, nei
momenti opportuni. Noi possiamo effettuare, nella nostra classe, un ovverride di questi metodi.
Questi metodi rappresentano azioni che sono notifiche di cose che sono appena avvenute, o che
stanno per avvenire. OnCreate, onStart, onResume, indicano azioni appena avvenute. OnPause,
onStop, onDestroy, indicano azioni che stanno per accadere. Solitamente si fa sempre un override
del metodo onCreate, e raramente si opera sugli altri metodi.

L’attività comincia ad esistere nel momento in cui Android decide di farla esistere, perché ha
intercettato un intent. Dopodiché istanzia un processo in cui crea un oggetto di tipo activity in cui
invocare onCreate. Ora la nostra attività può configurarsi e diventare visibile. Quando è visibile
viene chiamato onStart, fino a quando non viene chiamato onDestroy. Tra onCreate e onStop
l’attività è visibile (mentre fino ad onDestroy non è necessariamente sempre visibile). Tra questi
due metodi non è necessariamente interattiva. Tra onResume e onPause è interattiva, questo è
l’intervallo vivo della nostra applicazione. Dopo la chiamata di onDestroy, l’attività cessa di esistere.
Ci sono più momenti però, in cui l’attività può venire distrutta e ricreata.

Se sovrascritti, i metodi legati alle notifiche del ciclo di vita devono richiamare internamente
l’implementazione data dalla superclasse, se no il sistema lancerà un’eccezione e chiuderà
l’applicazione.

OnCreate riceve un parametro b: Bundle? e viene chiamato quando l’attività viene eseguita per la
prima volta (ed il bundle vale null). Il bundle contiene valori che possono essere salvati (perciò
devono essere di un certo tipo) per non venire persi (come nell’esempio in cui ruotavamo il
cellulare) quando viene chiamato onDestroy. Quando l’attività viene lanciata nuovamente (a causa
di mancanza di risorse o per altri motivi), infatti, il parametro bundle conterrà informazioni di stato.
Nel metodo onCreate bisogna eseguire tutte le inizializzazioni, preparando la vista della schermata
attiva e caricando i dati che vogliamo che funzionino.

OnStart viene chiamato quando l’applicazione diventa visibile all’utente. Se l’attività è stata visibile
in precedenza, tale metodo viene preceduto dalla chiamata onRestart(). Nei casi più comuni non
occorre ridefinire questo metodo.

OnResume viene chiamato quando l’attività raggiunge la cima dello stack e diventa interattiva. In
questo metodo è possibile avviare la riproduzione di animazioni, video, suoni… e acquisire risorse
temporanee (come sensori e camera).

OnPause viene chiamato per bloccare attività che devono essere spostate nella seconda posizione
dello stack delle attività (ad esempio quando finisce un’animazione. Qui è opportuno rilasciare
tutte le risorse necessarie mentre l’applicazione è in background. Deve essere veloce, perché
l’attività successiva non verrà avviata finché è in esecuzione il metodo onPause.

OnStop → l’applicazione non è più visibile all’utente. Non è detto che questo metodo sia sempre
chiamato.

OnDestroy viene chiamato quando l’attività viene rimossa dalla memoria. Può essere la
conseguenza di una richiesta esplicita da parte dell’applicazione (es. metodo finish()), oppure
conseguenza di qualcosa che è successo nel sistema (es. abbiamo ruotato il cellulare). Per
distinguere queste due situazioni, possiamo invocare il metodo isFinishing, che ci ritornerà true se
la scelta è dell’utente, false se del sistema.

Nel metodo onCreate dobbiamo istanziare una vista, e questo viene fatto tramite il metodo
setContentView, che è fatto come un “albero”, in cui i rami sono le varie sottoviste. Si crea quindi,
tramite il pattern “composite” una gerarchia di componenti visuali (chiamato display-tree). Tutto
ciò che sta sullo schermo deve derivare dalla classe view, che contiene due famiglie di sottoclassi:

- sottoclassi specializzate per un particolare tipo di interazione (textView, button,


imageView…);

- sottoclassi derivanti da viewgroup: rappresentano un rettangolo divisibile in diversi layout


(abbiamo i metodi addView, removeView, che permettono di aggiungere o eliminare viste).
La posizione delle sottoviste è governata dagli algoritmi di layout (linear, relative,
costraint…).

La creazione delle viste può essere fatta da programma, oppure tramite XML. Solitamente si adotta
un approccio ibrido, descrivendo la gerarchia tramite XML ed eseguendo successivamente delle
personalizzazioni via codice. Occorre poter fare riferimento ai singoli elementi dell’albero visuale:
ad ogni elemento si associa un attributo di nome id.

Salvare lo stato di un’attività: per evitare che si perdano i dati quando si evoca il metodo
onDestroy. Prima di questa chiamata, android chiama il metodo onSaveInstanceState(b: Bundle).
Quando viene chiamato questo metodo, bundle viene usato per salvare alcune informazioni
(oggetti semplici). Android prenderà i dati in bundle, li salverà, e quando l’activity viene ricreata, i
dati salvati nel bundle vengono riesumati e passati al metodo onCreate (il bundle salvato è il
bundle del nuovo onCreate). Siccome ora onCreate ha una grande quantità di dati da gestire, è
stato creato un nuovo metodo per contenere il bundle → onRestoreInstanceState. In questo modo
onCreate rimane semplice, crea la vista, e onrestore… si occupa del ripristino dei dati salvati.

Si noti che onSave… non viene chiamato ogni volta che l’applicazione viene “uccisa”, ma solo se
questa cosa è stata decisa da Android con lo scopo di salvare delle risorse per poi ricreare la vista in
un tempo futuro. Se l’attività si chiude con finish (cioè si chiude normalmente), non viene
chiamato.

INTENT

Le attività sono sempre e solo create da Android stesso. Allora, per creare delle activity quando ne
abbiamo bisogno, servono particolari messaggi che viaggiano dal sistema operativo alle
applicazioni: gli intent. Essi sono messaggi asincroni che il sistema operativo avviano
all’applicazione, per associare le richieste dei processi alle appropriate attività e/o servizi. A volte è
l’utente stesso a fabbricare un intent. Ogni attività o servizio si attiva perché è stato trasmesso un
intent.

Si dividono in due famiglie:

- intent espliciti: il messaggio dichiara esplicitamente a quale specifico componente è diretto.


Rappresentano la richiesta di attivare un particolare componente all’interno del processo
dell’applicazione. Tale componente viene indicato attraverso la relativa classe. Occorre
specificare un riferimento al contesto;

- intent impliciti: possono essere inviati (e ricevuti) in broadcast, non hanno uno specifico
destinatario. Indicano l’azione da svolgere, lasciando al sistema operativo il compito di
individuare il componente opportuno in grado di gestirla. Sono composti da: Action, URI,
categoria. Vengono usati per creare altre attività.

All’interno dell’intent ci sono alcune informazioni base: solitamente abbiamo un “verbo” che
descrive l’azione da effettuare (fetch, dial…, si chiamano action). Poi c’è un oggetto, espresso con la
sintassi delle URI → fetch che cosa?. In questa famiglia abbiamo le URL (http:…). Ultimo elemento
sempre presente è la categoria, che serve a meglio specificare l’azione. Poi si ha uno spazio lasciato
all’utente, dove possono essere inserite ulteriori informazioni, a patto che esse siano scritte in
modo serializzato (cioè senza contenere puntatori, riferimenti ad oggetti…), questo poiché gli
intent devono poter viaggiare da un processo all’altro. Questa possibilità permette al sistema
operativo di dettagliare meglio le richieste, sia servendoci dei complementi standard che di dati
arbitrari. Gli intent possono essere creati a piacimento.

Cosa succede realmente quando un’attività ne lancia un’altra? Dobbiamo ricordarci che viene
sfruttato lo stack. L’interfaccia dell’activity appena chiamata nasconderà la precedente attività.
Quando termina, verrà rimossa dallo stack rivelando la precedente, a cui sarà semplicemente
notificato di essere stata riesumata (riparte dallo stato in cui era).
Ci sono delle varianti di questo pattern di base. Una variazione possibile è quella in cui la nuova
attività vuole mettersi al posto di quella vecchia (quella vecchia sparisce, se premo back, usciamo
dall’applicazione). Oppure la nuova attività potrebbe fornire un certo tipo di risultato a quella
precedente.

L’attività da lanciare all’avvio è identificata nel file [Link], inserendo come


specificazione l’intent filter. In questo modo posso inserire i dettagli main e launcher, che
specificano che tale attività è quella da mostrare al lancio. Se non ho attività segnate in questo
modo, non avrò schermate visualizzare al lancio e l’app non potrà partire.

È importante ricordare che le nostre activity ereditano dalla classe Context, così come i servizi e le
applicazioni. Questa classe offre le funzionalità necessarie per accedere alle risorse e alle classi
specifiche dell’applicazione e per interagire con il sistema operativo. Ogni componente attivo
costituisce il proprio contesto, cioè mentre la nostra activity è running, il suo contesto è l’activity
stessa. C’è anche un contesto globale, che è l’intera applicazione, a cui si accede con un metodo
specifico. Context è una classe astratta.

Attraverso questa classe possiamo accedere alle risorse, cioè l’insieme di stringhe, immagini, file
xml… che vengono aggregate al file eseguibile allo scopo di essere accessibili in modo uniforme ed
indipendente dalla cartella di installazione.

La classe Context offre anche altre funzionalità:

- avviare istanze di attività;

- ottenere asset inclusi nell’applicazione (cioè file accessibili in sola lettura, che servono per
includere determinati contenuti. Ora vengono poco usati, si inseriscono direttamente
contenuti grossi nell’app perché si ha molta banda);

- richiedere servizi di sistema (GPS etc.);

- controllare ed attivare permessi applicativi (es. permessi per accesso alla fotocamera);

- gestire file, directory e basi di dati accessibili solamente all’applicazione corrente.

Quest’ultima funzione è la più importante per ora. Android fornisce due spazi dedicati a
memorizzare contenuto. Ogni volta che viene installata un’applicazione sul telefono, Android
costruisce una cartella relativa ad essa, a cui dà dei permessi di lettura e scrittura (limitati all’app).
Qui vengono messe delle sottocartelle: la Cache, la Dbs (database, se utilizzati), e la cartella dei file
principali, che possiamo creare noi (occupa uno spazio della memoria del dispositivo). Poiché i
dispositivi possono avere una scheda SD esterna, abbiamo che i file in essa sono leggibili e scrivibili
da tutti (è uno spazio pubblico, a differenza di quello delle cartelle delle app). Context permette di
leggere dei file in entrambi i posti, con dei metodi che lavorano sull’internal storage, e sull’external
storage. È anche possibile lavorare su dei file, in scrittura.
INTERFACCE UTENTE

Android Studio permette di costruire interfacce utente in maniera sofisticata e in poche operazioni.
I motivi che rendono la progettazione dell’interfaccia complessa sono:

- motivazioni sul piano cognitivo: bisogna costruire un’interfaccia che sia gradevole,
funzionale, coerente (uguale anche nei diversi posti di utilizzo), facile da apprendere e da
usare in contesti operativi reali. Deve essere esteticamente gradevole, ma anche funzionale
alla risoluzione dei problemi dell’utente. A volte le azioni da svolgere sono semplici (es.
scrivere un messaggio), se così non è occorre progettare attentamente il flusso informativo
e l’organizzazione grafica dei contenuti. Dietro questi studi c’è la comprensione dei modelli
comportamentali dell’utente. Se così non avviene, l’utente sarà confuso e rifiuterà l’uso
dell’applicazione;

- motivazioni sul piano operativo: bisogna considerare che i terminali mobili sono
estremamente eterogenei, quindi cambiano molte variabili. Cambiano le dimensioni fisiche,
e con esse la leggibilità e l’interattività, cambia la risoluzione dello schermo (e quindi la
quantità di informazione complessivamente presentabile, cambiando il numero dei pixel),
cambia la densità dei punti (cioè il rapporto tra le due grandezze precedenti, che impatta
tutte le dimensioni), cambia l’orientamento (cioè l’impaginazione e l’organizzazione tra
portrait e landscape). Alcune di queste variabili variano di poco, ma altre possono variare
molto, quindi non è possibile progettare una singola interfaccia ed adattarla a tutte le
variabili in gioco. Occorre invece prevedere un insieme di possibili configurazioni alternative
e saper gestire la diversità. Dopodiché bisogna considerare le diversità tra gli utenti, cioè la
presenza di lingue diverse, ma anche di diverse culture e diverse convenzioni a cui fare
riferimento (anche banalmente parlando di colori, metodologie di lettura). Gli utenti inoltre
possono avere diversi gradi di confidenza con l’utilizzo del dispositivo (novizi/esperti), che
quindi implicano che sia la progettazione dell’app che del dispositivo stesso siano semplici
ed intuitive.

Non esiste una strategia universale, ma si possono identificare alcune linee guida.

• Il primo passo è legato all’analisi del contesto e dei processi che si intendono supportare,

• Il secondo cerca di ricondurre le singole operazioni a pattern progettuali noti e studiati (es.
il login, selezione multipla in una lista...),

• il terzo fa leva sui principi generali dell’interaction design, per verificare che non siano
violati alcuni principi fondamentali.
Analisi del contesto

Si declina in tre dimensioni:

Per l’analisi degli utenti, solitamente ci


si affida alla costruzione di personas.

Pattern progettuali

Insiemi di soluzioni note per gestire situazioni tipiche: moduli di login, di ricerca, liste di elementi
tra cui scegliere, inserimento dati, splash screen…

Layout

Avendo capito come progettare l’interfaccia utente, si passa alla sua realizzazione.

Una GUI, in generale, consiste di un insieme di componenti elementari (widget) opportunamente


disposti sullo schermo. La disposizione può essere descritta via codice o in un file XML di layout (il
file XML facilita il disaccoppiamento tra presentazione e logica).

Sebbene sia possibile collocare i singoli widget in una posizione fissa, non è una buona idea (per
via della variabilità degli schermi che renderebbe inutilizzabile l’applicazione nella maggior parte
dei casi). Potremmo avere problemi legati alla risoluzione (numero di pixel) dello schermo (schermi
piccoli non mostrerebbero alcuni componenti, schermi grossi avrebbero spazi vuoti…) e problemi
legati alla dimensione (lunghezza della diagonale), poiché l’area dei componenti varia con il
quadrato della lunghezza della diagonale: schermi molto piccoli rendono molto difficile
l’interazione e la lettura.

Quando creiamo l’interfaccia, andiamo a modellare il visual tree: nel view group vengono inseriti i
singoli widget. Android, oltre al costraint layout, offre diverse tipologie di contenitori che aiutano a
costruire viste predefinite e specializzate. Aiutano inoltre a gestire la variabilità dei dispositivi.

Bisogna sempre indicare le dimensioni, per poter usare i vari metodi che usano i pixel come unità
di misura. Per indicare le dimensioni si usano i dpi (density independence pixel) per tutto, tranne
che per le caselle di testo, per cui si usano gli sp.
TIPI DI LAYOUT

Nel linear layout, gli elementi figli vengono


disposti uno dopo l’altro su una colonna o
riga, in base all’orientamento del
contenitore.

Il relative layout permette di specificare la


posizione di un sotto-componente in
relazione ad altri sotto-componenti dello
stesso contenitore ed al contenitore stesso.
In questo esempio, A è posizionato nella
parte alta del contenitore padre, C è
posizionato sotto A, con il margine destro
allineato a quello di A, e B è posizionato sotto A, alla sinistra di C.

Il frame layout visualizza i propri figli sovrapposti, sulla stessa area.


L’elemento aggiunto più di recente è disegnato per ultimo e quindi
appare sopra gli altri.

Il controllo scollview è usato quando una vista deve poter mostrare più dati di quanti possano
essere mostrare in una singola schermata. Implementa esclusivamente lo scorrimento verticale;
per quello orizzontale si usa HorizontalScrollView. Entrambe le viste permettono di avere un
singolo elemento figlio: se occorre far scorrere più elementi bisogna raggrupparli in un linear
layout.

A volte però serve comunque creare layout alternativi che si adattino alle diverse configurazioni ed
alla varietà dei dispositivi. Il sistema di gestione delle risorse offre un supporto automatico per
presentare layout differenti in base alle effettive caratteristiche di un dispositivo (aggiungendo
opportuni suffissi alla cartella res/layout, si possono indicare configurazioni specifiche). Occorre
creare in ogni sotto-cartella un file con lo stesso nome (e gli stessi elementi fondamentali); il
sistema sceglierà automaticamente quali caricare.

GLI ELEMENTI DELL’INTERFACCIA UTENTE

Il blocco base, la radice, di tutti gli elementi


della UI è la classe View. È una classe concreta
che è possibile istanziare, e occupa uno spazio
rettangolare (con determinate caratteristiche
tipo backgroud, padding…) e permette di
disegnare gli elementi e gestire gli eventi
associati. La vediamo sempre sottoforma delle
sue sottoclassi specializzate (textView…), poiché
è la classe base per i widget. La sottoclasse base per i layout è ViewGroup, da cui derivano tutti i
layout. Essa aggiunge il concetto di sottovista, fornendo dei contenitori invisibili per organizzare il
layout e mantenere altre viste o gruppi di viste.

Tutte le viste sono disposte a formare un albero, che viene descritto in uno o più file XML. In questi
file, per ogni vista possiamo configurare:

• Proprietà: tutte le proprietà che sono conosciute prima della compilazione dell’applicazione
possono essere impostate nel file di risorse del layout. Quando chiamo setContentView,
vengono chiamati i file res per creare oggetti, che vengono popolati dalle proprietà. Questo
processo di creazione delle viste e di collegamento si chiama inflating, perché si passa da
un XML “snello”, ad un albero ramificato. Alcune proprietà, dinamiche, non possono essere
specificate nell’XML;

• Focus: una delle proprietà dinamiche. Si può impostare manualmente su una specifica vista,
chiamando il metodo requestFocus della stessa. Esso è il destinatario degli eventi tastiera
(la finestra extra dove compare la tastiera, che permette di generare degli eventi, poiché
manca la tastiera fisica).

• Listener: le viste devono avere dei listener, oggetti che si collegano dinamicamente e a cui
vengono recapitate informazioni quando viene svolta un’azione sul widget stesso (es. classe
button).

• Visibilità: un altro attributo dinamico delle viste. Ogni vista può essere mostrata o nascosta
usando la proprietà visibility, che può avere tre valori: [Link], [Link] (non è
disegnata, ma le rimane lo spazio dedicato), [Link] (non è disegnata, ma non
partecipa alla misura dello spazio).

Focus

La vista interattiva che riceve l’input dell’utente possiede il Focus. Una sola finestra può possedere
il focus: serve a rendere esplicito chi riceva gli eventi di tipo tastiera. Il focus può essere assegnato
in vari modi: facendo tap su una vista; trasferendo automaticamente il focus al prossimo campo di
ingresso alla pressione di invio, tab, o mediante le frecce; programmaticamente, invocando il
metodo requestFocus su una vista qualsiasi.

La differenza tra clickable e focusable è che il primo è una vista che reagisce ad un tocco
dell’utente, mentre il secondo è una vista che acquisisce il focus al tocco. I dispositivi di ingresso
come la tastiera (virtuale) inviano i loro eventi alla vista che possiede il focus.

La vista che acquisirà il focus sarà:

- quella più in alto nel punto in cui è avvenuto un tocco,

- alla pressione di invio, il focus passa al controllo più vicino (da sx a dx, dall’alto in basso);

- interagendo con un controllo direzionale si può passare il focus ad un altro widget.

È opportuno, per guidare l’utente, indicare visivamente quale vista possiede il focus per renderlo
esplicito. Inoltre occorre indicare quali viste possono avere il focus, per aiutare a navigare tra i
controlli.

Il focus deve essere prevedibile e logico. Per guidare il focus si possono disporre i controlli da
destra a sinistra, dall’alto in basso, nell’ordine con cui si vuole far transitare il focus. I controlli
interattivi vanno disposti in un contenitore intermedio. L’ordine può essere reso esplicito tramite gli
appositi attributi.

Elemento Spinner: Offre la possibilità di scegliere un dato da un elenco. L’elenco viene creato
usando un oggetto di tipo Adapter. Devo crearmi il meccanismo base, con un array di parole che
voglio compaiano.

Elemento Button: Come sappiamo è possibile associare un testo al controllo. Si può associare un
evento alla pressione del bottone tramite il metodo setOnClickListener.

Elemento Checkbox: Possono essere selezionate una o più opzioni (o zero). Selezionare un’opzione
non deseleziona nulla. Gli utenti si aspettano una disposizione in colonna, solitamente seguita da
un bottone di invio/conferma. Ciascuna checkbox è una View e può avere il proprio gestore
dell’evento onClick.

ToggleButton e Switch: Il gestore del click è uguale a quello dei bottoni. Diversamente dalle
checkbox, mostrano un testo che cambia in funzione dello stato corrente (on/off).

RadioGroup e RadioButton: simile al checkbox, ha un’etichetta di testa associata ed uno stato (of/
off). I radio group permettono di raggruppare un insieme di radio button, in modo tale che solo
uno di questi sia ON. Su di esso si può impostare il metodo setOnCheckedChangeListener.

Indicatori di progresso: Sono usati per mostrare all’utente che l’applicazione sta svolgendo
qualcosa. La barra di progresso standard è tonda e animata, usata per azioni di tempo
indeterminato. Posso scegliere tra tre dimensioni. Posso associare una callback al controllo. Una
RatingBar permette all’utente di esprimere un’opinione di valore o consultare una valutazione
altrui.

Stili e temi: uno stile è una raccolta di attributi che specificano l’aspetto grafico di una vista. Lo
stesso stile può essere applicato a più viste per garantire la coerenza. Gli stili vengono memorizzati
come risorse, nella cartella res/value. Un elemento di stile contiene: nome univoco, indicazione
dello stile da cui deriva (parent, opzionale), un numero qualsiasi di item sotto forma di coppie
nome/valore.

GESTIONE DELLE LISTE

La visualizzazione delle liste è una delle cose più frequenti che avvengono su Android. Esse hanno il
difetto di essere arbitrariamente grandi, e quindi di difficile gestione, poiché la struttura dell’albero
potrebbe richiedere molta memoria e molti cicli macchina (per lo più inutilmente perché la
maggior parte delle cose della lista non sarebbe visibile). In fase di progettazione non è possibile
sapere quanti elementi sono presenti nella lista, poiché nella maggior parte dei casi essi vengono
prelevati da un server. Quindi non possiamo sapere a priori come strutturare la vista.
Istanziare dinamicamente un albero di viste solitamente non è una buona soluzione, perché se il
numero di voci da mostrare è grande, la memoria da allocare è enorme e consumo troppe risorse.

L’approccio deve essere diverso: sono state rese disponibili classi specializzate per risolvere questo
problema (ListView, GridView, Gallery…). Tutte queste derivano da AdapterView, che delega
all’adapter la responsabilità di gestire la visualizzazione dei dati. Ora l’uso di queste classi è
sconsigliato → sostituite con RecyclerView.

RecyclerView

Di solito solo un sottoinsieme limitato di voci possono essere mostrate sullo schermo. Invece di
creare tante sotto-viste quanti sono gli elementi della collezione, è possibile crearne solo tante
quante possono essere mostrate sul display + una: la vista fantasma (quando scrollo, la vista
corrente scompare e abbiamo spazio per una vista nuova, che viene popolata con i dati del +1).
Quest’ultima vista diventa parte della visualizzazione soltanto nel momento in cui comincio a fare
scroll (verso l’alto o verso il basso). Quando ciò avviene, sul lato opposto della lista un’altra voce
diventerà invisibile e prenderà il ruolo di vista fantasma.

Questo principio mi permette di riciclare le viste. Può essere applicato anche a viste di tipo griglie,
o griglie non uniformi (staggered grid): occorre soltanto avere un numero maggiore di viste
fantasma. Questo approccio migliora molto le prestazioni perché evita di allocare memoria in
grandi quantità e di consumare inutilmente la batteria.

Questo però non basta, perché questa vista non sa come sono fatti i dati da contenere. Perciò la
RecyclerView deve appoggiarsi ad un oggetto che dobbiamo costruire noi, nel quale memorizziamo
i dati da mostrare e come devono finire sullo schermo → la classe responsabile di ciò è la
[Link]. L’Adapter ha la responsabilità di creare le poche viste iniziali e di fare
riferimento ad esse tramite oggetti di tipo ViewHolder, che hanno la responsabilità di facilitarci
l’accesso alle parti interne contenute.

Quando istanziamo l’Adapter, esso mette a disposizione tre metodi: uno che dice quanti elementi
ho nella lista (per capire fino a dove posso scrollare), uno che è responsabile di istanziare un nuovo
ViewHolder, e l’ultimo responsabile di popolare il ViewHolder con i dati dell’i-esima voce della
struttura dati. I dati necessari per la RecyclerView possono essere messi in un array, in una lista…

Abbiamo inoltre bisogno di una collezione di dati per riempire la classe Adapter: questi possono
anche essere più sofisticati, e quindi è necessario inserirli in una classe dedicata. È poi necessario
preparare un layout per le singole Card della recycler view.

Sostanzialmente quindi la classe Adapter, che deriva dalla Recycler View, ha 3 responsabilità, con 3
rispettivi metodi:

 mantenere un riferimento alla collezione dei dati da mostrare ed offrire un metodo per
sapere qual è la loro cardinalità;

 supportare l’istanziazione di un nuovo viewholder e del corrispondente albero visuale;


 riempire il viewholder con i dati presenti nell’i-esimo elemento.
FRAMMENTI

La gestione dell’interfaccia può essere molto complessa, perché bisogna progettare per una grande
varietà di dispositivi. Per questo motivo è stato introdotto il concetto di Fragment, che rende più
adattabili le interfacce grafiche. Essi sono pensati per incapsulare gruppi di componenti funzionali
ad un dato scopo, così da facilitarne il riuso, nascondendo la complessità di gestire l’interazione
con l’utente all’interno del componente e permettendo un alto livello di personalizzazione ed
integrazione con altri componenti che possono essere attivi contemporaneamente o meno. Un
esempio tipico sono le mappe, i carrelli della spesa…

Questo permette una progettazione modulare del codice, migliorandone l’organizzazione e la


manutenzione. I frammenti sono riutilizzabili, sia tra activity diverse che nella stessa istanza. Sono
inoltre adattabili per le diverse interfacce, permettendomi di decidere come combinare le diverse
parti. Sono parte della libreria Jetpack, che è un’estensione di Android.

Concettualmente sta a metà tra un Activity ed una View: come un’Activity, ha un ciclo di vita
complesso, scandito da numerose callback. Al suo interno incapsula una gerarchia di viste, ma
come view può essere inserito direttamente in xml.

Per poterli usare dobbiamo creare una classe derivata per implementare uno specifico
comportamento. A differenza delle attività, i fragment sono manipolati direttamente dal
programmatore. Agisce da segnaposto per i vari frammenti la fragmentContainerView. L’attività
sarà poi responsabile di istanziare i frammenti richiesti e visualizzarli all’interno del proprio
segnaposto.

Durante il proprio ciclo di vita, un frammento può essere in uno dei seguenti macro stati:

 Esistente come oggetto, ma slegato da qualunque attività;

 Legato ad un’attività, ma non visibile;

 Legato ad un’attività e visibile.

Ogni volta che un frammento cambia il proprio stato, il sistema “glielo dice” e i cambiamenti
dell’activity si ripercuotono sul frammento. Per questo motivo le potenziali azioni di callback sono
molte. Se un fragment è scollegato, non fa parte dello stato complessivo dell’applicazione e viene
quindi ignorato. Quando connesso, segue l’evoluzione dello stato della sua attività. Le notifiche
saranno inviate subito prima o subito dopo le corrispondenti dell’activity. Possono anche esserci
notifiche esclusive per il fragment.

La visualizzazione del contenuto di un frammento può dipendere da cosa avviene in un altro


frammento. L’attività che li include può agire da controllore comune. I fragment non devono sapere
nulla l’uno dell’altro, ma devono parlare soltanto con l’activity che li contiene. Per generalità, si
definisce un’interfaccia che espone i possibili eventi: l’attività implementa tale interfaccia e si
occupa di invocare gli opportuni metodi sui diversi frammenti. Perché un fragment sappia a che
activity fare riferimento si utilizza la proprietà activity.
CICLO DI VITA DI UN FRAGMENT

Ciclo di vita complesso, riceve notifiche legate al ciclo di vita dell’attività. Non c’è onRestart(). Ci
sono poi notifiche proprie dei frammenti, che:

 notificano che il fragment è associato ad un’attivita o torna ad essere libero;

 richiedono al frammento di creare/rilasciare la propria vista;

 segnalano che l’attività a cui si è associati ha terminato la propria creazione.

È un meccanismo elaborato.

Figura 1. CASO 1
Se un frammento viene dinamicamente
aggiunto/rimosso da un’attività (cioè non vive per
tutta la durata dell’attività, come nel caso appena
descritto), i diagrammi cambiano, poiché un
frammento viene staccato e deve essere sostituito
(es. se clicco sulla x). In questo caso i due cicli di
vita sono debolmente interconnessi: il ciclo di
costruzione/distruzione di un frammento può
risultare completamente contenuto in una singola
fase del ciclo di vita dell’attività.

Figura 2. CASO 2
Quando viene creato un frammento fin dall’inizio, esso non è percepito dall’utente come tale, ma
come parte della schermata iniziale. Se esso viene cambiato dinamicamente in seguito ad
un’interazione, il cambiamento si nota e può anche essere sottolineato grazie ad un’animazione. La
presenza di una nuova schermata suppone che l’utente, tramite il tasto back torni allo stato
precedente, ma non è così, perché non si tratta di activity appartenenti allo stack.
MATERIAL DESIGN

È un insieme di linee guida per la progettazione, nato inizialmente per Android, ma che poi è
diventato uno stile generale, un linguaggio visivo che mette insieme principi fondamentali del
graphic design con le opportunità tecnologiche che rendono possibili l’interazione con l’utente.

Fa leva sui principi di minimalismo, consistenza (ha linee guida standard) e razionalità (perché imita
il mondo reale). Nasce dalla metafora della materia: quello che vediamo sullo schermo è il risultato
di una realtà nella quale intervengono materiali illuminati, che possono quindi proiettare ombre
(bidimensionali, ma di fatto tridimensionali). Le superfici ed i bordi forniscono suggerimenti visivi
legati alla realtà, le luci/ombre, superfici/movimenti forniscono indicazioni sulle relazioni
reciproche tra i componenti visivi (dominanza, subordinazione…). Questo aggiunge elementi in più
rispetto all’utilizzo unico di font, colori…

I componenti devono comportarsi come nel mondo reale: occupano spazio, proiettano ombre,
interagiscono tra loro.

Fa leva su alcune linee guida fondamentali: devono esserci colori (arbitrari, solitamente colore
principale e d’accento, molto diversi tra loro), lo schermo deve essere riempito da bordo a bordo,
caratteri di grandi dimensioni, gli spazi vuoti sono intenzionali per “aiutare” la Gestalt e evidenziare
la separazione degli elementi, le azioni sono enfatizzate e si fa il possibile per rendere chiaro quali
sono le funzionalità delle diverse componenti.

Elementi dei linguaggi visuali

IMMAGINI: aiutano a comunicare e differenziare un’app. Devono essere rilevanti, informative,


belle. Si suggerisce di accostarle ad un testo, scegliere soggetti originali, fornire un punto focale,
costruire una narrazione visiva.

TIPOGRAFIA: scelta del font, delle sue dimensioni, del peso, la spaziatura… Propone come font
standard Roboto, che possiede vari pesi. Bisogna ricordare che troppe dimensioni di font
confondono, bisogna scegliere un set limitato ed armonioso.

Il formato dei testi può essere definito come risorsa dell’app, ma si può anche inserire
direttamente uno stile, definito da Android, per velocizzare le cose. Possono anche essere scaricati
dal web, in modo da ridurre le dimensioni dell’APK e migliorando l’uso delle risorse fisiche di un
dispositivo (se le dimensioni dell’app sono piccole c’è più possibilità che essa venga scaricata).

COLORI: si raccomanda l’uso di:

 un colore primario,

 un insieme di tonalità derivate,

 un colore d’accento (lontano dal primario).

Android Studio crea automaticamente una palette. Il colorPrimary viene usato tipicamente nella
AppBar, mentre il colorPrimaryDark nella status bar ed il colore d’accento deve attirare l’attenzione
dell’utente (floating action button, switch…).

Si usa il contrasto di colori per separare i contenuti ed aumentare la leggibilità e l’accessibilità. Il


testo deve essere in contrasto con lo sfondo, prestando attenzione al daltonismo. I temi standard
gestiscono il colore del testo. Il più delle volte si usa il bianco sporco ed il “quasi nero”, apposta non
si usano i colori puri per dare un impercettibile spostamento verso una tonalità di colore, che avrà
un impatto psicologico per una persona.

MOVIMENTO: descrive le relazioni spaziali, le funzionalità e le intenzioni. Deve essere responsivo,


naturale, conscio ed intenzionale.

Serve a mantenere una continuità d’azione, riesco a vedere come due rappresentazioni sono
legate. Evidenzia elementi o azioni, crea transizioni naturali tra stati, indica il focus, sottolinea le
transizioni, fornisce un feedback.

USO DEGLI SPAZI: per dividere elementi di “gruppi” diversi. Le dimensioni sono espresse in dp,
tranne quelle dell’altezza del testa che vanno espresse in sp. Gli elementi si allineano su una griglia
con spaziatura razionale, non si dispongono a caso. Così potrò capire la divisione tra gli elementi.
Tendenzialmente le spaziature vengono in multipli di 8dp o 4dp. Esistono template per i pattern
più comuni (es. lista).

Servizi forniti dal Material Design

Per supportare lo stile sono state introdotte nella libreria diverse classi le cui funzionalità sono per
la maggior parte disponibili in modalità compatibile con le versioni precedenti (bisogna configurare
il progetto).

Uno stile è un gruppo di particolari proprietà che specificano l’estetica di una view. Un tema è una
collezione di stili che vengono applicati ad un’intera activity o applicazione e non solo ad una vista
(es. dark/light).

Toolbar: è una generalizzazione dell’action bar, più flessibile, che la può sostituire. È un contenitore
grafico, può avere un bottone di navigazione, un’icona, un titolo/sottotitolo, una o più viste custom
ed un menu a tendina (con azioni frequenti/importanti/tipiche e possibile continuazioni per voci
ulteriori). L’ordine tipico è frequent -> important -> typical.

Android è già progettato per strutturare dinamicamente la toolbar, mettendo le icone che ci stanno
ed il resto nel menu espandibile.

Card: vista particolare di grande impatto grafico, volta a racchiudere la descrizione e le azioni che si
possono effettuare su un determinato elemento. Raggruppano contenuto e azioni relative ad un
singolo argomento.

Il floating action button abilita l’azione primaria di un’app: è basato su un’icona rotonda, sollevata
dal resto della pagina. Viene usato per azioni frequenti, oppure permette di visualizzare una serie
di azioni collegate, o di aggiornare o trasformare gli altri elementi visivi della pagina.
Campi di testo: permettono all’utente di inserire e modificare il testo. Vengono in due versioni:
riempiti o bordati. Supportano testo con formattazioni ricche.

Snackbar: widget temporanei per mostrare brevi notifiche al fondo dello schermo. Spariscono
automaticamente dopo un certo intervallo di tempo, o se spazzati via. È possibile legare un’azione
ad una snackbar, ad esempio per riprovare un’azione fallita o disfarne una appena chiuda.

Chip: usati per rappresentare un’entità complessa in poco spazio (es. un contatto, una parola
chiave…). Contengono un’etichetta, un’icona iniziale e un tasto di chiusura opzionale.

Cassetto di navigazione: contiene un Drawer Layout ed un Navigation View, il primo con contenuti
arbitrari e la seconda è una finestra di dialogo per mostrare dei collegamenti in-app. Contiene
un’intestazione, di solito con grafica e un menu, dove ciascuna voce rappresenta una diversa
schermata verso cui navigare.

IL PROGETTO JETPACK

Android fase 2: introduzione di Android studio, utilizzo di Gradle come gestore del processo di
compilazione e messa in campo, introduzione del material design, ricchezza di consumazione,
grande abbondanza id librerie di terze parti.

Android fase 3: basato sulle librerie Jetpack (per riorganizzare le librerie terze parti). Vengono
introdotti pattern architetturali. Kotlin diventa il linguaggio principale, la gestione degli eventi
asincroni è fatta con RxJava e coroutine.

Il progetto Jetpack è un insieme di componenti, librerie e strumenti messi a disposizione degli


sviluppatori con l’obiettivo di:

 accelerare i tempi di sviluppo,

 eliminare il codice ripetitivo,

 costruire app robuste e di elevata qualità.

Copre 4 aree principali con le sue librerie:

 Jetpack Foundation: copre ambiti comuni a tutte le app (es. compatibilità con il passato,
controllo di qualità, valutazione delle prestazioni…). Esistono poi contesti d’uso particolari,
come l’integrazione con i veicoli o i dispositivi indossabili. Abbiamo poi aspetti legati allo
sviluppo, cioè all’utilizzo di Kotlin ed alla gestione di grossi progetti con Multidex.

 Jetpack Architecture: classi e modelli concettuali per strutturare un’applicazione affinché


sia robusta, testabile e manutenibile (es. gestione delle schermate, gestione del ciclo di
vita, aggiornamenti dinamici delle viste…);

 Jetpack Behavior: integrazione con i servizi standard offerti dalla piattaforma di esecuzione
(es. scaricamento contenuti in background, gestione notifiche, permessi, preferenze,
condivisione di contenuti…).
 Jetpack UI: componenti grafici e classi di ausilio che permettono di creare interfacce con
una UX sofisticata (es. gestione di animazioni e transizioni, uso di emoticon, frammenti,
disposizione grafica, uso di palette di colori dinamiche…).

Componenti architetturali -> Le app Android sono costruite unendo un insieme di componenti
gestiti direttamente dal sistema operativo: attività, servizi, content provider, ricevitori di eventi
broadcast. I singoli componenti possono essere attivati in modo indipendente e in sequenze
diverse da quelle che il progettista si aspetta. Possono essere distrutti in qualsiasi momento, come
conseguenza di eventi di sistema o richieste dell’utente.

Implicazioni del ciclo di vita -> Occorre evitare di memorizzare al suo interno dati dell’applicazione
o informazioni sul suo stato. Inoltre, nessun componente deve dipendere dall’esistenza in vita di un
altro componente. Eventuali azioni asincrone (come l’interazione con un server remoto) sono
particolarmente problematiche. Quando arriva una risposta, il componente che ha causato la
richiesta potrebbe non esistere più.

Suddivisione delle responsabilità -> Le attività e i frammenti sono responsabili di creare e


aggiornare l’interfaccia utente ed interagire con il sistema operativo. Questi oggetti possono essere
distrutti in qualsiasi momento dall’utente o dal sistema operativo. Le informazioni visualizzate
dovrebbero essere l’immagine di un modello, esterno a tali classi e possibilmente persistente. In
questo modo non vengono persi dati se l’app viene distrutta o se ci si trova in presenza di
connessioni di rete instabili.

Per queste problematiche comuni sono state date varie risposte che hanno condotto a tre pattern
architetturali: MCV (Model-View-Controller), MVP (Model View Presenter), MVVM (Model View-
View Model). Quello raccomandato da Google è l’ultimo.

ARCHITETTURA MVVM

Blocchi base:

 Model: contiene i dati che l’applicazione tratta e tutti i metodi necessari per manipolarli;

 View: la parte dell’interfaccia utente responsabile di presentare i dati dell’utente all’utente


e raccogliere le sue interazioni;

 ViewModel: agisce da ponte tra modello e vista, reperendo nel modello i dati necessari e
trasformandoli in un formato adatto ad essere usato dalla vista.

View e ViewModel sono connessi tramite DataBinding e LiveData osservabili.


Vantaggi:
 Disaccoppia il codice del progetto, separando la logica di business dalla presentazione;

 Facilita la manutenzione: la vista non ha percezione della computazione alle sue spalle e
rende il codice più testabile;

 Dà una struttura al codice e rende più facile orientarsi tra le classi perché ciascun blocco ha
responsabilità specifiche ed è facile aggiungere/togliere funzionalità.

Modelli: Strutture dati volte a rendere persistenti i dati applicativi. Devono essere indipendenti
dalle viste e da qualsiasi componente (attività/frammento), così da essere isolati rispetto alla
gestione del relativo ciclo di vita. Separare il codice dell’interfaccia da quello applicativo facilita la
manutenzione e permette il test dell’app.

Aggiornamenti asincroni: lo stato contenuto nel modello evolve come conseguenza di diverse
cause, alcune locali all’app, altre legate ad eventi remoti. In molti casi questi aggiornamenti sono
asincroni: possono avvenire con ritardi variabili, entrando potenzialmente in conflitto con il ciclo di
vita dei componenti.

LiveData<T>: è un contenitore dati generico, osservabile. Invia ai propri osservatori una notifica se
il dato contenuto al suo interno cambia. È rispettoso del ciclo di vita. Solo gli osservatori in uno
stato “attivo” ricevono tali notifiche.

Vantaggi: evita perdite di memoria e crash dovuti ad attività terminate. Non richiede la gestione
manuale del ciclo di vita dei componenti. I dati sono sempre aggiornati. Gestisce correttamente i
cambi di configurazione e facilita la condivisione di risorse.
ViewModel

Fa da ponte tra il modello e la vista. Progettato per memorizzare e gestire i dati relativi alle UI,
tenendo conto del ciclo di vita dei componenti coinvolti. I dati sopravvivono ai cambi di
configurazione (es. rotazione), permettendo così di semplificare la creazione delle classi. Non deve
mai referenziare una vita, un ciclo di vita, o qualsiasi classe che potrebbe avere una referenza del
contesto dell’activity.

È l’oggetto in cui conservare lo stato della vista:


può offrire dei LiveData immutabili per
osservare i dati. Ma essi vengono implementati
come Mutable, per consentire al ViewModel di
aggiornare il proprio stato.

Cosa NON deve stare nel ViewModel:


qualunque cosa che faccia riferimento ad
un’activity o ad un contesto, quindi -> viste,
attività, frammenti, adapter… Se serve
manipolare delle risorse, bisogna soltanto
memorizzarne l’id, lasciando alla vista il
compito di caricarle.

I ViewModel devono essere costruiti in


riferimento ad uno scope (frammento, attività o
grafo di navigazione) e restano attivi finché il
loro scope è vivo.

Gestire la navigazione

Android supporta diversi tipi di navigazione:

 Temporale: tasto back. Navigazione all’indietro -> Comportamento di base, supportato


dallo stack delle attività. Il metodo principale può essere sovrascritto per modificare il
comportamento (solo se occorre soddisfare particolari casi d’uso). Usando i frammenti, è
possibile usare un metodo per simulare il comportamento della stack.;

 Gerarchica: generico/specifico, su/giù. Navigazione gerarchica -> schermata genitrice


permette di navigare verso le schermate figlie (di solito è la schermata principale), le
schermate figlie possono essere implementate come frammenti e mostrati a lato o sotto la
schermata principale, se la dimensione del display lo permette. Deve essere implementata
in modo esplicito;

 Tra viste sorelle: tab multipli, a scorrimento;


 Basata su un cassetto.

Nella maggior parte dei casi è utile avere i frammenti.

Per fare manualmente questa gestione gerarchica, storicamente era possibile spezzare il flusso di
interazione dell’utente in diverse activity, ognuna orientata ad uno specifico sotto-compito. Questo
portava ad una grande complessità nella gestione dei dati condivisi e nella riattivazione a seguito di
un cambiamento del task.

Anche i frammenti permettono di modificare la vista corrente e gestire la navigazione tra


schermate diverse: la complessità del loro ciclo di vita e dell’interazione costituisce un ostacolo ed
una fonte di possibili errori. Per gestire semplicemente la navigazione è stata creata la libreria
Navigation.

UX DELLA NAVIGAZIONE

Un’app deve avere una schermata di partenza nota e fissa (home screen, activity o frammento), poi
si userà uno stack per rappresentare lo stato di navigazione dell’app.

Il bottone “up” non fa mai terminare l’app. Back e up sono equivalenti nel contesto di uno specifico
task, interno all’app. Se viene attivata una schermata dell’app tramite un link esterno (detto deep
link), lo stato di navigazione dovrà essere lo stesso se si è raggiunta quella schermata navigando
normalmente (tramite il link devo atterrare sulla pagina desiderata).

Viene creato un grafo di navigazione per la struttura dell’app, che può essere descritto come
risorsa XML e viene contenuto nella cartella res/navigation. Definisce la navigazione tra le
schermate. Può essere implementato tramite il Navigation editor, aggiungendo, togliendo e
collegando possibili destinazioni. Permette di associare le varie schermate ed inserire punti di
ingresso in profondità (deep link) per accedere a singole schermate dell’app (es. lanciare la
fotocamera da WhatsApp).

Le singole schermate sono composte da frammenti o activity: si usano fragments per funzionalità
interne all’app e activity per accedere ad altre app.

Le attività che ospitano la navigazione interna hanno un layout in cui sarà presente un frammento
di tipo NavHostFragment: al suo interno verranno ospitati i frammenti che descrivono le singole
schermate. Un oggetto di tipo NavController si occuperà poi di gestirne l’istanziazione e la
visualizzazione a seguito degli eventi di navigazione.
PROGRAMMAZIONE LATO SERVER

Node JS -> motore di esecuzione Javascript open-source, multipiattaforma. È basato sulla


programmazione asincrona, basata su callback e promesse, che ricalca il comportamento di molte
API dei browser. Ha un gran numero di librerie disponibili che offrono soluzioni preconfezionate
per la maggior parte dei problemi di programmazione lato server.

Analogamente a quanto avviene in un


browser, il codice Js viene eseguito in
un ciclo continuo: quando viene
eseguito il programma, le istruzioni
immediate contenute al suo interno
sono eseguite nell’ordine indicato. Se
arrivano operazioni che potranno
avvenire in futuro, NodeJS si mette in
attesa che tali eventi si verifichino e,
al momento opportuno, invoca le
funzioni indicate nel programma.

È progettato per favorire la


modularità del codice. Le funzionalità da sviluppare sono ben separate dal punto di vista logico e la
loro implementazione viene effettuata in file sorgenti distinti, tra loro indipendenti e riusabili. Un
modulo può essere tutto ciò che sta tra un singolo file sorgente, scritto direttamente dal
programmatore, ed un’intera libreria sviluppata da una terza parte. Dividere un progetto in moduli
lo rende più leggibile e comprensibile (un singolo modulo dovrebbe essere molto focalizzato verso
un task specifico, essere di poche righe). Un modulo può essere riutilizzato tra applicazioni diverse.
Nel caso più semplice è costituito da un singolo file, presente nella cartella del progetto (o in una
sottocartella).

Quando il modulo viene caricato, il codice immediato al suo interno viene eseguito ed eventuali
callback vengono registrate per essere successivamente eseguite.

Spesso i moduli usati non sono scritti direttamente dal programmatore, ma trovati in rete tra i
molti disponibili per lo svolgimento di un dato compito (per fare ciò si usa il NodeJS Package
Manager).

PROTOCOLLO HTTP (HYPER TEXT TRANSFER PROTOCOL)

Tutta la comunicazione tra client e server avviene tramite il protocollo http, basato su scambi di
messaggi detti rispettivamente richiesta e risposta. La richiesta si origina nel client (get, put…) e
contiene un insieme di metadati sotto forma di coppie Nome:Valore. In base al verbo, può
contenere o meno dei dati (es. body).

La risposta viene prodotta dal server e contiene un codice di stato a 3 cifre. Contiene un insieme di
metadati sotto forma di coppie Nome:Valore e può contenere un corpo. Quando un browser carica
una pagina la cui URL comincia per “http:” o “https:”, provvede automaticamente a contattare il
server indicato ed usa il corpo della risposta in sostituzione del documento corrente.

Elaborazione remota -> l’interazione con il server presenta molteplici possibilità:

 Raccogliere e memorizzare in modo permanente le informazioni prodotte dall’utente

 Permettere ad un utente di accedere alle proprie informazioni attraverso dispositivi e


browser differenti

 Permettere la comunicazione tra utenti diversi (mediata dal server)

 Accedere a contenuti non disponibili lato client…

Chi sviluppa il codice lato server prepara una serie di funzionalità che vengono messe a
disposizione di chi sviluppa il codice lato client, e prendono il nome di API (Application
Programming Interface). Ogni API viene mappata su una URL specifica e richiede l’uso di un certo
verbo http e l’invio di eventuali dati. Se il verbo è GET, i dati sono codificati all’interno della url
dopo un ?, altrimenti sono inviati nel corpo della richiesta usando la codifica riportata
nell’intestazione “content-type”.

Struttura di un server http -> tra le librerie base è presente il modulo http, che contiene le
funzionalità base necessarie per avviare un server e gestire le singole richieste, coerentemente con
il modulo http. Il server che si ottiene da questo modulo è minimale: è compito del programmatore
gestire tutte le richieste, indipendentemente dal loro contenuto.

Un applicativo server deve distinguere le richieste ricevute, in base a: metodo, URL, parametri di
richiesta, sessione, utente. E produrre la risposta conseguente in base alla logica che intende
implementare: reperendo le informazioni richieste o acquisendo le informazioni ricevute per una
successiva elaborazione; costruendo una rappresentazione del dato da restituire.

Sebbene sia possibile fornire al modulo http una callback che risponde a tutti questi requisiti,
spesso si preferisce NON farlo. La complessità di tale funzione la renderebbe praticamente non
manutenibile, impedendo di fatto la messa in campo di un server. Lo sviluppo di applicativi lato
server tende a basarsi su appositi moduli che favoriscono la gestione della complessità
(suddividendo le diverse responsabilità in funzioni più semplici).

La Libreria EXPRESS: È il framework più diffuso per realizzare applicazioni web basate su NodeJS:
enfatizza un approccio minimale e fortemente modulare, facilmente configurabile ed estendibile.
Esporta, come unico valore, una funzione che quando eseguita crea l’infrastruttura per la
configurazione e la gestione di servizi web.

L’oggetto applicazione mantiene al proprio interno una catena di gestori: oggetti specializzati nel
verificare se una data richiesta sia o meno conforme alle proprie attese e, nel caso, eseguire una
funzione che genera la risposta. La catena si configura invocando, sull’oggetto applicazione, i
metodi get, post, put, delete, use. Tali metodi restituiscono l’oggetto app e possono essere
chiamati in cascata. In fase di esecuzione vanno attivati nell’ordine in cui sono stati nominati.
Servire contenuti statici -> spesso, un server deve semplicemente mettere a disposizione del client
una serie di file così come essi si presentano (fogli di stile, script, immagini…). Il modulo
[Link] supporta questo tipo di attività: quando giunge una richiesta, cercherà se la URL
relativa corrisponde ad uno dei file contenuti nella sottocartella. In caso affermativo, provvederà a
terminare la richiesta con il contenuto relativo. Se non viene trovata nessuna corrispondenza, il
controllo prosegue con i successivi gestori.

Servire contenuti dinamici (popolare una vista) -> le funzioni responsabili di creare la singola vista
a fronte di una richiesta URL+http sono i controllori. Permettono di separare la logica delle varie
funzionalità offerte dal servizio. Possono usare sia le informazioni contenute nella richiesta che
quelle presenti in un database per generare la risposta.

Richieste -> modo con cui il client richiede qualcosa al server. Ogni richiesta contiene un verbo http
(get, post…) e una URL (descrive l’azione, es. login). Nella URL possono essere presenti parti
variabili. Indicano con maggiore precisione su quale informazione debba essere eseguita l’azione.
Tutte le richieste sono accompagnate da alcune righe di intestazione, con la forma
<chiave>:<valore>. L’insieme ed il significato delle intestazioni è stabilito dal protocollo http. Le
richieste Post e Put hanno un corpo associato, che permette di trasferire al server un dato di tipo
arbitrario. Le richieste Get possono ricevere dei parametri all’interno della query string.

Elaborazione di un form -> permettono di raccogliere informazioni strutturare dagli utenti, che
vengono poi elaborate lato server. L’elaborazione avviene in un processo a 4 fasi:

1. Il browser mostra una pagina contenente un form

2. L’utente compila i campi richiesti e preme il tasto invia

3. Il browser crea una richiesta al server verso la URL indicata nell’attributo action del form
usando il metodo indicato nell’attributo method.

4. Il server esamina i dati ricevuti e invia una risposta opportuna che sarà visualizzata nel
browser.

Risposte -> il server risponde al client inviando un codice di stato a 3 cifre (che indica se e come la
richiesta è stata accettata), una serie di metadati (header, descrivono alcune proprietà del corpo,
conformemente allo standard http) e un corpo principale (body, può essere di qualsiasi tipo, video/
audio, html, testo…).

Gestire le sessioni

I cookie sono delle informazioni mandate dal server, che si salvano sul browser per un certo
periodo (es. ID univoco che rappresenta il client). Il modo più semplice per identificare un certo
client e gestire lo stato della comunicazione client-server è una sessione. Per poter funzionare, le
sessioni hanno bisogno che il browser mantenga al suo interno (e invii ad ogni richiesta)
un’informazione, che solitamente consiste di un cookie che contiene un ID. Quando il server riceve
una richiesta che contiene il cookie, con l’ID della sessione, ne verifica l’autenticità e lo usa per
leggere le informazioni relative allo stato del client. I dati delle sessioni attive possono essere
salvati nella memoria del server o in una base di dati.

Potrebbero piacerti anche