Jetpack Compose 笔记(7) - 与传统 View 交互

1,856 阅读2分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。


直到第七节才学到这个,但相信这是很多很多小伙伴最关心的问题。显然,我们不可能一下子把整个项目换成 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 不就好啦?😆