文章

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 不就好啦?😆