Como usar o Compose em visualizações

É possível adicionar uma IU com base no Compose a um app já pronto que usa um design com base em visualizações.

Para criar uma tela totalmente baseada no Compose, faça sua atividade chamar o método setContent() e transmitir as funções combináveis que você quer usar.

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

Esse código é parecido com o que você encontraria em um app feito inteiramente com o Compose.

ViewCompositionStrategy por ComposeView

Por padrão, o Compose descarta a composição sempre que a visualização é desanexada de uma janela. Os tipos de View da IU do Compose, como ComposeView e AbstractComposeView, usam uma ViewCompositionStrategy que define esse comportamento.

Por padrão, o Compose usa a estratégia DisposeOnDetachedFromWindowOrReleasedFromPool. No entanto, esse valor padrão pode ser indesejável em algumas situações em que os tipos de View da IU do Compose são usados em:

  • Fragmentos. A composição precisa seguir o ciclo de vida de visualização do fragmento para que os tipos de View da IU do Compose salvem o estado.

  • Transições. Sempre que a View da IU do Compose é usada como parte de uma transição, ela é removida da janela assim que a transição é iniciada, e não quando ela termina. Isso faz com que o elemento combinável descarte o estado enquanto ainda está na tela.

  • Sua View personalizada gerenciada por ciclo de vida.

Em algumas dessas situações, o app também poderá apresentar vazamento lento de memória nas instâncias de composição, a menos que você chame manualmente AbstractComposeView.disposeComposition.

Para descartar as composições automaticamente quando elas não forem mais necessárias, defina uma estratégia diferente ou crie uma própria chamando o método setViewCompositionStrategy. Por exemplo, a estratégia DisposeOnLifecycleDestroyed descarta a composição quando o lifecycle é destruído. Essa estratégia é adequada para os tipos de View da IU do Compose que compartilham uma relação direta com um LifecycleOwner conhecido. Quando o LifecycleOwner não for conhecido, o DisposeOnViewTreeLifecycleDestroyed poderá ser usado.

Confira essa API em ação em ComposeView em fragmentos.

ComposeView em fragmentos

Se você quiser incorporar o conteúdo da IU do Compose em um fragmento ou um layout de visualização já existente, use ComposeView e chame o método setContent() dele. ComposeView é uma View para Android.

Você pode colocar a ComposeView no seu layout XML como qualquer outra 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>

No código-fonte do Kotlin, infle o layout usando o recurso de layout definido no XML. Em seguida, acesse a ComposeView usando o ID do XML, defina uma estratégia de composição que funcione melhor para a View host e chame setContent() para usar o 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
    }
}

Dois elementos de texto um pouco diferentes, um acima do outro

Figura 1. Isso mostra a saída do código que adiciona elementos do Compose a uma hierarquia de IU de visualização. A mensagem "Hello Android!" é exibida por um widget TextView. A mensagem "Hello Compose!" é exibida por um elemento de texto do Compose.

Também será possível incluir uma ComposeView diretamente em um fragmento se a tela cheia for criada com o Compose, o que permite evitar totalmente o uso de um arquivo de layout 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!")
                }
            }
        }
    }
}

Várias instâncias de ComposeView no mesmo layout

Se houver vários elementos ComposeView no mesmo layout, cada um precisará ter um ID exclusivo para que o 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
                // ...
            }
        )
    }
}

Os IDs ComposeView são definidos no arquivo res/values/ids.xml:

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

Próximas etapas

Agora que você aprendeu sobre as APIs de interoperabilidade para usar o Compose em visualizações, saiba como usar as visualizações no Compose.