Utilizzare Visualizzazioni in Scrivi

Puoi includere una gerarchia di oggetti View Android in un'interfaccia utente Compose. Questo approccio è particolarmente utile se vuoi utilizzare elementi dell'interfaccia utente non ancora disponibili in Compose, come AdView. Questo approccio ti consente anche di riutilizzare le viste personalizzate che potresti aver progettato.

Per includere un elemento di visualizzazione o una gerarchia, utilizza il componibile AndroidView . AndroidView viene passata una lambda che restituisce un View. AndroidView fornisce anche un update callback chiamato quando la visualizzazione viene visualizzata. AndroidView si ricompone ogni volta che cambia un State letto all'interno del callback. AndroidView, come molti altri composable integrati, accetta un parametro Modifier che può essere utilizzato, ad esempio, per impostarne la posizione nel composable padre.

@Composable
fun CustomView() {
    var selectedItem by remember { mutableIntStateOf(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 l'associazione di visualizzazione

Per incorporare un layout XML, utilizza l'API AndroidViewBinding, fornita dalla libreria androidx.compose.ui:ui-viewbinding. Per farlo, il progetto deve abilitare il binding delle visualizzazioni.

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

AndroidView in Lazy lists

Se utilizzi un AndroidView in un elenco pigro (LazyColumn, LazyRow, Pager e così via), valuta la possibilità di utilizzare l'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 così com'è, come nel caso degli elenchi pigri.

Questo sovraccarico di AndroidView aggiunge due parametri aggiuntivi:

  • onReset: un callback richiamato per segnalare che View sta per essere riutilizzato. Questo valore deve essere diverso da null per attivare il riutilizzo della visualizzazione.
  • onRelease (facoltativo): un callback richiamato per segnalare che View ha terminato la composizione e non verrà riutilizzato.

@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 (passaggio di transizione)

Utilizza il componibile AndroidFragment per aggiungere un Fragment in Compose. AndroidFragment ha una gestione specifica dei frammenti, ad esempio la rimozione del frammento quando il componibile esce dalla composizione.

Per includere un fragment, utilizza il componibile AndroidFragment. Passi una classe Fragment a AndroidFragment, che poi aggiunge un'istanza di quella classe direttamente nella composizione. AndroidFragment fornisce anche un oggetto fragmentState per creare AndroidFragment con un determinato stato, arguments da passare al nuovo frammento e un callback onUpdate che fornisce il frammento dalla composizione. Come molti altri elementi composable integrati, AndroidFragment accetta un parametro Modifier che puoi utilizzare, ad esempio, per impostarne la posizione nell'elemento composable padre.

Chiama AndroidFragment in Scrivi come segue:

@Composable
fun FragmentInComposeExample() {
    AndroidFragment<MyFragment>()
}

Stato del ciclo di vita del frammento del cappello

In Fragment 1.9.0 e versioni successive, puoi utilizzare il parametro maxLifecycle per limitare lo stato del ciclo di vita di Fragment incorporato.

Ad esempio, in un HorizontalPager, puoi impostare dinamicamente maxLifecycle su RESUMED solo quando la pagina corrispondente è attiva, mantenendo le pagine fuori dallo schermo a un massimo di STARTED. Quando l'utente passa a un'altra pagina, il limite di frammenti della pagina precedente diventa STARTED. Questa operazione mette in pausa o annulla i carichi di lavoro con ambito RESUMED (come le coroutine in esecuzione in repeatOnLifecycle(RESUMED)), impedendo alle pagine fuori schermo di eseguire il lavoro solo in primo piano mantenendo la relativa UI inizializzata a STARTED:

HorizontalPager(state = pagerState) { page ->
    // Dynamically cap the lifecycle state based on whether the page is selected
    val maxLifecycle = if (pagerState.settledPage == page) {
        Lifecycle.State.RESUMED
    } else {
        Lifecycle.State.STARTED
    }

    when (page) {
        0 -> AndroidFragment<HomeFragment>(maxLifecycle = maxLifecycle)
        1 -> AndroidFragment<LibraryFragment>(maxLifecycle = maxLifecycle)
        /* Other pages and corresponding fragments */
    }
}

Chiamare il framework Android da Compose

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

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

Composition Locals

Le classi CompositionLocal consentono di passare i dati in modo implicito tramite funzioni componibili. Di solito vengono forniti con un valore in un determinato nodo dell'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 è ospitato il codice Compose con il LocalContext, LocalConfiguration o LocalView corrispondente. Tieni presente che le classi CompositionLocal sono precedute da Local per una migliore rilevabilità con il completamento automatico nell'IDE.

Accedi al valore attuale di un CompositionLocal utilizzando la proprietà current. Ad esempio, il codice riportato di seguito mostra un messaggio di notifica 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")
    }
}

Broadcast receiver

Per mostrare CompositionLocal e gli effetti collaterali, se un BroadcastReceiver deve essere registrato da una funzione componibile, utilizza LocalContext per utilizzare il contesto corrente e gli effetti collaterali 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 */
}

Altre interazioni

Se non è definita un'utilità per l'interazione che ti serve, la best practice è seguire le linee guida generali di Compose, i dati scorrono verso il basso, gli eventi verso l'alto (argomento trattato più nel dettaglio in Pensare in Compose). Ad esempio, questo elemento 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)
    }
}