为什么要进行混用?
主要原因有这几点:
-
一个庞大的现有项目肯定不是立马动手将所有的代码都使用 Compose 重写,而是会逐步迁移,选择将新的模块或界面使用 Compose,然后再逐步将旧的 View 界面和组件替换为使用 Compose 实现,最终完成项目UI框架的替换。
-
有些自定义 View 的组件已经很完善了,并且很复杂。一时要将这个组件替换为使用 Compose 实现,是需要耗费大量时间、精力的,所以我们一般可以复用这些 View 组件,等到以后有时间、有机会了,再去重写它。
-
有些 View(如
SurfaceView、TextureView) 在 Compose 里面没有对等的实现,这时,就必须在 Compose 使用这些 View。
综合以上原因,进行混用是必须的。有时要在 Composable 函数中使用 View 的组件,有时要在传统 View 布局中嵌入 Composable 函数,我们就来看这两种方式怎么实现。
在传统 View 系统中使用 Compose
我们想要在 View 布局中引入 Compose,但Compose 和 View 是两套系统,该怎么进行融合、混用呢?
使用 ComposeView,它是一个继承了 ViewGroup 的类,可以装载 Composable 函数。
在使用代码构建的布局中动态添加 ComposeView
比如现在有一个 LinearLayout 布局,我们想将我们的自定义 Composable 函数放进去,我们可以这样:
// 线性布局
val linearLayout: LinearLayout = LinearLayout(this)
val composeView = ComposeView(context = this).apply {
// 提供 Composable 的环境
setContent {
Column {
Text(text = "我是 androidx.compose.material3 包下的 Text 组件")
Button(onClick = {}) {
Text("平平无奇的按钮")
}
}
}
}
linearLayout.addView(
composeView,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
在 XML 布局文件中添加
你也可以在 xml 布局文件中声明 ComposeView。
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
在代码中获取到这个 ComposeView,然后往里面添加 Composable 组件。
val composeViewInXml = findViewById<ComposeView>(R.id.composeView)
composeViewInXml.setContent {
Column {
Text(text = "我是 androidx.compose.material3 包下的 Text 组件")
Button(onClick = {}) {
Text("平平无奇的按钮")
}
}
}
在 Compose 中使用传统 View
反过来,我们想要在 Compose 里使用 View 的组件该怎么办?
使用 AndroidView Composable 函数,它允许我们往 Compose 中嵌入一个传统的 View,最少参数的函数原型:
@Composable
@UiComposable
fun <T : View> AndroidView(
factory: (Context) -> T,
modifier: Modifier = Modifier,
update: (T) -> Unit = NoOpUpdate
)
它有两个重要参数:
-
参数
factory:lambda 表达式会在首次组合时,被执行,我们可以在这里创建 View 实例。 -
参数
update:lambda 表达式会在首次组合以及后续的重组过程中被执行,参数是在factory中创建的 View 实例,我们可以在这里进行修改 View 实例的属性。
比如这个例子:
@Composable
fun UseViewInComposeSample() {
var text by remember { mutableStateOf("我是平平无奇的文本") }
val context = LocalContext.current
Column {
AndroidView(
factory = {
// 初始化 View 组件
TextView(context).apply {
text = "我是 android.widget 包下的 TextView"
}
},
update = {
// 初始化并且更新显示的文本
it.text = text
}
)
}
}
在 Compose 嵌入了一个原生的 TextView,并且它的 text 属性值会随着 text 状态的变化而更新。
总结
Jetpack Compose 与传统 View 系统的混用并不难,我们可以很方便地使用 Compose 给我们提供的 ComposeView 和 AndroidView 来完成。