Puoi includere una gerarchia di oggetti View Android in una UI di Compose. Questo approccio è particolarmente utile se vuoi utilizzare elementi UI non ancora disponibili in Compose, come AdView.
Questo approccio ti consente anche di riutilizzare le visualizzazioni personalizzate che potresti aver progettato.
Per includere un elemento o una gerarchia di visualizzazione, utilizza il componibile AndroidView
. A AndroidView viene passata una funzione lambda che restituisce un
View. AndroidView fornisce anche un callback update che viene chiamato quando la visualizzazione viene espansa. AndroidView viene ricomposto ogni volta che un State letto all'interno del callback cambia. AndroidView, come molti altri componibili integrati, accetta un parametro Modifier che può essere utilizzato, ad esempio, per impostare la sua posizione nel componibile principale.
@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 associazione di visualizzazioni
Per incorporare un layout XML, utilizza l'API AndroidViewBinding, fornita dalla libreria androidx.compose.ui:ui-viewbinding. Per farlo, il progetto deve abilitare l'associazione delle visualizzazioni.
@Composable fun AndroidViewBindingExample() { AndroidViewBinding(ExampleLayoutBinding::inflate) { exampleView.setBackgroundColor(Color.GRAY) } }
AndroidView in Lazy lists
Se utilizzi un AndroidView in un elenco Lazy (LazyColumn, LazyRow,
Pager e così via), valuta la possibilità di utilizzare l'overload di AndroidView
introdotto nella versione 1.4.0-rc01. Questo overload consente a Compose di riutilizzare l'istanza View sottostante quando la composizione contenente viene riutilizzata, come nel caso degli elenchi lazy.
Questo overload di AndroidView aggiunge due parametri aggiuntivi:
onReset: un callback richiamato per segnalare cheViewsta per essere riutilizzato. Questo valore non deve essere nullo per consentire il riutilizzo della visualizzazione.onRelease(facoltativo): un callback richiamato per segnalare cheViewè uscito dalla 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 (fase di transizione)
Utilizza il componibile AndroidFragment per aggiungere un Fragment in Compose.
AndroidFragment ha una gestione specifica per i frammenti, ad esempio la rimozione del frammento quando il composable lascia la composizione.
Per includere un frammento, 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 uno stato specifico, arguments da passare al nuovo frammento e un callback onUpdate che fornisce il frammento dalla composizione. Come molti altri elementi componibili integrati, AndroidFragment accetta un parametro Modifier che puoi utilizzare, ad esempio, per impostare la sua posizione nell'elemento componibile principale.
Chiama AndroidFragment in Compose come segue:
@Composable fun FragmentInComposeExample() { AndroidFragment<MyFragment>() }
Stato del ciclo di vita del frammento Cap
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 schermo limitate a STARTED. Quando l'utente passa a un'altra pagina, il limite di frammenti della pagina precedente diventa STARTED. In questo modo, i workload con ambito RESUMED (ad esempio le coroutine in esecuzione in repeatOnLifecycle(RESUMED)) vengono messi in pausa o annullati, impedendo alle pagine fuori schermo di eseguire operazioni solo in primo piano e mantenendo la loro UI inizializzata su 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 */ } }
Chiamata del framework Android da Compose
Compose opera all'interno delle classi del framework Android. Ad esempio, è ospitato
su classi di visualizzazione Android, come Activity o Fragment, e potrebbe utilizzare classi del framework Android come Context, risorse di sistema,
Service o BroadcastReceiver.
Per scoprire di più sulle risorse di sistema, consulta Risorse in Compose.
CompositionLocal
Le classi CompositionLocal consentono di passare i dati in modo implicito tramite funzioni componibili. In genere,
vengono forniti con un valore in un determinato nodo dell'albero della UI. 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 i corrispondenti LocalContext, LocalConfiguration o LocalView.
Tieni presente che le classi CompositionLocal hanno il prefisso Local per una migliore rilevabilità con il completamento automatico nell'IDE.
Accedi al valore corrente di un 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") } }
Broadcast receiver
Per mostrare CompositionLocal ed effetti collaterali, se è necessario registrare un BroadcastReceiver da una funzione componibile, utilizza LocalContext per utilizzare il contesto corrente ed 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 di cui hai bisogno, la best practice è seguire la linea guida generale di Compose, i dati fluiscono verso il basso, gli eventi verso l'alto (discussa in modo più approfondito in Pensare in Compose). Ad esempio, questo composable 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) } }