Utiliser Compose dans les vues

Vous pouvez ajouter une UI basée sur Compose à une application existante qui utilise un design basé sur les vues.

Pour créer un écran entièrement basé sur Compose, demandez à votre activité d'appeler la méthode setContent() et de transmettre toutes les fonctions modulables de votre choix.

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

        setContent { // In here, we can call composables!
            MaterialTheme {
                Greeting(name = "compose")
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

Ce code ressemble à ce que vous trouveriez dans une application Compose.

Rechercher ComposeView dans ViewCompositionStrategy

Par défaut, Compose supprime la composition dès lors que la View est dissociée d'une fenêtre. Les types de View de l'interface utilisateur Compose, comme ComposeView et AbstractComposeView, utilisent un élément ViewCompositionStrategy qui définit ce comportement.

Par défaut, Compose utilise la stratégie DisposeOnDetachedFromWindowOrReleasedFromPool. Cependant, cette valeur par défaut est parfois indésirable, notamment lorsque les types de View de l'UI Compose sont utilisés dans les éléments suivants :

  • Les fragments. Pour conserver l'état, la composition doit suivre le cycle de vie de la vue du fragment pour les types de View de l'UI Compose.

  • Les transitions. Chaque fois que la View de l'UI Compose est utilisée dans le cadre d'une transition, elle est détachée de sa fenêtre au début de la transition et non à la fin : votre composable est alors supprimé alors qu'il s'affiche à l'écran.

  • Votre propre View personnalisée gérée par le cycle de vie.

Dans certaines de ces situations, l'application peut être sujette à des fuites de mémoire progressives au niveau des instances de composition, sauf si vous appelez AbstractComposeView.disposeComposition manuellement.

Vous pouvez supprimer automatiquement les compositions qui ne sont plus nécessaires en définissant une autre stratégie, ou en créant la vôtre en appelant la méthode setViewCompositionStrategy. Par exemple, la stratégie DisposeOnLifecycleDestroyed supprime la composition lorsque le lifecycle est détruit. Cette stratégie convient aux types View d'interface utilisateur Compose qui partagent une relation de type 1:1 avec un LifecycleOwner connu. Lorsque LifecycleOwner n'est pas connu, vous pouvez utiliser DisposeOnViewTreeLifecycleDestroyed.

Consultez ComposeView dans les fragments pour découvrir comment fonctionne cette API.

ComposeView dans les fragments

Si vous souhaitez intégrer le contenu de l'interface utilisateur Compose dans un fragment ou une mise en page "View" existante, utilisez ComposeView et appelez sa méthode setContent(). ComposeView est une View Android.

Vous pouvez placer la ComposeView dans votre mise en page XML comme n'importe quelle autre View :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/hello_world"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello Android!" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Dans le code source en Kotlin, gonflez la mise en page à partir de la ressource de mise en page définie en XML. Obtenez ensuite ComposeView à l'aide de l'ID XML, définissez la stratégie de composition la plus adaptée pour l'hôte View, puis appelez setContent() pour utiliser Compose.

class ExampleFragment : Fragment() {

    private var _binding: FragmentExampleBinding? = null

    // This property is only valid between onCreateView and onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentExampleBinding.inflate(inflater, container, false)
        val view = binding.root
        binding.composeView.apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                // In Compose world
                MaterialTheme {
                    Text("Hello Compose!")
                }
            }
        }
        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

Deux éléments textuels légèrement différents, l'un au-dessus de l'autre.

Figure 1 : Cette image montre la sortie du code qui ajoute des éléments Compose dans une hiérarchie d'UI. Un widget TextView affiche le message texte "Hello Android!". Un élément de texte Compose affiche le texte "Hello Compose!".

Vous pouvez également inclure une ComposeView directement dans un fragment si votre mode plein écran est conçu avec Compose, ce qui vous évite d'utiliser un fichier de mise en page XML.

class ExampleFragmentNoXml : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                MaterialTheme {
                    // In Compose world
                    Text("Hello Compose!")
                }
            }
        }
    }
}

Plusieurs instances ComposeView dans la même mise en page

S'il existe plusieurs éléments ComposeView dans la même mise en page, chacun doit disposer d'un ID unique pour que savedInstanceState fonctionne correctement.

class ExampleFragmentMultipleComposeView : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View = LinearLayout(requireContext()).apply {
        addView(
            ComposeView(requireContext()).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                id = R.id.compose_view_x
                // ...
            }
        )
        addView(TextView(requireContext()))
        addView(
            ComposeView(requireContext()).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                id = R.id.compose_view_y
                // ...
            }
        )
    }
}

Les ID ComposeView sont définis dans le fichier res/values/ids.xml :

<resources>
    <item name="compose_view_x" type="id" />
    <item name="compose_view_y" type="id" />
</resources>

Étapes suivantes

Maintenant que vous connaissez les API d'interopérabilité pour utiliser Compose dans les vues, découvrez comment utiliser les vues dans Compose.