Utilizzare la funzionalità Scrivi in Visualizzazioni

Puoi aggiungere un'UI basata su Compose a un'app esistente che utilizza un design basato su visualizzazioni.

Per creare una nuova schermata interamente basata su Compose, fai in modo che la tua attività chiami il metodo setContent() e passi le funzioni componibili che preferisci.

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

Questo codice è esattamente quello che troveresti in un'app solo Compose.

ViewCompositionStrategy per ComposeView

ViewCompositionStrategy definisce quando la composizione deve essere eliminata. Il valore predefinito, ViewCompositionStrategy.Default, elimina la composizione quando l'oggetto ComposeView sottostante si scollega dalla finestra, a meno che non faccia parte di un container di pooling come un RecyclerView. In un'app solo Compose con una singola attività, questo comportamento predefinito è quello che ti aspetteresti. Tuttavia, se aggiungi Compose in modo incrementale alla tua codebase, questo comportamento potrebbe causare la perdita di stato in alcuni scenari.

Per modificare ViewCompositionStrategy, chiama il metodo setViewCompositionStrategy() e fornisci una strategia diversa.

La tabella seguente riassume i diversi scenari in cui puoi utilizzare ViewCompositionStrategy:

ViewCompositionStrategy Descrizione e scenario di interoperabilità
DisposeOnDetachedFromWindow La composizione verrà eliminata quando l'oggetto ComposeView sottostante viene scollegato dalla finestra. Da allora è stato sostituito da DisposeOnDetachedFromWindowOrReleasedFromPool.

Scenario di interoperabilità:

* ComposeView se è l'unico elemento nella gerarchia di oggetti View o nel contesto di una schermata mista View/Compose (non in Fragment).
DisposeOnDetachedFromWindowOrReleasedFromPool (Predefinito) Simile a DisposeOnDetachedFromWindow, quando la composizione non si trova in un container di pooling, ad esempio un RecyclerView. Se si trova in un container di pooling, verrà eliminato quando il container di pooling stesso si scollega dalla finestra o quando l'elemento viene eliminato (ovvero quando il pool è pieno).

Scenario di interoperabilità:

* ComposeView se è l'unico elemento nella gerarchia di oggetti View o nel contesto di una schermata mista View/Compose (non in Fragment).
* ComposeView come elemento in un contenitore di raggruppamento come RecyclerView.
DisposeOnLifecycleDestroyed La composizione verrà eliminata quando l'oggetto Lifecycle fornito viene eliminato.

Scenario di interoperabilità

* ComposeView nella visualizzazione di un frammento.
DisposeOnViewTreeLifecycleDestroyed La composizione verrà eliminata quando l'oggetto Lifecycle di proprietà dell'oggetto LifecycleOwner restituito dall'oggetto ViewTreeLifecycleOwner.get della finestra successiva a cui è collegata la vista viene eliminato.

Scenario di interoperabilità:

* ComposeView nella vista di un frammento.
* ComposeView in una View in cui il ciclo di vita non è ancora noto.

ComposeView in Frammenti (passaggio di transizione)

Se vuoi incorporare contenuti dell'interfaccia utente di Compose in un frammento o in un layout di visualizzazione esistente, utilizza ComposeView e chiama il relativo metodo setContent(). ComposeView è un View Android.

Puoi inserire ComposeView nel tuo layout XML come qualsiasi altro 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>

Nel codice sorgente Kotlin, esegui l'inflate del layout dalla risorsa di layout definita in XML. Poi, recupera ComposeView utilizzando l'ID XML, imposta una strategia di composizione che funzioni meglio per l'host View e chiama setContent() per utilizzare 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
    }
}

In alternativa, puoi anche utilizzare l'associazione delle visualizzazioni per ottenere riferimenti a ComposeView facendo riferimento alla classe di associazione generata per il file di 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
    }
}

Due elementi di testo leggermente diversi, uno sopra l'altro

Figura 1. Questo mostra l'output del codice che aggiunge elementi di Compose in una gerarchia della UI di View. Il testo "Hello Android!" viene visualizzato da un widget TextView. Il testo "Hello Compose!" viene visualizzato da un elemento di testo di Compose.

Puoi anche includere un ComposeView direttamente in un frammento se lo schermo intero è creato con Compose, il che ti consente di evitare completamente l'utilizzo di un file di 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!")
                }
            }
        }
    }
}

Più istanze di ComposeView nello stesso layout

Se nello stesso layout sono presenti più elementi ComposeView, ognuno deve avere un ID univoco affinché savedInstanceState funzioni.

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

Gli ID ComposeView sono definiti nel file res/values/ids.xml:

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

Visualizza l'anteprima dei componibili nell'editor di layout

Puoi anche visualizzare l'anteprima dei componibili nell'editor di layout per il tuo layout XML contenente un ComposeView. In questo modo puoi vedere l'aspetto dei tuoi composabili all'interno di un layout misto di Views e Compose.

Supponiamo che tu voglia visualizzare il seguente componibile nell'editor di layout. Tieni presente che i componibili annotati con @Preview sono buoni candidati per l'anteprima nell'editor di layout.

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

Per visualizzare questo composable, utilizza l'attributo tools tools:composableName e imposta il suo valore sul nome completo del composable da visualizzare in anteprima nel 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>

Composable visualizzato nell'editor di layout