Il disegno di un'interfaccia utente è solo una parte della creazione di una visualizzazione personalizzata. Devi anche fare in modo che la visualizzazione risponda all'input dell'utente in modo da assomigliare il più possibile all'azione reale che stai imitando.
Fai in modo che gli oggetti nella tua app si comportino come gli oggetti reali. Ad esempio, non consentire che le immagini nella tua app scompaiano e ricompaiano altrove, perché gli oggetti nel mondo reale non lo fanno. Sposta invece le immagini da un luogo all'altro.
Gli utenti percepiscono anche il comportamento o l'aspetto più sottile di un'interfaccia e reagiscono meglio alle sottigliezze che imitano il mondo reale. Ad esempio, quando gli utenti lanciano un oggetto UI, dai loro un senso di inerzia all'inizio che ritarda il movimento. Alla fine del movimento, dai all'oggetto un senso di inerzia che lo porti oltre il lancio.
Questa pagina mostra come utilizzare le funzionalità del framework Android per aggiungere questi comportamenti reali alla tua visualizzazione personalizzata.
Puoi trovare ulteriori informazioni correlate in Panoramica degli eventi di input e Panoramica dell'animazione delle proprietà.
Gestire i gesti di input
Come molti altri framework UI, Android supporta un modello di eventi di input. Le azioni
dell'utente si trasformano in eventi che attivano i callback e puoi eseguire l'override
dei callback per personalizzare il modo in cui la tua app risponde all'utente. L'evento di input più comune nel sistema Android è il tocco, che attiva onTouchEvent(android.view.MotionEvent).
Esegui l'override di questo metodo per gestire l'evento nel seguente modo:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
Java
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
Gli eventi touch di per sé non sono particolarmente utili. Le moderne UI touch
definiscono le interazioni in termini di gesti come toccare, tirare, spingere,
scorrere rapidamente e zoomare. Per convertire gli eventi tocco non elaborati in gesti, Android
fornisce
GestureDetector.
Costruisci un GestureDetector passando un'istanza di una classe
che implementa
GestureDetector.OnGestureListener.
Se vuoi elaborare solo alcuni gesti, puoi estendere
GestureDetector.SimpleOnGestureListener
anziché implementare l'interfaccia GestureDetector.OnGestureListener. Ad esempio, questo codice crea una classe che estende
GestureDetector.SimpleOnGestureListener e sostituisce
onDown(MotionEvent).
Kotlin
private val myListener = object : GestureDetector.SimpleOnGestureListener() { override fun onDown(e: MotionEvent): Boolean { return true } } private val detector: GestureDetector = GestureDetector(context, myListener)
Java
class MyListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } } detector = new GestureDetector(getContext(), new MyListener());
Indipendentemente dall'utilizzo di GestureDetector.SimpleOnGestureListener,
implementa sempre un
metodo onDown()
che restituisce true. Questo è necessario perché tutti i gesti
iniziano con un messaggio onDown(). Se restituisci false
da onDown(), come
GestureDetector.SimpleOnGestureListener, il sistema presuppone
che tu voglia ignorare il resto del gesto e gli altri metodi di
GestureDetector.OnGestureListener non vengono chiamati. Restituisci solo
false da onDown() se vuoi ignorare un'intera
gesture.
Dopo aver implementato GestureDetector.OnGestureListener e creato
un'istanza di GestureDetector, puoi utilizzare
GestureDetector per interpretare gli eventi tocco che ricevi in
onTouchEvent().
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return detector.onTouchEvent(event).let { result -> if (!result) { if (event.action == MotionEvent.ACTION_UP) { stopScrolling() true } else false } else true } }
Java
@Override public boolean onTouchEvent(MotionEvent event) { boolean result = detector.onTouchEvent(event); if (!result) { if (event.getAction() == MotionEvent.ACTION_UP) { stopScrolling(); result = true; } } return result; }
Quando passi onTouchEvent() un evento tocco che non
riconosce come parte di un gesto, restituisce false. Puoi quindi eseguire
il tuo codice personalizzato di rilevamento dei gesti.
Creare un movimento fisicamente plausibile
I gesti sono un modo efficace per controllare i dispositivi touchscreen, ma possono essere controintuitivi e difficili da ricordare a meno che non producano risultati fisicamente plausibili.
Ad esempio, supponiamo di voler implementare un gesto di scorrimento orizzontale che imposti la rotazione dell'elemento disegnato nella visualizzazione attorno al suo asse verticale. Questo gesto ha senso se l'interfaccia utente risponde muovendosi rapidamente nella direzione dello scorrimento veloce, per poi rallentare, come se l'utente spingesse un volano e lo facesse girare.
La documentazione su come
animare un gesto
di scorrimento fornisce una spiegazione dettagliata su come implementare il tuo comportamento
di scorrimento. Ma simulare la sensazione di un volano non è banale. Per far funzionare correttamente un modello di volano, sono necessarie molte conoscenze di fisica e matematica. Fortunatamente,
Android fornisce classi helper per simulare questo e altri comportamenti. La classe
Scroller
è la base per la gestione dei movimenti di scorrimento in stile volano.
Per avviare uno scorrimento, chiama
fling()
con la velocità iniziale e i valori minimo e massimo di x e y
dello scorrimento. Per il valore della velocità, puoi utilizzare il valore calcolato da
GestureDetector.
Kotlin
fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { scroller.fling( currentX, currentY, (velocityX / SCALE).toInt(), (velocityY / SCALE).toInt(), minX, minY, maxX, maxY ) postInvalidate() return true }
Java
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY); postInvalidate(); return true; }
La chiamata a fling() configura il modello fisico per il gesto di scorrimento
rapido. Dopodiché, aggiorna Scroller chiamando
Scroller.computeScrollOffset()
a intervalli regolari. computeScrollOffset() aggiorna lo stato interno dell'oggetto Scroller leggendo l'ora corrente e utilizzando il modello fisico per calcolare la posizione x e y in quel momento. Chiama
getCurrX()
e
getCurrY()
per recuperare questi valori.
La maggior parte delle visualizzazioni passa le posizioni x e y dell'oggetto Scroller direttamente a scrollTo().
Questo esempio è leggermente diverso: utilizza la posizione x di scorrimento corrente
per impostare l'angolo di rotazione della visualizzazione.
Kotlin
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
Java
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
La classe Scroller calcola le posizioni di scorrimento per te, ma
non le applica automaticamente alla visualizzazione. Applica nuove coordinate
con una frequenza sufficiente a rendere fluida l'animazione di scorrimento. Puoi farlo in due modi:
- Forza un nuovo disegno chiamando
postInvalidate()dopo aver chiamatofling(). Questa tecnica richiede di calcolare gli offset di scorrimento inonDraw()e chiamarepostInvalidate()ogni volta che l'offset di scorrimento cambia. - Configura un
ValueAnimatorper l'animazione per la durata dello spostamento e aggiungi un listener per elaborare gli aggiornamenti dell'animazione chiamandoaddUpdateListener(). Questa tecnica ti consente di animare le proprietà di unView.
Rendi fluide le transizioni
Gli utenti si aspettano che una UI moderna passi da uno stato all'altro in modo fluido: gli elementi della UI appaiono e scompaiono gradualmente, mentre i movimenti iniziano e terminano in modo fluido, anziché bruscamente. Il framework di animazione delle proprietà di Android semplifica le transizioni fluide.
Per utilizzare il sistema di animazione, ogni volta che una proprietà modifica l'aspetto della visualizzazione, non modificare direttamente la proprietà. Utilizza invece
ValueAnimator per apportare la modifica. Nell'esempio seguente,
la modifica del componente secondario selezionato nella visualizzazione fa ruotare l'intera visualizzazione
in modo che il puntatore di selezione sia centrato.
ValueAnimator modifica la rotazione in un periodo di diverse centinaia
di millisecondi, anziché impostare immediatamente il nuovo valore di rotazione.
Kotlin
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply { setIntValues(targetAngle) duration = AUTOCENTER_ANIM_DURATION start() }
Java
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0); autoCenterAnimator.setIntValues(targetAngle); autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); autoCenterAnimator.start();
Se il valore che vuoi modificare è una delle proprietà di base View, l'animazione è ancora più semplice, perché le visualizzazioni hanno un ViewPropertyAnimator
integrato ottimizzato per l'animazione simultanea di più proprietà, come nell'esempio
seguente:
Kotlin
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();