Android: creare favolose animazioni con ConstraintLayout – Parte 2

A

Nell’articolo precedente abbiamo visto come con poche righe di codice e l’utilizzo di ConstraintLayout si possano creare delle favolose animazioni per le nostre applicazioni Android.

Navigando sul web mi sono imbattuto in un sito che proponeva, nella pagina dedicata ai vari piani di costo del servizio, un’animazione che ho deciso di riprodurre in un’app nativa android attraverso appunto l’uso di un ConstraintLayout.

Vediamo quindi in cosa consiste e come realizzarla ricordandovi come sempre che potete vedere tutto il codice utilizzato in questo articolo nell’app di esempio sul mio account di GitHub.

Cosa andremo a realizzare

Il progetto consiste nel realizzare un’activity che appena eseguita visualizzi alcuni pianeti con immagine e nome. Cliccando sul pianeta la relativa immagine verrà ingrandita e comparirà un breve testo. Mentre l’animazione verrà eseguita le immagini degli altri pianeti si rimpiccioliranno e si compatteranno per far posto a quella selezionata.

L’animazione verrà realizzata in 2 modi diversi:

  1. Riprendendo l’esempio dell’articolo precedente si realizzarenno più layouts e attraverso ConstraintSet e Transition si creerà l’animazione per ogni immagine. Il risultato che andremo ad ottenere sarà il seguente
  2. Vedremo come con 1 solo layout potremo riprodurre lo stesso effetto utilizzando invece dei ValueAnimator gestiti da un AnimatorSet. Ecco come si presenterà l’animazione

Ovviamente i 2 esempi non saranno uguali al 100% ma le due diverse animazioni cercheranno di replicare lo stesso effetto.

Primo esempio: ConstraintSet e Transition

La teoria che stà alla base di questa animazione è la stessa descritta nel precedente articolo, richiede solo qualche layout in più perchè dovremo averne a disposizione uno base e uno per ogni pianeta che dovremo animare ( 3 nel nostro esempio ).

In particolare i layout realizzati sono:

Per cercare di capire come funziona l’animazione vediamo di dare un’occhiata ai blueprint simulando in sequenza il comportamento dei componenti.

Layout di default

Partendo dal layout di default possiamo subito notare le 2 GuideLine poste rispettivamente a 33% e 66% della larghezza. In questo modo la vista risulterà suddivisa equamente in 3 aree, ognuna delle quali conterrà le informazioni del pianeta. Ogni immagine e descrizione è ancorata ai lati della propria sezione. Questo permetterà, al variare della posizione della propria GuideLine, di variare la dimensione delle viste ancorate. Nella seconda figura

Layout di selezione del pianeta sinistro

possiamo notare come il layout si presenti quando l’utente selezionerà il pianeta che si trova a sinistra. In questo caso le posizioni delle GuideLine vengono variate rispettivamente a 60% e 80%. In questo modo il pianeta selezionato occuperà il 60% della larghezza totale mentre ai 2 rimanenti rimarranno solamente il 20% ciascuno. Questo durante l’animazione si tradurrà in un ingrandimento dell’immagine del primo pianeta e un rimpicciolimento di tutti gli altri.

Layout di selezione del pianeta centrale

Il comportamento degli altri layout sarà analogo al primo cambiando solamente la posizione delle GuideLine che saranno impostate a 20% e 80% nel secondo caso e 20% e 40% nel terzo caso. In questo modo manterremo sempre una larghezza del 60% per il pianeta selezionato e del 20% per gli altri. Il nome del pianeta seguirà sempre l’immagine perchè le sue constraint sono impostate verticalmente sul

Layout di selezione del pianeta destro

margine inferiore dell’immagine mentre orizzontalmente ai lati sinistro e destro dell’immagine.

Per fare in modo di poter visualizzare tutto il testo della descrizione la vista è stata ancorata al lato inferiore del layout mentre al suo lato superiore è stato ancorato il nome del pianeta. In questo modo sarà la descrizione a “spingere” in alto nome e immagine del pianeta. Ovviamente questo implica che più lungo sarà il testo più l’immagine risulterà piccola.

Come abbiamo già visto nell’esempio precedente tutto quello che ci rimane da fare ora è quello di applicare il layout giusto alla nostra animazione

private fun updateConstraints(planetMode: PlanetMode) {
    val newConstraintSet = ConstraintSet()
    newConstraintSet.clone(this, planetMode.layoutResId)
    newConstraintSet.applyTo(rootLayout)
    val transition = ChangeBounds()
    transition.interpolator = OvershootInterpolator()
    transition.duration = 500
    TransitionManager.beginDelayedTransition(rootLayout, transition)
}

