Использование представлений в Compose

Вы можете включить иерархию 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 в представлениях и наоборот, изучите страницу «Другие рекомендации», чтобы узнать больше.

{% дословно %} {% дословно %} {% дословно %} {% дословно %}