Fournir un retour arrière personnalisé

Les utilisateurs naviguent en arrière dans les écrans à l'aide du retour en arrière. La plupart des appareils Android sont équipés d'un bouton "Retour" (physique, logiciel ou basé sur un geste). En règle générale, vous ne devez pas ajouter de bouton "Retour" à votre application. Toutefois, les appareils Android Automotive OS (AAOS) en mode de compatibilité utilisent un bouton "Retour" du système. Il gère la navigation, vous n'avez donc pas besoin d'en ajouter un. Pour en savoir plus, consultez Mode de compatibilité AAOS.

Android conserve une pile "Retour" des destinations à mesure que l'utilisateur parcourt votre application. Cela permet généralement à Android de naviguer correctement vers les destinations précédentes en appuyant sur le bouton "Retour". Toutefois, dans certains cas, il se peut que votre application doive implémenter son propre comportement "Retour" afin d'offrir la meilleure expérience utilisateur possible. Par exemple, lorsque vous utilisez une WebView, vous pouvez ignorer le comportement par défaut du bouton "Retour" pour permettre à l'utilisateur de revenir dans son historique de navigation Web plutôt que dans les écrans précédents de votre application.

Implémenter un retour en arrière personnalisé dans Compose

Dans Jetpack Compose, vous pouvez gérer le retour en arrière personnalisé à l'aide du BackHandler composable.

Lorsque vous utilisez Navigation Compose, vous utilisez généralement NavController.navigateUp() ou NavController.popBackStack() pour accéder à l'écran précédent dans la pile "Retour". Toutefois, BackHandler est utile dans les cas où vous souhaitez implémenter un comportement personnalisé lorsque l'utilisateur appuie sur le bouton "Retour" du système ou utilise le geste de retour. Par exemple, si vous affichez un WebView dans votre application, vous pouvez autoriser les utilisateurs à revenir dans l'historique de navigation lorsqu'ils appuient sur le bouton "Retour" du système.

Utilisation de base

Voici un exemple montrant comment intercepter l'événement de retour pour contrôler l'état personnalisé (comme la réduction d'un panneau) :

@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")
        }
    }
}

Interception de l'arborescence des composables

Si vous avez plusieurs composables BackHandler activés à différents niveaux de votre arborescence de composables, seul le plus interne intercepte l'événement de retour.

Implémenter un retour arrière personnalisé avec des vues

ComponentActivity, la classe de base pour FragmentActivity et AppCompatActivity, vous permet de contrôler le comportement du bouton "Retour" à l'aide de son OnBackPressedDispatcher, que vous pouvez récupérer en appelant getOnBackPressedDispatcher().

Le OnBackPressedDispatcher contrôle la manière dont les événements du bouton "Retour" sont distribués à un ou plusieurs objets OnBackPressedCallback. Le constructeur de OnBackPressedCallback prend un booléen pour l'état d'activation initial. Ce n'est que lorsqu' un rappel est activé, par exemple lorsque isEnabled() renvoie true, que le répartiteur appelle le handleOnBackPressed() du rappel pour gérer l'événement du bouton "Retour" . Vous pouvez modifier l'état d'activation en appelant setEnabled().

Les rappels sont ajoutés à l'aide des méthodes addCallback. Utilisez la addCallback() méthode qui prend un LifecycleOwner. De cette façon, le OnBackPressedCallback n'est ajouté que lorsque le LifecycleOwner est Lifecycle.State.STARTED. L'activité supprime également les rappels enregistrés lorsque le LifecycleOwner associé est détruit. Cela permet d'éviter les fuites de mémoire et de les utiliser dans des fragments ou d'autres propriétaires de cycle de vie ayant une durée de vie plus courte.

Voici un exemple d'implémentation du rappel :

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
    }
    ...
}

Vous pouvez fournir plusieurs rappels à l'aide de addCallback(). Dans ce cas, les rappels sont appelés dans l'ordre inverse de leur ajout. Le rappel ajouté en dernier est le premier à avoir la possibilité de gérer l'événement du bouton "Retour". Par exemple, si vous avez ajouté trois rappels nommés one, two et three dans l'ordre, ils seraient appelés dans l'ordre three, two et one, respectivement.

Les rappels suivent le schéma de chaîne de responsabilité. Chaque rappel de la chaîne n'est appelé que si le rappel précédent n'a pas été activé. Cela signifie que dans l'exemple précédent, le rappel two ne serait appelé que si le rappel three n'était pas activé. Le rappel one ne serait appelé que si le rappel two n'était pas activé, et ce schéma se poursuit pour tous les rappels supplémentaires.

Notez que lorsqu'il est ajouté à l'aide de addCallback(), le rappel n'est pas ajouté à la chaîne de responsabilité tant que le LifecycleOwner n'est pas dans l'état Lifecycle.State.STARTED.

Il est fortement recommandé de modifier l'état d'activation du rappel pour les modifications temporaires, car cela conserve l'ordre décrit précédemment, ce qui est particulièrement important si vous avez enregistré des rappels pour plusieurs propriétaires de cycle de vie imbriqués différents.

Si vous souhaitez supprimer complètement le rappel, appelez remove(). Cela n'est généralement pas nécessaire, car les rappels sont automatiquement supprimés lorsque le LifecycleOwner associé est détruit.