Utilizzare Visualizzazioni in Scrivi

In una UI di Compose puoi includere una gerarchia di Visualizzazione Android. Questo approccio è particolarmente utile se vuoi utilizzare elementi UI non ancora disponibili in Compose, ad esempio AdView. Questo approccio ti consente anche di riutilizzare le visualizzazioni personalizzate che hai progettato.

Per includere un elemento di visualizzazione o una gerarchia, utilizza l'elemento componibile AndroidView . AndroidView riceve una funzione lambda che restituisce View. AndroidView fornisce anche un update callback che viene richiamato quando la visualizzazione è gonfia. AndroidView si ricompone ogni volta che una lettura State all'interno del callback cambia. AndroidView, come molti altri componibili integrati, richiede un parametro Modifier che può essere usato, ad esempio, per impostare la propria posizione nell'elemento componibile principale.

@Composable
fun CustomView() {
    var selectedItem by remember { mutableStateOf(0) }

    // Adds view to Compose
    AndroidView(
        modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
        factory = { context ->
            // Creates view
            MyView(context).apply {
                // Sets up listeners for View -> Compose communication
                setOnClickListener {
                    selectedItem = 1
                }
            }
        },
        update = { view ->
            // View's been inflated or state read in this block has been updated
            // Add logic here if necessary

            // As selectedItem is read here, AndroidView will recompose
            // whenever the state changes
            // Example of Compose -> View communication
            view.selectedItem = selectedItem
        }
    )
}

@Composable
fun ContentExample() {
    Column(Modifier.fillMaxSize()) {
        Text("Look at this CustomView!")
        CustomView()
    }
}

AndroidView con associazione vista

Per incorporare un layout XML, utilizza l'API AndroidViewBinding, fornita dalla libreria androidx.compose.ui:ui-viewbinding. Per farlo, il tuo progetto deve attivare l'associazione di visualizzazioni.

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

AndroidView in elenchi Lazy

Se utilizzi un AndroidView in un elenco Lazy (LazyColumn, LazyRow, Pager ecc.), valuta l'utilizzo dell'overload AndroidView introdotto nella versione 1.4.0-rc01. Questo sovraccarico consente a Compose di riutilizzare l'istanza View sottostante quando la composizione contenitore viene riutilizzata, come avviene per gli elenchi Lazy.

Questo sovraccarico di AndroidView aggiunge altri 2 parametri:

  • onReset: viene richiamato un callback per segnalare che View sta per essere riutilizzato. Deve essere un valore diverso da null per abilitare il riutilizzo delle visualizzazioni.
  • onRelease (facoltativo) - Un callback richiamato per segnalare che View è uscito dalla composizione e non verrà riutilizzato.

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun AndroidViewInLazyList() {
    LazyColumn {
        items(100) { index ->
            AndroidView(
                modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
                factory = { context ->
                    MyView(context)
                },
                update = { view ->
                    view.selectedItem = index
                },
                onReset = { view ->
                    view.clear()
                }
            )
        }
    }
}

Frammenti in Compose

Utilizza il componibile AndroidViewBinding per aggiungere un elemento Fragment in Compose. AndroidViewBinding prevede una gestione specifica per i frammenti, ad esempio la rimozione del frammento quando il componibile lascia la composizione.

Per farlo, gonfia un XML contenente FragmentContainerView come titolare del tuo Fragment.

Ad esempio, se hai definito my_fragment_layout.xml, potresti utilizzare un codice come questo durante la sostituzione dell'attributo XML android:name con il nome della classe di Fragment:

<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.compose.snippets.interop.MyFragment" />

Aumenta questo frammento in Compose come segue:

@Composable
fun FragmentInComposeExample() {
    AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<MyFragment>()
        // ...
    }
}

Se devi utilizzare più frammenti nello stesso layout, assicurati di aver definito un ID univoco per ogni FragmentContainerView.

Chiamare il framework Android da Compose

