在 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 列表(LazyColumnLazyRow、� 等)中使用 AndroidView,请考虑使用版本 1.4.0-rc01 中引入的 AndroidView 重载。Pager此重载允许 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 还提供了一个 fragmentState 对象,用于创建具有给定状态的 AndroidFragmentarguments 用于传递到新 fragment;以及一个 onUpdate 回调,用于从组合中提供 fragment。与许多其他内置可组合项一样,AndroidFragment 接受 Modifier 参数,该参数可用于设置它在父级可组合项中的位置等用途。

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

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

限制 fragment 生命周期状态

在 Fragment 1.9.0 及更高版本中,您可以使用 maxLifecycle 参数来限制嵌入式 Fragment 的生命周期状态。

例如,在 HorizontalPager 中,您可以仅在相应页面处于活动状态时将 maxLifecycle 动态设置为 RESUMED,从而将屏幕外页面的数量上限保持为 STARTED。当用户导航到另一个页面时,前一个页面的 fragment cap 会变为 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 */
    }
}

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

广播接收器

为了展示 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 */
}

其他交互

如果没有为您需要的交互定义实用程序,最佳实践是遵循常规 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)
    }
}