Panoramica degli eventi di input

Prova Compose
Jetpack Compose è il toolkit UI consigliato per Android. Scopri come utilizzare il tocco e l'input in Compose.

Su Android, esistono diversi modi per intercettare gli eventi derivanti dall'interazione di un utente con la tua applicazione. Quando prendi in considerazione gli eventi all'interno dell'interfaccia utente, l'approccio consiste nell'acquisire gli eventi dall'oggetto View specifico con cui l'utente interagisce. La classe View fornisce i mezzi per farlo.

Nelle varie classi View che utilizzerai per comporre il layout, potresti notare diversi metodi di callback pubblici che sembrano utili per gli eventi UI. Questi metodi vengono chiamati dal framework Android quando l'azione corrispondente si verifica sull'oggetto. Ad esempio, quando viene toccata una View (ad esempio un pulsante), viene chiamato il metodo onTouchEvent() su quell'oggetto. Tuttavia, per intercettare questo evento, devi estendere la classe ed eseguire l'override del metodo. Tuttavia, estendere ogni oggetto View per gestire un evento di questo tipo non sarebbe pratico. Per questo motivo, la classe View contiene anche una raccolta di interfacce nidificate con callback che puoi definire molto più facilmente. Queste interfacce, chiamate listener di eventi, sono il tuo biglietto per acquisire l'interazione dell'utente con la tua UI.

Sebbene tu utilizzi più comunemente i listener di eventi per ascoltare l'interazione dell'utente, potrebbe arrivare un momento in cui vuoi estendere una classe View per creare un componente personalizzato. Forse vuoi estendere la classe Button per creare qualcosa di più sofisticato. In questo caso, potrai definire i comportamenti degli eventi predefiniti per la tua classe utilizzando i gestori di eventi della classe.

Listener di eventi

Un listener di eventi è un'interfaccia nella classe View che contiene un singolo metodo di callback. Questi metodi verranno chiamati dal framework Android quando la View a cui è stato registrato il listener viene attivata dall'interazione dell'utente con l'elemento nell'UI.

Le interfacce dei listener di eventi includono i seguenti metodi di callback:

