Creare sfondi e forme con Shape Drawable
Saper personalizzare la grafica di un'applicazione è molto importante. In questo tutorial vedremo come definire degli elementi grafici che non siano vere e proprie immagini, direttamente all'interno di un file xml greazie a shape
Una delle richieste più frequenti che ricevo dai miei clienti è la possibilità di creare delle forme, cornici, sfondi da applicare ad alcuni elementi del Layout (View), al fine di renderli più accattivanti dal punto di vista grafico.
Basti pensare ad un elenco ListView e alla possibilità di applicare ad ogni riga uno sfondo, un gradiente, un bordo colorato etc, oppure ad un bottone a cui si voglia applicare un bordo più o meno arrotondato oppure uno sfondo con effetto gradiente, oppure una linea che separi due sezioni del layout di una nostra Activity e così via.
Come avviene nel mondo delle pagine web, che grazie ad alcune regole dei fogli di stile css, come border, gradient, width, height, background etc è possibile creare delle forme con sfondi e bordi, anche in Android è possibile disegnare delle forme elementari, come rettangoli, cerchi, bordi e di applicare, non solo colori di sfondo uniformi, ma anche gradienti, grazie ad una serie di tag da inserire all’interno di un file xml sotto la cartella delle nostre risorse drawable.
Nei tutorial precedenti abbiamo già visto l'importanza della cartella res e abbiamo visto cosa inserire dentro alla sottocartella layout e value. La cartella drawable, con le diverse tipologie in base alla dimensione del dispositivo, è quella che ci permette di memorizzare all'interno sia le immagini che dovranno apparire costantemente nella nostra applicazione, ad esempio le diverse icone associate ad un particolare menu o ad una riga, sia file xml opportuni, grazie ai quali è possibile ricreare delle forme,disegni, sfondi colorati, animazioni etc.
Cosa imparerai
In questo tutorial vedremo di introdurre quegli elementi che ci permetteranno di disegnare sullo schermo linee, rettangoli, cerchi e di applicare a queste dei colori uniformi o con gradiente, in modo da poterli usare per personalizzare gli elementi di un’interfaccia utente UI.
- Capirai a cosa server il tag <shape> e come si creano aree rettangolari, circolari, linee
- Imparerai a creare sfondi con gradiente grazie al tag <gradient>
- Imparerai a impostare dei bordi arrotondati con il tag <corners>
- Imparerai a creare linee di separazione colorate con il tag <stroke>
- Imparerai a creare uno spazio di separazione dal bordo con <padding>
Che strumenti abbiamo in Android per disegnare?
Putroppo Android non è Photoshop, quindi anche disegnare una semplice linea, richiede la scrittura di qualche riga di codice, esattamente come avviene con le pagine web.
Fortunatamente, quest’attività è diventata relativamente semplice, in quanto molti siti web offrono delle interfacce, grazie alle quali è possibile creare in automatico il codice necessario per disegnare le sezioni di una pagina che vogliamo personalizzare.
Partiamo con una delle prime esigenze di chi vuole proporsi alle aziende come progettista di applicazioni: riuscire ad impostare il colore di sfondo sfumato di uno specifico layout, da sostituire al classico bianco o nero, tipici degli stile predefiniti in Android.
Esempio: Cambiare il colore di sfondo, applicando una sfumatura
Proviamo prima di tutto a fare una prova. Copia il codice che vedi qui sotto, all'interno di una qualsiasi delle cartelle Drawable, dandogli il nome sfondosfumato.xml:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:startColor="#99CC00" android:endColor="#2C8008" android:angle="90.0" /> </shape>
Poi crea un classico Linear layout, e imposta l'attributo background a sfondosfumato (il nome usato in precedenza), in questo modo:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/sfondosfumato" </LinearLayout>
Quella che otterrai ottenuto è rappresentato in figura b.
Uno sfondo colorato, con una sfumatura che parte da sopra con un colore startcolor=#99CC00 (verde chiaro)
e prosegue verso il basso terminando con il colore endColor=#2C8008 (verde scuro)
L’inclinazione è ottenuta andando a impostare, la proprietà angle=90 che può assumere valori da 0 fino a 360. Quindi un valore di 90° significa che partiremo con il colore di partenza dal basso e ci sposteremo in verticali (90°) verso l’alto. Immagina quindi di essere una freccia e che i tuoi piedi siano sempre il punto di partenza, mentre la tua testa indichi la direzione.
La proprietà background di un linear layout, ma in generale, di tutti i componenti di una interfaccia utente UI, , permette di impostare il colore e l’eventuale forma dello sfondo.
Ad esempio se volessi impostare un colore uniforme, potrei inserire direttamente il codice esadecimale del colore (es. bianco #FFFFFF), senza necessariamente creare un file xml apposito, mentre nel caso voglia ottenere degli effetti più complessi, allora dovrò sfruttare questo nuovo tag <shape>.
Ebbene grazie ad un semplice file xml, allora, siamo riusciti ad inserire un nodo radice <shape>, dal quale partire per la creazione di una serie di forme geometriche elementari (shape=rectangle, shape=oval, shape=line, shape=ring), che poi è possibile personalizzare ulteriormente, con l’aggiunta di una serie di nodi figli come <gradient>, <corners>, <stroke>, <solid>
Riassumendo, in Android grazie al tag <shape>, io posso disegnare queste forme base:
- shape="rectangle" - disegno un rettangolo
- shape="oval" - disegno un cerchio pieno o ellisse
- shape="line" - disegno una linea
- shape="ring" - disegno un anello (circonferenza vuota)
Nel caso tu non specifichi uno di questi valori, quello predefinito è basato sulla forma dell’oggetto a cui si applica, generalmente un rettangolo.
Per personalizzare queste forme ho a disposizione:
- gradient: Specifica un effetto gradiente da applicare come sfondo
- solid: Specifica il colore pieno da usare come sfondo
- padding: Specifica la distanza, tra i bordi della forma e il suo contenuto
- size: Specifica la larghezze e altezza della forma (width e height)
- stroke: Specifica una linea che contorna il bordo della forma.
Una volta creata la forma che vogliamo disegnare, il tutto ahimè non usando Photoshop, possiamo aggiungerle al nostro layout, grazie all’uso degli attributi tipici di ogni componente View, come ad esempio background o src.
Il vantaggio è che se più componenti devono avere lo stesso sfondo, con una semplice modifica al file xml dove ho definito il tag <shape> con le caratteristiche di quello sfondo, automaticamente cambierò al volo tutti gli sfondi dei diversi componenti della mia app.
Esistono, chiaramente, molte opzioni per ognuno dei tag figli di shape elencati qui sopra. Come sempre non è necessario impararle a memoria, perche il nostro software di sviluppo ci aiuterà nella scelta degli attributi corretti non appena scriviamo il relativo tag.
In ogni caso, a lista completa la puoi trovare a questo link
Qui di seguito ho voluto elencare alcuni dei tipici usi di <shape> con la spiegazione di come ottenere l’effetto desiderato.
Esempio: creare uno sfondo con un gradiente radiale
Supponiamo tu voglia ottenere il risultato di figura:
La forma che dovrai progettare sarà evidentemente un gradiente, con la differenza che invece di essere lineare sarà radiale.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:startColor="#99CC00" android:endColor="#2C8008" android:type="radial" android:gradientRadius="500"/> </shape>
I colori di inizio e fine sono gli stessi di prima, la differenza è che ho applicato un gradiente di tipo radial ( type="radial") con un raggio di 500.
Esempio: creare uno sfondo con gradiente lineare per un componente TextView
Supponiamo tu voglia ottenere il risultato di figura:
In questo caso ipotizzo di utilizzare lo stesso sfondo creato in precedenza, sfondosfumato.xml, quindi lo posso applicare alla proprietà background del mio componente. Per i margini del testo dal bordo posso usare la proprietà padding del componente, oppure andare ad agire direttamente nella forma, con il tag <padding>. In questo secondo caso, tutti i componenti che useranno tale sfondo, avranno lo stesso padding minimo.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/sfondogradiente" android:padding="0dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="Testo prima riga" android:id="@+id/textView" android:layout_gravity="center_horizontal" android:padding="10dp" android:background="@drawable/sfondosfumato" /> </LinearLayout>
Esempio: creare una linea di separazione orizzontale tra due elementi TextView
Esistono diverse tecniche per separare elementi con delle righe orizzontali o verticali.
Una tra queste prevede l’utilizzo del componente generico View, in cui è possibile impostare un attributo background ad un oggetto <shape> di tipo rettangolo (shape="rectangle") con all’interno <solid>, oppure un oggetto <shape> di tipo line (shape="line")con all’interno un bordo <stroke> (NB: una linea non può avere colore di sfondo, esattamente come accade in photoshop).
Oppure si potrebbe sfruttare un componente ImageView e impostare l’attributo src con le forme elencate sopra e così via.
In tutti i casi, ipotizzando di usare (shape="line"), la forma che dovremo creare dovrà contenere al suo interno il figlio <stroke> che permette di definire una linea di un certo spessore width (occhio che width non imposta una larghezza in questo caso)
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="line"> <stroke android:width="1dp" android:color="#000000" /> </shape>
Questa forma la posso poi applicare ad un componente immagine.
<ImageView android:layout_width="match_parent" android:layout_height="10dp" android:id="@+id/imageView" android:layout_gravity="center_horizontal" android:src="@drawable/separatore"/>
che potrò inserire tra due componenti TextView (fig. b):
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/sfondogradiente" android:padding="0dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="Testo prima riga" android:id="@+id/textView" android:layout_gravity="center_horizontal" android:padding="10dp" /> <ImageView android:layout_width="match_parent" android:layout_height="10dp" android:id="@+id/imageView" android:layout_gravity="center_horizontal" android:src="@drawable/separatore" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="Testo seconda riga" android:id="@+id/textView2" android:layout_gravity="center_horizontal" android:padding="10dp"/> </LinearLayout>
Esempio: creare uno sfondo rettangolare, con bordi arrotondati e all’interno del testo
In questo caso è possibile sfruttare il tag <corner> e applicare la forma ad un componente TextView grazie all’attributo background. E’ possibile anche sfruttare il tag <padding> per aggiungere un margine interno alla forma, in modo da distanziare i bordi dal testo, cosa che potrebbe essere fatta direttamente nel componente TextView.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <padding android:left="15dp" android:top="4dp" android:right="15dp" android:bottom="4dp" /> <stroke android:width="1dp" android:color="#D8FDFB" /> <corners android:radius="5dp" /> <solid android:color="#FFFFFF" /> </shape>
Come esercizio potresti provare a modificare la forma sopra e ad applicare invece che un colore uniforme, un gradiente <gradient>
Esempio: creare tre sezioni circolari sovrapposte (stile google+ app)
In questo caso si dovrà creare una forma <shape> di tipo ovale (shape=oval) andando ad impostare il colore con il nodo figlio <solid>. Agendo poi sulle proprietà del componente a cui applicherò la forma, come per esempio alpha, che determina la trasparenza, potrò ottenere effetti di sovrapposizione e di velature.
Ognuno dei cerchi è ottenuto grazie all'attributo shape="oval" andando ad impostare un colore di sfondo uniforme (<solid>)
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#fffdd80c"/> </shape>
A questo punto la potrei applicare sempre ad un componente immagine, inserito all'interno di un linear layout, con altri componenti immagine, ognuno che fa riferimento ad una forma ovale, con colore diverso:
<ImageView android:layout_width="120dp" android:layout_height="120dp" android:id="@+id/imageView2" android:src="@drawable/sfondocircolare_rosso" android:alpha="0.9"/>
Agendo sulla larghezza e altezza, posso cambiarne la dimensione mentre agendo sul canale alpha, posso renderla più o meno trasparente.
Se invece volessi creare solo la circonferenza:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <stroke android:width="1dp" android:color="#fffafd07" /> </shape>
Come vedi è usato solo <stroke> che definisce proprio il bordo della forma, togliendo <solid> che invece imposta lo sfondo.
Ci sarebbero molte altre caratteristiche da vedere, ma giusto per avere le basi di come si creano forme elementari da applicare ai nostri componenti dell’interfaccia.
Proseguire oltre Shape Drawable: Layer List
In quest’ultimo esempio, l’effetto di sovrapposizione dei due cerchi è stato ottenuto andando ad agire sui margini rispetto al relative layout in cui erano contenute.
In Android è possibile usare un concetto molto simile a quello che avviene per photoshop, ossia il concetto di livello. In photoshop, cos’è un livello? Beh è un ipotetico foglio in cui vado a disegnare qualcosa e che posso sovrapporre ad altri fogli, altri livelli, indipendenti dai precedenti, ottenendo così l’immagine finale.
Allo stesso modo in Android è possibile definire un file xml, in cui vado ad inserire diversi fogli <item> ognuno dei quali caratterizzato da una certa forma, creata con le tecniche viste in precedenza, il tutto racchiuso in un tag padre <layer-list>,come nell’esempio qui sotto:
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/ sfondocircolare_rosso"/> <item android:drawable="@drawable/sfondocircolare_giallo"/> </layer-list>
Ebbene, questi concetti sono alla base per andare a personalizzare uno degli elementi più usati in un’applicazione: i bottoni.
Tutte considerazioni che inizieremo a vedere a partire dal prossimo tutorial, quando impareremo a personalizzare la grafica di un bottone o menu, a seconda dello stato in cui si trova (premuto/non premuto, attivo/disattivato).
2024-12-13 Tipo/Autore: Davide Copelli {ing} Pubblicato da: CorsoAndroid.it