Jetpack Compose 笔记(7) - 与传统 View 交互
直到第七节才学到这个,但相信这是很多很多小伙伴最关心的问题。显然,我们不可能一下子把整个项目换成 Compose。退一步说,哪怕从头开始新项目,也无法保证所有依赖都是 Compose。所以 Compose 与传统 View 系统的交互非常重要。
嵌入显示
Viwe 中嵌入 Compose
乍一看这活挺复杂的,其实很简单。
首先在传统 View 里加一个 ComposeView
作为 Compose 的容器:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Android!" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
然后在代码里取得这个 View 即可进去 Compose 环境:
val composeView = findViewById<ComposeView>(R.id.composeView)
composeView.setContent {
Text(text = "Hello Compose!")
}
Compose 中嵌入 View
我们已经了解了,Compose 在 Android 中整体是一个 View,那为什么 View 中还能添加 View 呢?因为它说 ViewGroup 呀!
abstract class AbstractComposeView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr)
在 Compose 中嵌入传统 View 需要调用 AndroidView()
函数,它第一个参数是工厂 lambda,在这里面创建传统的 View 并返回就行了,如下。
setContent {
Column {
Text(text = "Hello Compose")
AndroidView(factory = {
TextView(this@MainActivity).apply {setText("Hello View")}
})
}
}
交互
更新 View
上面已经在 Compose 中嵌入了 View。那该如何更新它呢?有一个最粗暴的方案:把 View 定义为类字段,像传统写法一样来更新。但这样就完全丧失了 Compose 中自动订阅的优势 —— 每次更新变量还要记得 set View 属性。
其实 AndroidView
除了 factory,最后还有个名为 update
的 lambda 参数,它将在 Recompose 过程中被调用。顾名思义,主要用来更新。
setContent {
Column {
Text(text = "Hello Compose")
AndroidView(factory = {
TextView(this@MainActivity)
}) {
it.setText(message)
}
}
}
这样一来数据发生改变时传统 View 也能 “自动刷新”了。
数据共享
Compose 中多用 MutableState 包装数据实现自动更新,而在外部更多的是使用普通数据或者 LiveData。
为此,Compose 提供了扩展函数 LiveData.observeAsState()
把 LiveData 转为 State 在 Compose 中实现自动订阅,记得添加依赖 implementation ("androidx.compose.runtime:runtime-livedata:$compose_version")
。
说到订阅,还得提到一个原生的东西 — Flow
。同理,Compose 也提供了一个扩展 Flow.collectAsState()
。
反过来,如果要在外部使用 Compose 的数据呢?
可惜,目前没有类似 MutableState.toLiveData()
的东西。换个思路,对于那些需要在外部使用的数据,直接定义成 LiveData,然后再转成 State 不就好啦?😆