在 Compose 中使用 View

您可以在 Compose 界面中添加 Android View 层次结构。如果您要使用 Compose 中尚未提供的界面元素(如 AdView),此方法特别有用。此方法还可让您重复使用您可能已设计的自定义视图。

如需添加视图元素或层次结构,请使用 AndroidView 可组合项。系统会向 AndroidView 传递一个返回 View 的 lambda。AndroidView 还提供了在视图膨胀时被调用的 update 回调。每当在该回调中读取的 State 发生变化时,AndroidView 都会重组。与许多其他内置可组合项一样,AndroidView 接受 Modifier 参数,该参数可用于设置它在父级可组合项中的位置等用途。

@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 布局,请使用 androidx.compose.ui:ui-viewbinding 库提供的 AndroidViewBinding API。为此,您的项目必须启用视图绑定

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

延迟列表中的 AndroidView

如果您在 Lazy 列表(LazyColumnLazyRowPager 等)中使用 AndroidView,请考虑使用版本 1.4.0-rc01 中引入的 AndroidView 重载。此重载允许 Compose 在包含的组合被重用时(例如对于延迟列表),重用底层 View 实例。

AndroidView 重载添加了 2 个额外的参数:

  • onReset - 一个回调,用于指示 View 即将被重用。此值必须为非 null,才能启用视图重用。
  • 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

使用 AndroidFragment 可组合项在 Compose 中添加 FragmentAndroidFragment 包含特定于 fragment 的处理方式,例如当可组合项退出组合时移除 fragment。

如需添加 fragment,请使用 AndroidFragment 可组合项。您将 Fragment 类传递给 AndroidFragment,然后 AndroidFragment 会将该类的实例直接添加到组合中。AndroidFragment 还提供了一个 fragmentState 对象,用于创建具有给定状态的 AndroidFragmentarguments(用于传递到新 fragment)以及一个 onUpdate 回调(用于从组合中提供 fragment)。与许多其他内置可组合项一样,AndroidFragment 接受 Modifier 参数,该参数可用于设置它在父级可组合项中的位置等用途。

在 Compose 中调用 AndroidFragment,如下所示:

@Composable
fun FragmentInComposeExample() {
    AndroidFragment<MyFragment>()
}

从 Compose 调用 Android 框架

Compose 在 Android 框架类中运行。例如,它托管在 Android View 类(如 ActivityFragment)上,并且可能会使用 Android 框架类(如 Context、系统资源、ServiceBroadcastReceiver)。

如需详细了解系统资源,请参阅 Compose 中的资源

CompositionLocal

CompositionLocal 类允许通过可组合函数隐式传递数据。它们通常在界面树的某个节点具有一个值。该值可供其可组合项的后代使用,而无需在可组合函数中将 CompositionLocal 声明为参数。

CompositionLocal 用于为 Compose 中的 Android 框架类型(例如 ContextConfigurationView)传播值,其中 Compose 代码与相应的 LocalContextLocalConfigurationLocalView 一起托管。请注意,CompositionLocal 类的前缀是 Local,以便于 IDE 中的自动补全功能更轻松地检测到这些类。

CompositionLocal 的当前值可通过它的 current 属性进行访问。例如,以下代码通过向 Toast.makeToast 方法提供 LocalContext.current 来显示消息框消息。

@Composable
fun ToastGreetingButton(greeting: String) {
    val context = LocalContext.current
    Button(onClick = {
        Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show()
    }) {
        Text("Greet")
    }
}

如需查看更完整的示例,请参阅本文档末尾的案例研究:BroadcastReceiver 部分。

其他交互

如果没有为您需要的交互定义实用程序,最佳实践是遵循常规 Compose 准则,即数据向下流动而事件向上流动(Compose 编程思想一文对此进行了更为详细的说明)。例如,以下可组合函数会启动一个不同的 activity:

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)
    }
}

案例研究:BroadcastReceiver

举一个更实际的例子,您可能想要迁移一些功能或在 Compose 中实现一些功能,以及展示 CompositionLocal附带效应,在这种情况下,需要通过可组合函数注册 BroadcastReceiver

该解决方案利用 LocalContext 来使用当前上下文以及 rememberUpdatedStateDisposableEffect 附带效应。

@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 */
}

后续步骤

现在,您已经了解在 View 中使用 Compose 以及在 Compose 中使用 View 时的互操作性 API。如需了解更多内容,可以浏览其他注意事项页面。