Compose opera all'interno delle classi del framework Android. Ad esempio, è ospitata su classi di Android View, come Activity o Fragment, e potrebbe utilizzare classi framework Android come Context, risorse di sistema Service o BroadcastReceiver.

Per saperne di più sulle risorse di sistema, vedi Risorse in Compose.

Composizioni locali

Le classi CompositionLocal consentono di trasmettere dati in modo implicito tramite funzioni componibili. Generalmente viene fornito un valore in un determinato nodo della struttura ad albero dell'interfaccia utente. Questo valore può essere utilizzato dai suoi discendenti componibili senza dichiarare CompositionLocal come parametro nella funzione componibile.

CompositionLocal viene utilizzato per propagare i valori per i tipi di framework Android in Compose, ad esempio Context, Configuration o View, in cui il codice di Compose è ospitato con i corrispondenti valori LocalContext, LocalConfiguration o LocalView. Tieni presente che le classi CompositionLocal sono precedute dal prefisso Local per una migliore rilevabilità con il completamento automatico nell'IDE.

Accedi al valore corrente di CompositionLocal utilizzando la relativa proprietà current. Ad esempio, il codice seguente mostra un messaggio toast fornendo LocalContext.current nel metodo Toast.makeToast.

@Composable
fun ToastGreetingButton(greeting: String) {
    val context = LocalContext.current
    Button(onClick = {
        Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show()
    }) {
        Text("Greet")
    }
}

Per un esempio più completo, consulta la sezione Case study: BroadcastRicevirs alla fine di questo documento.

Altre interazioni

Se non è stata definita un'utilità per l'interazione di cui hai bisogno, la best practice è seguire le linee guida generali di Compose: i dati scorrono verso il basso e gli eventi scorrono (trattati più approfonditamente in Thinking in Compose). Ad esempio, questo componibile avvia un'attività diversa:

class OtherInteractionsActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // get data from savedInstanceState
        setContent {
            MaterialTheme {
                ExampleComposable(data, onButtonClick = {
                    startActivity(Intent(this, MyActivity::class.java))
                })
            }
        }
    }
}

@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
    Button(onClick = onButtonClick) {
        Text(data.title)
    }
}

Case study: ricevitori di trasmissioni

Per un esempio più realistico delle funzionalità di cui vuoi eseguire la migrazione o l'implementazione in Compose, e per mostrare CompositionLocal e gli effetti collaterali, supponiamo che sia necessario registrare BroadcastReceiver da una funzione componibile.

La soluzione utilizza LocalContext per usare il contesto corrente, nonché gli effetti collaterali di rememberUpdatedState e DisposableEffect.

@Composable
fun SystemBroadcastReceiver(
    systemAction: String,
    onSystemEvent: (intent: Intent?) -> Unit
) {
    // Grab the current context in this part of the UI tree
    val context = LocalContext.current

    // Safely use the latest onSystemEvent lambda passed to the function
    val currentOnSystemEvent by rememberUpdatedState(onSystemEvent)

    // If either context or systemAction changes, unregister and register again
    DisposableEffect(context, systemAction) {
        val intentFilter = IntentFilter(systemAction)
        val broadcast = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                currentOnSystemEvent(intent)
            }
        }

        context.registerReceiver(broadcast, intentFilter)

        // When the effect leaves the Composition, remove the callback
        onDispose {
            context.unregisterReceiver(broadcast)
        }
    }
}

@Composable
fun HomeScreen() {

    SystemBroadcastReceiver(Intent.ACTION_BATTERY_CHANGED) { batteryStatus ->
        val isCharging = /* Get from batteryStatus ... */ true
        /* Do something if the device is charging */
    }

    /* Rest of the HomeScreen */
}

Passaggi successivi

Ora che conosci le API di interoperabilità quando utilizzi Compose in Views e viceversa, esplora la pagina Altre considerazioni per scoprire di più.