Cómo usar Compose en Views

Puedes agregar una IU basada en Compose a una app existente que use un diseño basado en View.

Para crear una pantalla nueva basada íntegramente en Compose, haz que tu actividad llame al método setContent() y pasa las funciones de componibilidad que quieras.

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

Este código es similar al que se encuentra en una app exclusivamente de Compose.

ViewCompositionStrategy para ComposeView

De forma predeterminada, Compose elimina la composición cuando la vista se separa de una ventana. Los tipos de View de la IU de Compose, como ComposeView y AbstractComposeView, usan ViewCompositionStrategy para definir este comportamiento.

De forma predeterminada, Compose usa la estrategia DisposeOnDetachedFromWindowOrReleasedFromPool. Sin embargo, este valor predeterminado podría no ser deseable cuando se usan los tipos de View de la IU de Compose en los siguientes elementos:

  • Fragmentos: La composición debe seguir el ciclo de vida de la vista del fragmento para los tipos de View de la IU de Compose para guardar el estado.

  • Transiciones: Cada vez que se use la View de la IU de Compose como parte de una transición, se separa de la ventana cuando comienza y no cuando finaliza, por lo que elimina su estado mientras aún está en pantalla.

  • Tu propia View personalizada administrada por el ciclo de vida.

En algunas de estas situaciones, es posible que la app también pierda memoria lentamente a partir de instancias de composición, a menos que llames de forma manual a AbstractComposeView.disposeComposition.

Para eliminar automáticamente composiciones cuando ya no sean necesarias, configura una estrategia diferente o crea una con llamadas al método setViewCompositionStrategy. Por ejemplo, la estrategia DisposeOnLifecycleDestroyed elimina la composición cuando se destruye lifecycle. Esta estrategia es adecuada para los tipos de View de la IU de Compose que se relacionan 1 a 1 con un LifecycleOwner conocido. Cuando no se conoce el LifecycleOwner, puedes usar DisposeOnViewTreeLifecycleDestroyed.

Observa cómo funciona esta API en ComposeView en Fragments.

ComposeView en Fragments

Si quieres incorporar contenido de la IU de Compose a un fragmento o un diseño de View existente, usa ComposeView y llama a su método setContent(). ComposeView es una View de Android.

Puedes colocar ComposeView en tu diseño XML como cualquier otro elemento 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>

En el código fuente de Kotlin, aumenta el diseño del recurso de diseño que se define en XML. Luego, obtén la ComposeView con el ID de XML, establece la estrategia de composición que mejor se adapte a la View del host y llama a setContent() para usar 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
    }
}

Dos elementos de texto ligeramente diferentes, uno sobre el otro

Figura 1: Muestra el resultado del código que agrega elementos de Compose a una jerarquía de la IU de View. El texto "Hello Android!" se muestra en un widget TextView. El texto "Hello Compose!" se muestra en un elemento de texto de Compose.

También puedes incluir una ComposeView directamente en un fragmento si toda tu pantalla está compilada con Compose, por lo que no necesitas usar un archivo de diseño 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!")
                }
            }
        }
    }
}

Varias instancias de ComposeView en el mismo diseño

Si hay varios elementos ComposeView en el mismo diseño, cada uno debe tener un ID único para que savedInstanceState funcione.

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

Los IDs de ComposeView se definen en el archivo res/values/ids.xml:

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

Próximos pasos

Ahora que conoces las APIs de interoperabilidad para usar Compose en Views, obtén información sobre cómo usar Views en Compose.