Fornisci una navigazione a ritroso personalizzata

Gli utenti navigano all'indietro tra le schermate utilizzando la navigazione a ritroso. La maggior parte dei dispositivi Android ha un pulsante Indietro (fisico, software o basato su gesti). In genere, non devi aggiungere un pulsante Indietro alla tua app. Tuttavia, i dispositivi Android Automotive OS (AAOS) in modalità di compatibilità utilizzano un pulsante Indietro di sistema. Questo pulsante gestisce la navigazione, quindi non devi aggiungerne uno tuo. Per maggiori dettagli, consulta la sezione Modalità di compatibilità AAOS.

Android gestisce un back stack delle destinazioni man mano che l'utente naviga nell'applicazione. In genere, questo consente ad Android di navigare correttamente alle destinazioni precedenti quando viene premuto il pulsante Indietro. Tuttavia, in alcuni casi la tua app potrebbe dover implementare il proprio comportamento del pulsante Indietro per offrire la migliore esperienza utente possibile. Ad esempio, quando utilizzi un WebView, potresti voler eseguire l'override del comportamento predefinito del pulsante Indietro per consentire all'utente di navigare all'indietro nella cronologia di navigazione web anziché nelle schermate precedenti dell'app.

Implementare la navigazione a ritroso personalizzata in Compose

In Jetpack Compose, puoi gestire la navigazione a ritroso personalizzata utilizzando il BackHandler composable.

Quando utilizzi Navigation Compose, in genere utilizzi NavController.navigateUp() o NavController.popBackStack() per navigare alla schermata precedente nello stack indietro. Tuttavia, BackHandler è utile nei casi in cui vuoi implementare un comportamento personalizzato quando l'utente preme il pulsante Indietro del sistema o utilizza il gesto Indietro. Ad esempio, se visualizzi un WebView nella tua app, potresti voler consentire agli utenti di navigare all'indietro nella cronologia di navigazione quando premono il pulsante Indietro del sistema.

Utilizzo di base

Ecco un esempio che mostra come intercettare l'evento Indietro per controllare lo stato personalizzato (ad esempio, comprimere un riquadro):

@Composable
fun MyScreen() {
    var isExpanded by remember { mutableStateOf(false) }

    // Intercept the back button or gesture only when isExpanded is true
    BackHandler(enabled = isExpanded) {
        // Collapsing the content disables this handler, so it does not
        // receive the next back event.
        isExpanded = false
    }

    if (isExpanded) {
        Text("Expanded content. Go back to collapse it.")
    } else {
        Button(onClick = { isExpanded = true }) {
            Text("Expand")
        }
    }
}

Intercettazione dell'albero composable

Se hai più composable BackHandler attivati a livelli diversi dell'albero composable, solo quello più interno intercetta l'evento Indietro.

Implementare la navigazione a ritroso personalizzata con le visualizzazioni

ComponentActivity, la classe base per FragmentActivity e AppCompatActivity, ti consente di controllare il comportamento del pulsante Indietro utilizzando il relativo OnBackPressedDispatcher, che puoi recuperare chiamando getOnBackPressedDispatcher().

Il OnBackPressedDispatcher controlla il modo in cui gli eventi del pulsante Indietro vengono inviati a uno o più oggetti OnBackPressedCallback. Il costruttore di OnBackPressedCallback accetta un valore booleano per lo stato iniziale attivato. Solo quando un callback è attivato, ad esempio quando isEnabled() restituisce true, il dispatcher chiama handleOnBackPressed() del callback per gestire l'evento del pulsante Indietro. Puoi modificare lo stato attivato chiamando setEnabled().

I callback vengono aggiunti utilizzando i metodi addCallback. Utilizza il addCallback() metodo che accetta un LifecycleOwner. In questo modo, il OnBackPressedCallback viene aggiunto solo quando il LifecycleOwner è Lifecycle.State.STARTED. L'attività rimuove anche i callback registrati quando il relativo LifecycleOwner viene eliminato, il che impedisce perdite di memoria e lo rende adatto all'uso in fragment o altri proprietari del ciclo di vita con una durata inferiore a quella dell'attività.

Ecco un'implementazione di callback di esempio:

class MyFragment : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // This callback will only be called when MyFragment is at least Started.
        val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
            // Handle the back button event
        }

        // The callback can be enabled or disabled here or in the lambda
    }
    ...
}

Puoi fornire più callback utilizzando addCallback(). In questo caso, i callback vengono richiamati nell'ordine inverso in cui vengono aggiunti: il callback aggiunto per ultimo è il primo a cui viene data la possibilità di gestire l'evento del pulsante Indietro. Ad esempio, se hai aggiunto tre callback denominati one, two e three in ordine, verranno richiamati nell'ordine three, two e one rispettivamente.

I callback seguono il pattern Chain of Responsibility. Ogni callback nella catena viene richiamato solo se il callback precedente non è stato attivato. Ciò significa che nell'esempio precedente, il callback two verrebbe richiamato solo se il callback three non è stato attivato. Il callback one verrebbe richiamato solo se il callback two non è stato attivato e questo pattern continua per eventuali callback aggiuntivi.

Tieni presente che, quando viene aggiunto utilizzando addCallback(), il callback non viene aggiunto alla catena di responsabilità finché il LifecycleOwner non entra nello stato Lifecycle.State.STARTED.

È vivamente consigliabile modificare lo stato attivato del callback per le modifiche temporanee, in quanto mantiene l'ordinamento descritto in precedenza, il che è particolarmente importante se hai callback registrati su più proprietari del ciclo di vita nidificati diversi.

Se vuoi rimuovere completamente il callback, chiama remove(). In genere non è necessario perché i callback vengono rimossi automaticamente quando il relativo LifecycleOwner viene eliminato.