Utiliser les vues dans Compose

Vous pouvez inclure une hiérarchie des vues Android dans une interface utilisateur Compose. Cette approche est particulièrement utile si vous souhaitez utiliser des éléments d'interface utilisateur qui ne sont pas encore disponibles dans Compose, comme AdView. Cela vous permet également de réutiliser des vues personnalisées.

Pour inclure un élément ou une hiérarchie de vues, utilisez le composable AndroidView . AndroidView reçoit un lambda qui renvoie une View. AndroidView fournit également un rappel update, qui est appelé lorsque la vue est gonflée. AndroidView se recompose chaque fois qu'une lecture State du rappel change. Comme de nombreux autres composables intégrés, AndroidView utilise un paramètre Modifier qui peut servir, par exemple, à définir sa position dans le composable parent.

@Composable
fun CustomView() {
    var selectedItem by remember { mutableIntStateOf(0) }

    // Adds view to Compose
    AndroidView(
        modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
        factory = { context ->
            // Creates view
            MyView(context).apply {
                // Sets up listeners for View -> Compose communication
                setOnClickListener {
                    selectedItem = 1
                }
            }
        },
        update = { view ->
            // View's been inflated or state read in this block has been updated
            // Add logic here if necessary

            // As selectedItem is read here, AndroidView will recompose
            // whenever the state changes
            // Example of Compose -> View communication
            view.selectedItem = selectedItem
        }
    )
}

@Composable
fun ContentExample() {
    Column(Modifier.fillMaxSize()) {
        Text("Look at this CustomView!")
        CustomView()
    }
}

AndroidView avec liaison de vue

Pour intégrer une mise en page XML, utilisez l'API AndroidViewBinding fournie par la bibliothèque androidx.compose.ui:ui-viewbinding. Pour ce faire, votre projet doit activer la liaison de vue.

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

AndroidView dans les listes différées

Si vous utilisez un AndroidView dans une liste Lazy (LazyColumn, LazyRow, Pager, etc.), envisagez d'utiliser la surcharge AndroidView introduite dans la version 1.4.0-rc01. Cette surcharge permet à Compose de réutiliser l'instance View sous-jacente lorsque la composition contenant est réutilisée, comme c'est le cas pour les listes Lazy.

Cette surcharge de AndroidView ajoute deux paramètres :

  • onReset : rappel appelé pour signaler que le View est sur le point d'être réutilisé. Cette valeur ne doit pas être nulle pour activer la réutilisation des vues.
  • onRelease (facultatif) : rappel invoqué pour signaler que View a quitté la composition et ne sera plus réutilisé.

@Composable
fun AndroidViewInLazyList() {
    LazyColumn {
        items(100) { index ->
            AndroidView(
                modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
                factory = { context ->
                    MyView(context)
                },
                update = { view ->
                    view.selectedItem = index
                },
                onReset = { view ->
                    view.clear()
                }
            )
        }
    }
}

Fragments dans Compose

Utilisez le composable AndroidFragment pour ajouter un Fragment dans Compose. AndroidFragment offre une gestion spécifique au fragment, comme la suppression du fragment lorsque le composable quitte la composition.

Pour inclure un fragment, utilisez le composable AndroidFragment. Vous transmettez une classe Fragment à AndroidFragment, qui ajoute ensuite une instance de cette classe directement dans la composition. AndroidFragment fournit également un objet fragmentState pour créer le AndroidFragment avec un état donné, arguments à transmettre au nouveau fragment, et un rappel onUpdate qui fournit le fragment à partir de la composition. Comme de nombreux autres composables intégrés, AndroidFragment accepte un paramètre Modifier que vous pouvez utiliser, par exemple, pour définir sa position dans le composable parent.

Appelez AndroidFragment dans Compose comme suit :

@Composable
fun FragmentInComposeExample() {
    AndroidFragment<MyFragment>()
}

Appeler le framework Android à partir de Compose