Il codice sorgente dell’intera activity è possibile trovarlo nell’app di esempio nel file ZoomCarouselActivity.kt. Ecco quindi che con poco codice e qualche layout siamo riusciti a creare questa fantastica animazione.

Secondo esempio: ValueAnimator e AnimatorSet

Il secondo esempio che voglio portarvi è una differente soluzione a quanto è stato proposto prima. Partiamo sempre da una base comune e cioè da un ConstraintLayout in cui 2 GuideLine verticali lo suddividono in 3 zone uguali.

In questo caso però invece di ottenere le animazioni con delle transizioni tra layout utilizzeremo dei ValueAnimator e AnimatorSet.

ValueAnimator

ValueAnimator è una classe che ci permette di eseguire un’animazione di una durata stabilita tra un valore iniziale e finale e un listener che ci fornisce il valore corrente in base al tempo. Per capire meglio il funzionamento vediamo il seguente esempio pratico. Poniamo il caso di dover spostare una GuideLine da una posizione del 20% al 60% in un tempo di 1 secondo, ecco come il ValueAnimator dovrà essere configurato:

val animator = ValueAnimator.ofFloat(0.2f,0.6f)
animator.addUpdateListener { animation ->
    guideline.setGuidelinePercent(animation.animatedValue as Float)
    guideline.requestLayout()
}
animator.duration = 1000

Nella prima prima riga andiamo a crearci un ValueAnimator con dei valori di riferimento che comprendono un intervallo tra 0.2 e 0.6. Il listener, chiamato a ogni frame dell’animazione, ci fornisce tramite animation.animatedValue il valore di riferimento corrente rispetto al tempo trascorso. A inizio animazione quindi il valore sarà 0.2, a fine 0.6 e a metà 0.4. Questo valore servirà per impostare la percentuale alla GuideLine che in questo modo verrà spostata ad ogni frame per raggiungere il valore finale del 60%.

AnimatorSet

Un AnimatorSet possiamo pensarlo come una sorta di contenitore che permette all’utente di aggiungere varie animazioni per poi poterle eseguire in sequenza, in parallelo, una prima dell’altra, ecc… In questo caso, nell’esempio di selezione di un pianeta, andremo ad eseguire 3 animazioni in questo modo:

spostamento GuideLine sinistra -> spostamento GuideLine destra -> visualizzazione testo

Le guideline verranno spostate in contemporanea mentre a fine della loro animazione verrà fatto comparire il testo, tradotto in codice l’AnimatorSet sarà il seguente:

val animatorSet = AnimatorSet()
animatorSet.play(leftGuidelineAnimator).with(rightGuidelineAnimator).before(descriptionAnimator)
animatorSet.start()

Avendo capito l’utilizzo di ValueAnimator e AnimatorSet è facile capire ora come realizzare le varie animazioni. Si tratta solo di spostare le GuideLine in base al pianeta selezionato e infine visualizzare la descrizione e di riportare tutto allo stato iniziale se viene selezionato il pianeta attualmente ingrandito o lo spazio nero. ZoomCarouselValueAnimatorActivity.kt è l’activity che esegue tutto questo.

Conclusioni

In questo esempio lo svantaggio di cui abbiamo parlato nell’articolo precedente è molto evidente: creare un’animazione di questa portata prevede la creazione e la gestione di ben 4 layout. Certo, non è un problema insormontabile ma sono del parere che trovandomi di fronte alla scelta tra le 2 motodologie di affrontare il problema la seconda è quella che forse ha il minor costo in termini di mantenibilità.

Come avete potuto notare non esiste un limite alle animazioni che possono essere create con un ConstraintLayout. Chiunque volesse proporne una realizzata da sè o chiedere come crearne una sono a disposizione.

Riferimenti

Potere trovare tutti punti analizzati nell’app di esempio da me realizzata sul mio account di GitHub.

Documentazione ufficiale di ValueAnimator e AnimatorSet.

A proposito di me

Gianluca Fattarsi

Perito informatico, esperienza come programmatore nell'ambito lavorativo dal lontano 2002. La sua formazione in Java spazia dal Front-End al Back-End con esperienze in gestione database e realizzazione di App Android in Kotlin. Hobby preferiti: Linux, lettura e World of Warcraft.

Gli articoli più letti

Articoli recenti

Commenti recenti