Вы можете включить иерархию View Android в интерфейс Compose. Этот подход особенно полезен, если вы хотите использовать элементы пользовательского интерфейса, которые ещё не доступны в Compose, например AdView.
Этот подход также позволяет повторно использовать созданные вами специальные представления.
Чтобы включить элемент или иерархию View, используйте composable-функцию AndroidView
. В AndroidView передается лямбда-выражение, которое возвращает View. AndroidView также предоставляет обратный вызов update, который активируется при заполнении представления. AndroidView перекомпонуется всякий раз, когда изменяется State, прочитанный в обратном вызове. AndroidView, как и многие другие встроенные composable-функции, принимает параметр Modifier, который можно использовать, например, для установки его положения в родительской composable-функции.
@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 с привязкой представления
Чтобы встроить макет XML, используйте API AndroidViewBinding, который предоставляется библиотекой androidx.compose.ui:ui-viewbinding. Для этого в проекте должна быть включена привязка представлений.
@Composable fun AndroidViewBindingExample() { AndroidViewBinding(ExampleLayoutBinding::inflate) { exampleView.setBackgroundColor(Color.GRAY) } }
AndroidView в Lazy lists
Если вы используете AndroidView в ленивом списке (LazyColumn, LazyRow, Pager и т. д.), рекомендуем использовать перегрузку AndroidView, которая появилась в версии 1.4.0-rc01. Эта перегрузка позволяет Compose повторно использовать базовый экземпляр View, когда содержащая композиция повторно используется, как в случае с ленивыми списками.
Эта перегрузка AndroidView добавляет два дополнительных параметра:
onReset– обратный вызов, который инициируется, чтобы сообщить о том, чтоViewбудет использоваться повторно. Чтобы включить повторное использование View, это значение не должно быть нулевым.onRelease(необязательно) – обратный вызов, который сообщает, чтоViewбольше не используется в композиции и не будет использоваться в дальнейшем.
@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() } ) } } }
Фрагменты в Compose (переходный этап)
Чтобы добавить Fragment в Compose, используйте composable-функцию AndroidFragment.
AndroidFragment имеет специальную обработку фрагментов, например удаление фрагмента, когда composable-функция покидает композицию.
Чтобы добавить фрагмент, используйте composable-функцию AndroidFragment. Вы передаете класс Fragment в AndroidFragment, который затем добавляет экземпляр этого класса непосредственно в композицию. AndroidFragment также предоставляет объект fragmentState для создания AndroidFragment с заданным состоянием, arguments для передачи в новый фрагмент и обратный вызов onUpdate, который предоставляет фрагмент из композиции. Как и многие другие встроенные composable-функции, AndroidFragment принимает параметр Modifier, который можно использовать, например, чтобы задать положение в родительской composable-функции.
Вызовите AndroidFragment в окне создания письма следующим образом:
@Composable fun FragmentInComposeExample() { AndroidFragment<MyFragment>() }
Ограничение статуса жизненного цикла фрагмента
В Fragment 1.9.0 и более поздних версиях вы можете использовать параметр maxLifecycle, чтобы ограничить состояние жизненного цикла встроенного Fragment.
Например, в HorizontalPager можно динамически задавать для maxLifecycle значение RESUMED только тогда, когда активна соответствующая страница, а для страниц за пределами экрана – STARTED. Когда пользователь переходит на другую страницу, ограничение на количество фрагментов для предыдущей страницы становится равным STARTED. Это приостанавливает или отменяет рабочие нагрузки, ограниченные RESUMED (например, сопрограммы, выполняемые в repeatOnLifecycle(RESUMED)), не позволяя закадровым страницам выполнять работу только на переднем плане, сохраняя при этом их пользовательский интерфейс инициализированным в 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 */ } }
Вызов фреймворка Android из Compose
Compose работает в классах фреймворка Android. Например, он размещается в классах Android View, таких как Activity или Fragment, и может использовать классы фреймворка Android, такие как Context, системные ресурсы, Service или BroadcastReceiver.
Подробнее о ресурсах в Compose…
Локальные версии композиций
Классы CompositionLocal позволяют неявно передавать данные через composable-функции. Обычно они предоставляются со значением в определенном узле дерева пользовательского интерфейса. Это значение может использоваться дочерними composable-функциями без объявления CompositionLocal в качестве параметра.
CompositionLocal используется для распространения значений типов фреймворка Android в Compose, таких как Context, Configuration или View, в которых размещен код Compose с соответствующими LocalContext, LocalConfiguration или LocalView.
Обратите внимание, что к классам CompositionLocal добавляется префикс Local, чтобы их было проще находить с помощью автозаполнения в IDE.
Чтобы получить текущее значение CompositionLocal, используйте свойство current. Например, в приведенном ниже коде показано всплывающее сообщение, которое передает LocalContext.current в метод Toast.makeToast.
@Composable fun ToastGreetingButton(greeting: String) { val context = LocalContext.current Button(onClick = { Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show() }) { Text("Greet") } }
Широковещательные приемники
Чтобы продемонстрировать CompositionLocal и побочные эффекты, если BroadcastReceiver нужно зарегистрировать из composable-функции, используйте LocalContext для текущего контекста, а также побочные эффекты rememberUpdatedState и 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 */ }
Другие взаимодействия
Если для нужного вам взаимодействия не определена утилита, рекомендуется следовать общему руководству по Compose: данные передаются вниз, события – вверх (подробнее об этом рассказывается в статье Мышление в Compose). Например, эта composable-функция запускает другое действие:
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) } }