Compose fonctionne dans les classes du framework Android. Par exemple, il est hébergé sur les classes de vues Android, comme Activity ou Fragment, et peut utiliser des classes du framework Android comme Context, les ressources système, Service ou encore BroadcastReceiver.

Pour en savoir plus sur les ressources système, consultez la section Ressources dans Compose.

Compositions locales

Les classes CompositionLocal permettent de transmettre des données implicitement via des fonctions modulables. Elles sont généralement accompagnées d'une valeur dans un nœud spécifique de l'arborescence de l'interface utilisateur. Cette valeur peut être utilisée par ses descendants composables sans déclarer le CompositionLocal en tant que paramètre dans la fonction modulable.

CompositionLocal permet de propager des valeurs pour les types de frameworks Android dans Compose, tels que Context, Configuration ou View, dans lesquels le code Compose est hébergé avec les éléments LocalContext, LocalConfiguration ou LocalView correspondants. Notez que les classes CompositionLocal sont précédées de Local pour une meilleure visibilité avec la saisie semi-automatique dans l'IDE.

Pour accéder à la valeur actuelle de CompositionLocal, utilisez sa propriété current. Par exemple, le code ci-dessous affiche un toast en fournissant le LocalContext.current dans la méthode Toast.makeToast.

@Composable
fun ToastGreetingButton(greeting: String) {
    val context = LocalContext.current
    Button(onClick = {
        Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show()
    }) {
        Text("Greet")
    }
}

Pour un exemple plus complet, consultez la section Étude de cas : BroadcastReceivers à la fin de ce document.

Autres interactions

Si aucun utilitaire n'est défini pour l'interaction dont vous avez besoin, nous vous recommandons de suivre les consignes générales de Compose : le flux de données descend, le flux d'événements monte. Plus de détails sont disponibles dans Raisonnement dans Compose. Par exemple, ce composable lance une autre activité :

class OtherInteractionsActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // get data from savedInstanceState
        setContent {
            MaterialTheme {
                ExampleComposable(data, onButtonClick = {
                    startActivity(Intent(this, MyActivity::class.java))
                })
            }
        }
    }
}

@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
    Button(onClick = onButtonClick) {
        Text(data.title)
    }
}

Étude de cas : broadcast receivers

Pour obtenir un exemple plus concret des fonctionnalités que vous pouvez migrer ou implémenter dans ComposeCompositionLocal, ainsi que des effets secondaires, imaginons qu'un BroadcastReceiver doit être enregistré à partir d'une fonction modulable.

La solution utilise LocalContext pour utiliser le contexte actuel, ainsi que les effets secondaires rememberUpdatedState et DisposableEffect.

@Composable
fun SystemBroadcastReceiver(
    systemAction: String,
    onSystemEvent: (intent: Intent?) -> Unit
) {
    // Grab the current context in this part of the UI tree
    val context = LocalContext.current

    // Safely use the latest onSystemEvent lambda passed to the function
    val currentOnSystemEvent by rememberUpdatedState(onSystemEvent)

    // If either context or systemAction changes, unregister and register again
    DisposableEffect(context, systemAction) {
        val intentFilter = IntentFilter(systemAction)
        val broadcast = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                currentOnSystemEvent(intent)
            }
        }

        context.registerReceiver(broadcast, intentFilter)

        // When the effect leaves the Composition, remove the callback
        onDispose {
            context.unregisterReceiver(broadcast)
        }
    }
}

@Composable
fun HomeScreen() {

    SystemBroadcastReceiver(Intent.ACTION_BATTERY_CHANGED) { batteryStatus ->
        val isCharging = /* Get from batteryStatus ... */ true
        /* Do something if the device is charging */
    }

    /* Rest of the HomeScreen */
}

Étapes suivantes

Maintenant que vous connaissez les API d'interopérabilité lorsque vous utilisez Compose dans les vues et inversement, consultez la page Autres considérations pour en savoir plus.