W interfejsie Compose możesz umieścić hierarchię widoków Androida. To podejście jest szczególnie przydatne, jeśli chcesz używać elementów interfejsu, które nie są jeszcze dostępne w Compose, np. AdView.
Dzięki temu możesz też ponownie wykorzystać zaprojektowane przez siebie widoki niestandardowe.
Aby uwzględnić element widoku lub hierarchię, użyj funkcji kompozycyjnej AndroidView
. AndroidView przyjmuje wyrażenie lambda, które zwraca wartość View. AndroidView udostępnia też updatewywołanie zwrotne, które jest wywoływane po rozwinięciu widoku. Funkcja AndroidView jest ponownie komponowana, gdy zmieni się wartość State odczytana w wywołaniu zwrotnym. AndroidView, podobnie jak wiele innych wbudowanych funkcji kompozycyjnych, przyjmuje parametr Modifier, którego można użyć np. do ustawienia jego pozycji w kompozycyjnej funkcji nadrzędnej.
@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 z powiązaniem widoku
Aby osadzić układ XML, użyj interfejsu API AndroidViewBinding, który jest udostępniany przez bibliotekę androidx.compose.ui:ui-viewbinding. Aby to zrobić, musisz włączyć w projekcie powiązanie widoku.
@Composable fun AndroidViewBindingExample() { AndroidViewBinding(ExampleLayoutBinding::inflate) { exampleView.setBackgroundColor(Color.GRAY) } }
AndroidView na listach leniwych
Jeśli używasz AndroidView na liście Lazy (LazyColumn, LazyRow, Pager itp.), rozważ użycie przeciążenia AndroidView wprowadzonego w wersji 1.4.0-rc01. Ten overload umożliwia Compose ponowne użycie bazowej instancji View, gdy zawierająca kompozycja jest ponownie używana w takiej samej postaci, jak w przypadku list leniwych.
Ten przeciążony operator AndroidView dodaje 2 dodatkowe parametry:
onReset– wywołanie zwrotne, które sygnalizuje, że obiektViewma zostać ponownie użyty. Aby włączyć ponowne użycie widoku, ta wartość musi być różna od null.onRelease(opcjonalnie) – wywołanie zwrotne, które sygnalizuje, że elementViewopuścił kompozycję i nie będzie już używany.
@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() } ) } } }
Fragmenty w Compose (etap przejściowy)
Użyj komponentu AndroidFragment, aby dodać Fragment w Compose.
AndroidFragment ma obsługę specyficzną dla fragmentu, np. usuwanie fragmentu, gdy komponent opuści kompozycję.
Aby uwzględnić fragment, użyj funkcji kompozycyjnej AndroidFragment. Przekazujesz Fragment klasę do AndroidFragment, która następnie dodaje instancję tej klasy bezpośrednio do kompozycji. AndroidFragment udostępnia też obiekt fragmentState do tworzenia AndroidFragment z określonym stanem, arguments do przekazywania do nowego fragmentu i wywołanie zwrotne onUpdate, które udostępnia fragment z kompozycji. Podobnie jak wiele innych wbudowanych funkcji kompozycyjnych, AndroidFragment akceptuje parametr Modifier, którego możesz użyć np. do ustawienia jego pozycji w nadrzędnej funkcji kompozycyjnej.
Wywołaj funkcję AndroidFragment w funkcji Compose w ten sposób:
@Composable fun FragmentInComposeExample() { AndroidFragment<MyFragment>() }
Stan cyklu życia fragmentu Cap
W fragmencie w wersji 1.9.0 i nowszej możesz użyć parametru maxLifecycle, aby ograniczyć stan cyklu życia osadzonego elementu Fragment.
Na przykład w HorizontalPager możesz dynamicznie ustawić maxLifecycle na RESUMED tylko wtedy, gdy odpowiednia strona jest aktywna, a strony poza ekranem są ograniczone do STARTED. Gdy użytkownik przejdzie na inną stronę, limit fragmentów poprzedniej strony zmieni się na STARTED. Wstrzymuje lub anuluje zadania w zakresie RESUMED (np. współprogramy działające w repeatOnLifecycle(RESUMED)), co zapobiega wykonywaniu przez strony poza ekranem pracy tylko na pierwszym planie, przy jednoczesnym zachowaniu zainicjowanego interfejsu na poziomie 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 */ } }
Wywoływanie platformy Android z Compose
Compose działa w ramach klas platformy Android. Jest on na przykład hostowany w klasach widoku Androida, takich jak Activity lub Fragment, i może korzystać z klas platformy Androida, takich jak Context, zasoby systemowe, Service lub BroadcastReceiver.
Więcej informacji o zasobach systemowych znajdziesz w artykule Zasoby w Compose.
Composition Locals
CompositionLocal
klasy umożliwiają niejawne przekazywanie danych za pomocą funkcji, które można łączyć. Zwykle mają one wartość w określonym węźle drzewa interfejsu. Jej wartość może być używana przez elementy podrzędne, które można łączyć, bez deklarowania CompositionLocal jako parametru w funkcji typu „composable”.
Symbol CompositionLocal służy do propagowania wartości typów platformy Android w Compose, takich jak Context, Configuration lub View, w których jest hostowany kod Compose, z odpowiednimi wartościami LocalContext, LocalConfiguration lub LocalView.
Pamiętaj, że klasy CompositionLocal mają przedrostek Local, co ułatwia ich wyszukiwanie za pomocą autouzupełniania w IDE.
Aby uzyskać dostęp do bieżącej wartości CompositionLocal, użyj właściwości current. Na przykład poniższy kod wyświetla komunikat w formie wyskakującego okienka, przekazując wartość LocalContext.current do metody Toast.makeToast.
@Composable fun ToastGreetingButton(greeting: String) { val context = LocalContext.current Button(onClick = { Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show() }) { Text("Greet") } }
Odbiorniki
Aby zaprezentować CompositionLocal i efekty uboczne, jeśli BroadcastReceiver musi zostać zarejestrowany w funkcji kompozycyjnej, użyj LocalContext, aby użyć bieżącego kontekstu, oraz efektów ubocznych rememberUpdatedState i 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 */ }
Inne interakcje
Jeśli nie ma funkcji zdefiniowanej dla interakcji, której potrzebujesz, najlepszym rozwiązaniem jest przestrzeganie ogólnych wytycznych dotyczących Compose: dane przepływają w dół, a zdarzenia w górę (więcej informacji znajdziesz w artykule Myślenie w Compose). Na przykład ten komponent kompozycyjny uruchamia inną aktywność:
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) } }