Вы можете включить иерархию Android View в пользовательский интерфейс Compose. Этот подход особенно полезен, если вы хотите использовать элементы пользовательского интерфейса, которые еще недоступны в Compose, например AdView
. Этот подход также позволяет повторно использовать пользовательские представления, которые вы, возможно, разработали.
Чтобы включить элемент представления или иерархию, используйте составной компонент AndroidView
. AndroidView
передается лямбда-выражение, которое возвращает View
. AndroidView
также предоставляет обратный вызов update
, который вызывается при раздувании представления. AndroidView
перекомпоновывается всякий раз, когда изменяется State
, прочитанное в обратном вызове. AndroidView
, как и многие другие встроенные составные объекты, принимает параметр Modifier
, который можно использовать, например, для установки его позиции в родительском составном объекте.
@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
с привязкой представления
Чтобы внедрить макет XML, используйте API AndroidViewBinding
, который предоставляется библиотекой androidx.compose.ui:ui-viewbinding
. Для этого в вашем проекте должна быть включена привязка представления .
@Composable fun AndroidViewBindingExample() { AndroidViewBinding(ExampleLayoutBinding::inflate) { exampleView.setBackgroundColor(Color.GRAY) } }
AndroidView
в ленивых списках
Если вы используете AndroidView
в ленивом списке ( LazyColumn
, LazyRow
, Pager
и т. д.), рассмотрите возможность использования перегрузки AndroidView
, представленной в версии 1.4.0-rc01. Эта перегрузка позволяет Compose повторно использовать базовый экземпляр View
при повторном использовании содержащей композиции, как в случае с отложенными списками.
Эта перегрузка AndroidView
добавляет два дополнительных параметра:
-
onReset
— обратный вызов, вызываемый для сигнализации о том, чтоView
будет использоваться повторно. Это значение должно быть ненулевым, чтобы разрешить повторное использование представления. -
onRelease
(необязательно) — обратный вызов, вызываемый для сигнализации о том, чтоView
вышло из композиции и больше не будет использоваться повторно.
@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() } ) } } }
Фрагменты в Compose
Используйте составной элемент AndroidViewBinding
, чтобы добавить Fragment
в Compose. AndroidViewBinding
имеет обработку, специфичную для фрагмента, например удаление фрагмента, когда компонуемый объект покидает композицию.
Сделайте это, раздув XML, содержащий FragmentContainerView
в качестве держателя вашего Fragment
.
Например, если у вас определен my_fragment_layout.xml
, вы можете использовать такой код, заменяя XML-атрибут android:name
именем класса вашего 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" />
Разполните этот фрагмент в Compose следующим образом:
@Composable fun FragmentInComposeExample() { AndroidViewBinding(MyFragmentLayoutBinding::inflate) { val myFragment = fragmentContainerView.getFragment<MyFragment>() // ... } }
Если вам нужно использовать несколько фрагментов в одном макете, убедитесь, что вы определили уникальный идентификатор для каждого FragmentContainerView
.
Вызов платформы Android из Compose
Compose работает в рамках классов платформы Android. Например, он размещается в классах Android View, таких как Activity
или Fragment
, и может использовать классы платформы Android, такие как Context
, системные ресурсы, Service
или BroadcastReceiver
.
Чтобы узнать больше о системных ресурсах, см. Ресурсы в Compose .
Состав Местные жители
Классы CompositionLocal
позволяют неявно передавать данные через составные функции. Обычно им присваивается значение в определенном узле дерева пользовательского интерфейса. Это значение может использоваться его составными потомками без объявления 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") } }
Более полный пример можно найти в разделе «Пример использования: BroadcastReceivers» в конце этого документа.
Другие взаимодействия
Если для необходимого вам взаимодействия не определена утилита, лучше всего следовать общему правилу Compose: данные идут вниз, события — вверх (подробнее обсуждается в разделе «Мышление в Compose »). Например, этот составной объект запускает другое действие:
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) } }
Практический пример: приемники вещания
Для более реалистичного примера функций, которые вы, возможно, захотите перенести или реализовать в Compose, а также для демонстрации CompositionLocal
и побочных эффектов , скажем, BroadcastReceiver
необходимо зарегистрировать из составной функции.
Решение использует 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 */ }
Следующие шаги
Теперь, когда вы знаете API-интерфейсы совместимости при использовании Compose в представлениях и наоборот, изучите страницу «Другие рекомендации», чтобы узнать больше.
{% дословно %}Рекомендуется для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Другие соображения
- Побочные эффекты в Compose
- Данные локального масштаба с помощью CompositionLocal
Вы можете включить иерархию Android View в пользовательский интерфейс Compose. Этот подход особенно полезен, если вы хотите использовать элементы пользовательского интерфейса, которые еще недоступны в Compose, например AdView
. Этот подход также позволяет вам повторно использовать пользовательские представления, которые вы, возможно, разработали.
Чтобы включить элемент представления или иерархию, используйте составной элемент AndroidView
. AndroidView
передается лямбда-выражение, которое возвращает View
. AndroidView
также предоставляет обратный вызов update
, который вызывается при раздувании представления. AndroidView
перекомпоновывается всякий раз, когда изменяется State
, прочитанное в обратном вызове. AndroidView
, как и многие другие встроенные составные объекты, принимает параметр Modifier
, который можно использовать, например, для установки его позиции в родительском составном объекте.
@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
с привязкой представления
Чтобы внедрить макет XML, используйте API AndroidViewBinding
, который предоставляется библиотекой androidx.compose.ui:ui-viewbinding
. Для этого в вашем проекте должна быть включена привязка представления .
@Composable fun AndroidViewBindingExample() { AndroidViewBinding(ExampleLayoutBinding::inflate) { exampleView.setBackgroundColor(Color.GRAY) } }
AndroidView
в ленивых списках
Если вы используете AndroidView
в ленивом списке ( LazyColumn
, LazyRow
, Pager
и т. д.), рассмотрите возможность использования перегрузки AndroidView
, представленной в версии 1.4.0-rc01. Эта перегрузка позволяет Compose повторно использовать базовый экземпляр View
при повторном использовании содержащей композиции, как в случае с отложенными списками.
Эта перегрузка AndroidView
добавляет два дополнительных параметра:
-
onReset
— обратный вызов, вызываемый для сигнализации о том, чтоView
будет использоваться повторно. Это значение должно быть ненулевым, чтобы разрешить повторное использование представления. -
onRelease
(необязательно) — обратный вызов, вызываемый для сигнализации о том, чтоView
вышло из композиции и больше не будет использоваться повторно.
@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() } ) } } }
Фрагменты в Compose
Используйте составной элемент AndroidViewBinding
, чтобы добавить Fragment
в Compose. AndroidViewBinding
имеет обработку, специфичную для фрагмента, например удаление фрагмента, когда компонуемый элемент покидает композицию.
Сделайте это, раздув XML, содержащий FragmentContainerView
в качестве держателя вашего Fragment
.
Например, если у вас определен my_fragment_layout.xml
, вы можете использовать такой код, заменяя XML-атрибут android:name
именем класса вашего 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" />
Разполните этот фрагмент в Compose следующим образом:
@Composable fun FragmentInComposeExample() { AndroidViewBinding(MyFragmentLayoutBinding::inflate) { val myFragment = fragmentContainerView.getFragment<MyFragment>() // ... } }
Если вам нужно использовать несколько фрагментов в одном макете, убедитесь, что вы определили уникальный идентификатор для каждого FragmentContainerView
.
Вызов платформы Android из Compose
Compose работает в рамках классов платформы Android. Например, он размещается в классах Android View, таких как Activity
или Fragment
, и может использовать классы платформы Android, такие как Context
, системные ресурсы, Service
или BroadcastReceiver
.
Чтобы узнать больше о системных ресурсах, см. Ресурсы в Compose .
Состав Местные жители
Классы CompositionLocal
позволяют неявно передавать данные через составные функции. Обычно им присваивается значение в определенном узле дерева пользовательского интерфейса. Это значение может использоваться его составными потомками без объявления 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") } }
Более полный пример можно найти в разделе «Пример использования: BroadcastReceivers» в конце этого документа.
Другие взаимодействия
Если для необходимого вам взаимодействия не определена утилита, лучше всего следовать общему правилу Compose: данные идут вниз, события — вверх (подробнее обсуждается в разделе «Мышление в Compose »). Например, этот составной объект запускает другое действие:
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) } }
Практический пример: приемники вещания
Для более реалистичного примера функций, которые вы, возможно, захотите перенести или реализовать в Compose, а также для демонстрации CompositionLocal
и побочных эффектов , скажем, BroadcastReceiver
необходимо зарегистрировать из составной функции.
Решение использует 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 */ }
Следующие шаги
Теперь, когда вы знаете API-интерфейсы совместимости при использовании Compose в представлениях и наоборот, изучите страницу «Другие рекомендации», чтобы узнать больше.
{% дословно %}Рекомендуется для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Другие соображения
- Побочные эффекты в Compose
- Данные локального масштаба с помощью CompositionLocal