Como usar o Compose em visualizações

É possível adicionar uma interface com base no Compose a um app já existente que usa um design com base em visualização.

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

ViewCompositionStrategy define quando a composição deve ser descartada. O padrão, ViewCompositionStrategy.Default, descarta a composição quando o ComposeView é desanexado da janela, a menos que faça parte de um contêiner de pool, como um RecyclerView. Em um app de atividade única somente com Compose, esse comportamento padrão é o que você quer. No entanto, se você estiver adicionando o Compose de forma incremental na sua base de código, esse comportamento poderá causar perda de estado em alguns cenários.

Para mudar o ViewCompositionStrategy, chame o método setViewCompositionStrategy() e forneça uma estratégia diferente.

A tabela abaixo resume os diferentes cenários em que você pode usar ViewCompositionStrategy:

ViewCompositionStrategy Descrição e cenário de interoperabilidade
DisposeOnDetachedFromWindow A composição será descartada quando o ComposeView subjacente for removido da janela. Desde então, foi substituído por DisposeOnDetachedFromWindowOrReleasedFromPool.

Cenário de interoperabilidade:

* ComposeView, seja o único elemento na hierarquia de visualização ou no contexto de uma tela mista de visualização/Compose (não em Fragment).
DisposeOnDetachedFromWindowOrReleasedFromPool (padrão) Semelhante a DisposeOnDetachedFromWindow, quando a composição não está em um contêiner de pooling, como um RecyclerView. Se ele estiver em um contêiner de agrupamento, será descartado quando o contêiner se separar da janela ou quando o item for descartado (ou seja, quando o pool estiver cheio).

Cenário de interoperabilidade:

* ComposeView se é o único elemento na hierarquia de visualização ou no contexto de uma tela mista de visualização/composição (não em um fragmento).
* ComposeView como um item em um contêiner de agrupamento, como RecyclerView.
DisposeOnLifecycleDestroyed A composição será descartada quando o Lifecycle fornecido for destruído.

Cenário de interoperabilidade

* ComposeView na visualização de um fragmento.
DisposeOnViewTreeLifecycleDestroyed A composição será descartada quando o Lifecycle pertencente ao LifecycleOwner retornado por ViewTreeLifecycleOwner.get da próxima janela a que a visualização está anexada for destruído.

Cenário de interoperabilidade:

* ComposeView em uma visualização de fragmento.
* ComposeView em uma visualização em que o ciclo de vida ainda não é conhecido.

ComposeView em fragmentos

Se você quiser incorporar o conteúdo da interface 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:

<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/text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />

  <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 ExampleFragmentXml : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val view = inflater.inflate(R.layout.fragment_example, container, false)
        val composeView = view.findViewById<ComposeView>(R.id.compose_view)
        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
    }
}

Como alternativa, você também pode usar a vinculação de visualizações para acessar referências ao ComposeView referenciando a classe de vinculação gerada para seu arquivo de layout XML:

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 interface 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>

Visualizar elementos combináveis no Layout Editor

Também é possível conferir uma prévia dos elementos combináveis no Layout Editor para seu layout XML que contém um ComposeView. Assim, você pode ver como os elementos combináveis ficam em um layout misto de visualizações e Compose.

Suponha que você queira mostrar o seguinte elemento combinável no Layout Editor. Observação Os elementos combináveis anotados com @Preview são bons candidatos para visualização no Layout Editor.

@Preview
@Composable
fun GreetingPreview() {
    Greeting(name = "Android")
}

Para mostrar esse elemento combinável, use o atributo de ferramentas tools:composableName e defina o valor dele como o nome totalmente qualificado do elemento combinável a ser visualizado no layout.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <androidx.compose.ui.platform.ComposeView
      android:id="@+id/my_compose_view"
      tools:composableName="com.example.compose.snippets.interop.InteroperabilityAPIsSnippetsKt.GreetingPreview"
      android:layout_height="match_parent"
      android:layout_width="match_parent"/>

</LinearLayout>

Elemento combinável mostrado no Layout Editor

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.