La creazione di una UI è 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 molto all'azione nel mondo reale che stai imitando.
Fai in modo che gli oggetti nell'app si comportino come gli oggetti reali. Ad esempio, fai in modo che le immagini nella tua app non scompaiano e riappari altrove, perché questo comportamento non è consentito dagli oggetti nel mondo reale. Sposta invece le immagini da una posizione all'altra.
Gli utenti percepiscono comportamenti o emozioni anche sottile nell'interfaccia e reagiscono meglio alle sfumature 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 un senso di slancio che porta l'oggetto oltre il movimento.
Questa pagina mostra come utilizzare le funzionalità del framework Android per aggiungere questi comportamenti reali alla visualizzazione personalizzata.
Puoi trovare ulteriori informazioni correlate in Panoramica degli eventi di input e Panoramica animazione della proprietà.
Gestire i gesti di immissione
Come molti altri framework UI, Android supporta un modello di evento di input. Le azioni degli utenti
vengono trasformate in eventi che attivano i callback e puoi ignorare i
callback per personalizzare il modo in cui la tua app risponde all'utente. L'evento di input più comune nel sistema Android è il touch, che attiva onTouchEvent(android.view.MotionEvent)
.
Sostituisci questo metodo per gestire l'evento come segue:
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 tocco, tiro, spinta, scorrimento e zoom. 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
invece di 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 dal fatto che tu utilizzi o meno GestureDetector.SimpleOnGestureListener
, implementa sempre un metodo onDown()
che restituisca true
. Questa operazione è necessaria 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 false
da onDown()
solo se vuoi ignorare un intero gesto.
Dopo aver implementato GestureDetector.OnGestureListener
e creato
un'istanza di GestureDetector
, puoi utilizzare il tuo
GestureDetector
per interpretare gli eventi touch ricevuti 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 a onTouchEvent()
un evento tocco che non riconosce come parte di un gesto, restituisce false
. Puoi quindi eseguire il tuo codice di rilevamento dei gesti personalizzato.
Crea movimento fisicamente plausibile
I gesti rappresentano uno strumento efficace per controllare i dispositivi touchscreen, ma possono essere controintuitivi e difficili da ricordare a meno che non produca risultati fisicamente plausibili.
Ad esempio, supponi di voler implementare un gesto di scorrimento orizzontale che imposti l'elemento disegnato nella visualizzazione che ruota intorno al suo asse verticale. Questo gesto ha senso se l'interfaccia utente risponde spostandosi rapidamente nella direzione dell'orientamento, quindi rallentando, come se l'utente preme un volano e lo faccia ruotare.
La documentazione su come animare un gesto di scorrimento fornisce una spiegazione dettagliata su come implementare il tuo comportamento di punteggio. Tuttavia, simulare la sensazione di un volano non è banale. Per far funzionare un modello con volano è necessaria molta
fisica e matematica. Fortunatamente,
Android offre classi helper per simulare questo e altri comportamenti. La classe Scroller
è la base per la gestione dei gesti di scorrimento a forma di volano.
Per avviare una corsa, chiama
fling()
con la velocità iniziale e i valori minimo e massimo x e y
della freccia. Per il valore di 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. In seguito, aggiorna Scroller
chiamando
Scroller.computeScrollOffset()
a intervalli regolari. computeScrollOffset()
aggiorna lo
stato interno dell'oggetto Scroller
leggendo l'ora attuale e
utilizzando il modello fisico per calcolare la posizione x e y in quel
tempo. Chiama
getCurrX()
e
getCurrY()
per recuperare questi valori.
La maggior parte delle viste passa le posizioni x e y dell'oggetto Scroller
direttamente a scrollTo()
.
Questo esempio è leggermente diverso: utilizza la posizione di scorrimento x corrente per impostare l'angolo di rotazione della vista.
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 tua visualizzazione. Applica spesso nuove coordinate
per rendere l'animazione di scorrimento fluida. Puoi farlo in due modi:
- Forza un nuovo disegno chiamando
postInvalidate()
dopo aver chiamatofling()
. Questa tecnica richiede il calcolo degli offset di scorrimento inonDraw()
e la chiamata dipostInvalidate()
ogni volta che l'offset di scorrimento cambia. - Configura un
ValueAnimator
per l'animazione per la durata dell'animazione e aggiungi un listener per elaborare gli aggiornamenti dell'animazione chiamandoaddUpdateListener()
. Questa tecnica consente di animare le proprietà di un elementoView
.
Rendi fluide le transizioni
Gli utenti si aspettano una UI moderna che passi senza intoppi da uno stato all'altro: elementi UI che scorrono all'interno e all'esterno invece di apparire e scomparire, e movimenti che iniziano e terminano in modo fluido invece di avviarsi e arrestarsi bruscamente. Il framework di animazione delle proprietà Android semplifica le transizioni fluide.
Per utilizzare il sistema di animazione, ogni volta che una proprietà cambia ciò che influisce sull'aspetto della vista, non modificare direttamente la proprietà. Utilizza invece
ValueAnimator
per apportare la modifica. Nell'esempio seguente, la modifica del componente secondario selezionato nella vista comporta la rotazione dell'intera vista visualizzata in modo che il puntatore di selezione sia centrato.
ValueAnimator
modifica la rotazione in un periodo di diverse centinaia
di millisecondi, invece di 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à View
di base, 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();