onClick()
Da View.OnClickListener. Viene chiamato quando l'utente tocca l'elemento (in modalità multi-touch) o lo seleziona con i tasti di navigazione o la trackball e preme il tasto "Invio" appropriato o preme la trackball.
onLongClick()
Da View.OnLongClickListener. Viene chiamato quando l'utente tocca e tiene premuto l'elemento (in modalità multi-touch) o lo seleziona con i tasti di navigazione o la trackball e preme e tiene premuto il tasto "Invio" appropriato o preme e tiene premuta la trackball (per un secondo).
onFocusChange()
Da View.OnFocusChangeListener. Viene chiamato quando l'utente si sposta sull'elemento o lo abbandona utilizzando i tasti di navigazione o la trackball.
onKey()
Da View.OnKeyListener. Viene chiamato quando l'utente è selezionato sull'elemento e preme o rilascia una chiave hardware sul dispositivo.
onTouch()
Da View.OnTouchListener. Viene chiamato quando l'utente esegue un'azione qualificata come evento di tocco, inclusa una pressione, un rilascio, o qualsiasi gesto di movimento sullo schermo (entro i limiti dell'elemento).
onCreateContextMenu()
Da View.OnCreateContextMenuListener. Viene chiamato quando viene creato un menu contestuale (a seguito di un "clic lungo" prolungato). Per maggiori informazioni sui menu contestuali, consulta la guida per gli sviluppatori relativa ai menu.

Questi metodi sono gli unici elementi delle rispettive interfacce. Per definire uno di questi metodi e gestire gli eventi, implementa l'interfaccia nidificata nella tua Activity o definiscila come classe anonima. Quindi, passa un'istanza della tua implementazione al rispettivo metodo View.set...Listener(). Ad esempio, chiama setOnClickListener() e passagli la tua implementazione di OnClickListener.

L'esempio seguente mostra come registrare un listener di clic per un pulsante.

Kotlin

protected void onCreate(savedValues: Bundle) {
    ...
    val button: Button = findViewById(R.id.corky)
    // Register the onClick listener with the implementation above
    button.setOnClickListener { view ->
        // do something when the button is clicked
    }
    ...
}

Java

// Create an anonymous implementation of OnClickListener
private OnClickListener corkyListener = new OnClickListener() {
    public void onClick(View v) {
      // do something when the button is clicked
    }
};

protected void onCreate(Bundle savedValues) {
    ...
    // Capture our button from layout
    Button button = (Button)findViewById(R.id.corky);
    // Register the onClick listener with the implementation above
    button.setOnClickListener(corkyListener);
    ...
}

Potresti anche trovare più comodo implementare OnClickListener come parte della tua Activity. In questo modo, eviterai il caricamento di classi e l'allocazione di oggetti aggiuntivi. Ad esempio:

Kotlin

class ExampleActivity : Activity(), OnClickListener {
  
    protected fun onCreate(savedValues: Bundle) {
        val button: Button = findViewById(R.id.corky)
        button.setOnClickListener(this)
    }

    // Implement the OnClickListener callback
    fun onClick(v: View) {
        // do something when the button is clicked
    }
}

Java

public class ExampleActivity extends Activity implements OnClickListener {
    protected void onCreate(Bundle savedValues) {
        ...
        Button button = (Button)findViewById(R.id.corky);
        button.setOnClickListener(this);
    }

    // Implement the OnClickListener callback
    public void onClick(View v) {
      // do something when the button is clicked
    }
    ...
}

Tieni presente che la callback onClick() nell'esempio precedente non ha un valore restituito, ma alcuni altri metodi di listener di eventi devono restituire un valore booleano. Il motivo dipende dall'evento. Per i pochi che lo fanno, ecco perché:

  • onLongClick() - : restituisce un valore booleano per indicare se hai utilizzato l'evento e se non deve essere eseguito ulteriormente. Ovvero, restituisci true per indicare che hai gestito l'evento e che deve essere interrotto qui; restituisci false se non l'hai gestito e/o se l'evento deve continuare a qualsiasi altro listener di clic.
  • onKey() - : restituisce un valore booleano per indicare se hai utilizzato l'evento e se non deve essere eseguito ulteriormente. Ovvero, restituisci true per indicare che hai gestito l'evento e che deve essere interrotto qui; restituisci false se non l'hai gestito e/o se l'evento deve continuare a qualsiasi altro listener di tasti.
  • onTouch() - : restituisce un valore booleano per indicare se il listener utilizza questo evento. La cosa importante è che questo evento può avere più azioni che si susseguono. Pertanto, se restituisci false quando viene ricevuto l'evento di azione verso il basso, indichi che non hai utilizzato l'evento e che non ti interessano le azioni successive di questo evento. Di conseguenza, non verrà chiamata per altre azioni all'interno dell'evento, ad esempio un gesto con le dita o l'eventuale evento di azione verso l'alto.

Ricorda che gli eventi dei tasti hardware vengono sempre inviati alla View attualmente selezionata. Vengono inviati a partire dalla parte superiore della gerarchia di oggetti View e poi verso il basso, finché non raggiungono la destinazione appropriata. Se la tua View (o un elemento secondario della tua View) è attualmente selezionata, puoi vedere l'evento che passa attraverso il metodo dispatchKeyEvent(). In alternativa all'acquisizione degli eventi dei tasti tramite la View, puoi anche ricevere tutti gli eventi all'interno della tua Activity con onKeyDown() e onKeyUp().

Inoltre, quando pensi all'input di testo per la tua applicazione, ricorda che molti dispositivi hanno solo metodi di input software. Questi metodi non devono necessariamente essere basati sui tasti; alcuni potrebbero utilizzare l'input vocale, la scrittura a mano e così via. Anche se un metodo di input presenta un'interfaccia simile a una tastiera, in genere non attiva la onKeyDown() famiglia di eventi. Non devi mai creare un'UI che richieda la pressione di tasti specifici per essere controllata, a meno che tu non voglia limitare la tua applicazione ai dispositivi con una tastiera hardware. In particolare, non fare affidamento su questi metodi per convalidare l'input quando l'utente preme il tasto Invio; utilizza invece azioni come IME_ACTION_DONE per segnalare al metodo di input come la tua applicazione si aspetta di reagire, in modo che possa modificare la sua UI in modo significativo. Evita di fare ipotesi sul funzionamento di un metodo di input software e affidati al fatto che fornisca testo già formattato alla tua applicazione.

Nota: Android chiama prima i gestori di eventi e poi i gestori predefiniti appropriati dalla definizione della classe. Pertanto, la restituzione di true da questi listener di eventi interrompe la propagazione dell'evento ad altri listener di eventi e blocca anche la callback al gestore di eventi predefinito nella View. Assicurati quindi di voler terminare l'evento quando restituisci true.

Gestori di eventi

Se stai creando un componente personalizzato da View, potrai definire diversi metodi di callback utilizzati come gestori di eventi predefiniti. Nel documento sui componenti View personalizzati, imparerai alcune delle callback comuni utilizzate per la gestione degli eventi, tra cui:

Esistono altri metodi di cui devi essere a conoscenza, che non fanno parte della classe View, ma che possono influire direttamente sul modo in cui puoi gestire gli eventi. Pertanto, quando gestisci eventi più complessi all'interno di un layout, prendi in considerazione questi altri metodi:

Modalità multi-touch

Quando un utente naviga in un'interfaccia utente con i tasti direzionali o una trackball, è necessario selezionare gli elementi su cui è possibile agire (come i pulsanti) in modo che l'utente possa vedere cosa accetterà l'input. Tuttavia, se il dispositivo ha funzionalità touch e l'utente inizia a interagire con l'interfaccia toccandola, non è più necessario evidenziare gli elementi o selezionare una View specifica. Esiste quindi una modalità di interazione denominata "modalità multi-touch".

Per un dispositivo con funzionalità touch, una volta che l'utente tocca lo schermo, il dispositivo entra in modalità multi-touch. Da questo momento in poi, solo le View per cui isFocusableInTouchMode() è true saranno selezionabili, ad esempio i widget di modifica del testo. Altre View che possono essere toccate, come i pulsanti, non verranno selezionate quando vengono toccate; attiveranno semplicemente i listener di clic quando vengono premute.

Ogni volta che un utente preme un tasto direzionale o scorre con una trackball, il dispositivo esce dalla modalità multi-touch e trova una View da selezionare. Ora, l'utente può riprendere a interagire con l'interfaccia utente senza toccare lo schermo.

Lo stato della modalità multi-touch viene mantenuto in tutto il sistema (tutte le finestre e le Activity). Per eseguire una query sullo stato attuale, puoi chiamare isInTouchMode() per verificare se il dispositivo è attualmente in modalità multi-touch.

Gestione dello stato attivo

Il framework gestirà il movimento di routine dello stato attivo in risposta all'input dell'utente. Ciò include la modifica dello stato attivo quando le View vengono rimosse o nascoste o quando diventano disponibili nuove View. Le View indicano la loro disponibilità ad acquisire lo stato attivo tramite il metodo isFocusable(). Per modificare se una View può acquisire lo stato attivo, chiama setFocusable(). In modalità multi-touch, puoi eseguire una query per verificare se una View consente lo stato attivo con isFocusableInTouchMode(). Puoi modificare questa impostazione con setFocusableInTouchMode().

Sui dispositivi con Android 9 (livello API 28) o versioni successive, le Activity non assegnano uno stato attivo iniziale. Devi invece richiedere esplicitamente lo stato attivo iniziale, se lo desideri.

Il movimento dello stato attivo si basa su un algoritmo che trova il vicino più vicino in una determinata direzione. In rari casi, l'algoritmo predefinito potrebbe non corrispondere al comportamento previsto dallo sviluppatore. In queste situazioni, puoi fornire override espliciti con i seguenti attributi XML nel file di layout: nextFocusDown, nextFocusLeft, nextFocusRight e nextFocusUp. Aggiungi uno di questi attributi alla View da cui lo stato attivo sta uscendo. Definisci il valore dell'attributo come l'ID della View a cui deve essere assegnato lo stato attivo. Ad esempio:

<LinearLayout
    android:orientation="vertical"
    ... >
  <Button android:id="@+id/top"
          android:nextFocusUp="@+id/bottom"
          ... />
  <Button android:id="@+id/bottom"
          android:nextFocusDown="@+id/top"
          ... />
</LinearLayout>

Normalmente, in questo layout verticale, la navigazione verso l'alto dal primo pulsante non porterebbe da nessuna parte, né la navigazione verso il basso dal secondo pulsante. Ora che il pulsante in alto ha definito quello in basso come nextFocusUp (e viceversa), la selezione passerà da alto a basso e da basso ad alto.

Se vuoi dichiarare una View come selezionabile nella tua UI (quando tradizionalmente non lo è), aggiungi l'attributo XML android:focusable alla View nella dichiarazione del layout. Imposta il valore su true. Puoi anche dichiarare una View come selezionabile in modalità multi-touch con android:focusableInTouchMode.

Per richiedere a una View specifica di acquisire lo stato attivo, chiama requestFocus().

Per ascoltare gli eventi di stato attivo (ricevere una notifica quando una View acquisisce o perde lo stato attivo), utilizza onFocusChange(), come descritto nella sezione Listener di eventi.