如果你的 View 不支持 Compose 怎么办

Compose 本身独立于安卓经典的 View 系统,但并不是所有的 View 都可以用 Compose 替代,例如:WebViewMapView

但如果你真的需要用这些 View 怎么办?

AndroidView 是一个可组合函数,可用于在 @Composable 函数内部添加安卓经典的 View

AndroidView

我们先上一个示例:

Kotlin 复制代码
AndroidView(factory = {
    TextView(it).apply {
        setText("Text 1")
    }
})

AndroidView 需要一个创建 Viewlambda 表达式------factory。当 Compose 需要显示这个 View 的时候,会调用该 factory 方法去创建 View

效果如下:

效果如图,一个简单的 TextView 被成功显示。

当然了,你同样可以使用 Modifier 去修饰 AndroidView

Kotlin 复制代码
AndroidView(
    modifier = Modifier
        .size(100.dp)
        .background(Color.LightGray), // 给定尺寸、背景
    factory = {
        TextView(it).apply {
            setText("Text 1")
            setTextColor(android.graphics.Color.RED)
        }
    }
)

更新状态

当然了,如果只是使用 AndroidView 去加载一个经典的 Android View,其优势尚未完全体现。

如果我们想更新 TextView 的状态怎么办?

Kotlin 复制代码
var text by remember { mutableStateOf("Text 1") }

AndroidView(
    modifier = Modifier
        .size(100.dp)
        .background(Color.LightGray),
    factory = {
        TextView(it).apply {
            setText(text)
            setTextColor(android.graphics.Color.RED)
        }
    }
)

Button(onClick = {
    text = "Clicked" // 这里将 text 改成 Clicked
}) {
    Text("Click")
}

很简单的一段使用 MutableState 去更新状态的代码,我们看下效果:

奇怪的事情发生了,界面并未更新,点击按钮后 TextView 的文本保持不变。无论你点击多少次,TextViewText 1 都不会变成 Clicked

AndroidView 使用 factory 来创建 View,并且保证在 View 整个生命周期期间,factory 都只会调用一次。如果想更新状态的话,需要通过另一个 lambda 表达式------update

我们更改一下上面的代码,使之能够更新状态:

Kotlin 复制代码
var text by remember { mutableStateOf("Text 1") }

AndroidView(
    modifier = Modifier
        .size(100.dp)
        .background(Color.LightGray),
    factory = {
        TextView(it).apply {
            // 这里去掉了原先的设置文字逻辑
            setTextColor(android.graphics.Color.RED)
        }
    },
    update = {
        it.text = text // 通过 update 更新文字
    }
)

Button(onClick = {
    text = "Clicked"
}) {
    Text("Click")
}

现在,我们在 update 中更新状态了:

是的,运行的很完美。

这里有两个事情值得注意:

  1. update 会在 factory 完成之后,执行一次。也就说,AndroidView 确保 View 在创建完毕之后,执行一次更新操作。
  2. 当状态发生变化时,便会执行 update

我们添加部分日志来观测这些现象:

Kotlin 复制代码
var text by remember { mutableStateOf("Text 1") }

AndroidView(
    modifier = Modifier
        .size(100.dp)
        .background(Color.LightGray),
    factory = {
        Log.d("AnV","create view") // 创建
        TextView(it).apply {
            setTextColor(android.graphics.Color.RED)
        }
    },
    update = {
        Log.d("AnV","update view") // 更新
        it.text = text
    }
)

Button(onClick = {
    text = "Clicked"
    Log.d("AnV","click") // 点击
}) {
    Text("Click")
}

按照上面的步骤操作,我们看下日志:

sql 复制代码
2025-09-10 21:39:08.911  AnV  D  create view
2025-09-10 21:39:08.913  AnV  D  update view
2025-09-10 21:39:15.886  AnV  D  click
2025-09-10 21:39:15.890  AnV  D  update view

可以看到,当创建完成之后,立马执行了更新。当点击按钮之后,更新操作也执行了。

这也解释了为什么我们的代码没有在 factory 中设置文字,但是当我们看到该 View 的时候,文字确是对的的原因。

onRelease

如果当前页面离开,或者 AndroidView 离开了当前的重组,可以通过 onRelease 表达式执行对于 View 的清理工作。

如果你的 View 是一个 WebView,或者播放器的 PlayerView,这一点至关重要。

Kotlin 复制代码
AndroidView(
    modifier = Modifier
        .size(100.dp)
        .background(Color.LightGray),
    factory = {
        Log.d("AnVR","create view")
        TextView(it).apply {
            setText("Text 1")
            setTextColor(android.graphics.Color.RED)
        }
    },
    onRelease = {
        Log.d("AnVR","release view")
    }
)

这里我们执行一次进入和退出页面的操作:

sql 复制代码
2025-09-10 21:46:00.682 AnVR D create view
2025-09-10 21:46:03.925 AnVR D release view

可以看到,在退出的时候执行了 onRelease

onReset

AndroidView 还支持一种情况,View 的复用。

想象一下在一个列表中使用 AndroidView 的情况:

Kotlin 复制代码
val items = remember { (0..20).toList().toMutableStateList() }

LazyColumn(
    modifier = Modifier.fillMaxWidth(),
    verticalArrangement = Arrangement.spacedBy(20.dp),
    contentPadding = PaddingValues(vertical = 20.dp),
    horizontalAlignment = Alignment.CenterHorizontally,
) {

    items(items = items, key = { it }) { id ->
        AndroidView(
            modifier = Modifier
                .fillMaxWidth()
                .height(120.dp)
                .background(Color.LightGray),
            factory = {
                Log.d("AnV","create $id")
                TextView(it).apply {
                    setTextColor(android.graphics.Color.RED)
                }
            },
            update = {
                Log.d("AnV","update $id")
                it.text = "@ $id"
            },
            onRelease = {
                Log.d("AnV","release $id")
            }
        )
    }
}

逻辑非常简单,我们在一个 LazyColumn 中,使用 AndroidView 加载普通的 TextView 去显示列表信息。

我们来回滚动一下列表,看下日志:

yaml 复制代码
2025-09-10 21:56:57.815 AnV D create 0
2025-09-10 21:56:57.836 AnV D update 0
2025-09-10 21:56:57.848 AnV D create 1
2025-09-10 21:56:57.852 AnV D update 1
2025-09-10 21:56:57.865 AnV D create 2
2025-09-10 21:56:57.868 AnV D update 2
2025-09-10 21:56:57.881 AnV D create 3
2025-09-10 21:56:57.884 AnV D update 3
2025-09-10 21:56:57.896 AnV D create 4
2025-09-10 21:56:57.899 AnV D update 4
2025-09-10 21:56:57.912 AnV D create 5
2025-09-10 21:56:57.915 AnV D update 5
2025-09-10 21:56:57.928 AnV D create 6
2025-09-10 21:56:57.931 AnV D update 6
2025-09-10 21:57:01.840 AnV D create 7
2025-09-10 21:57:01.844 AnV D update 7
2025-09-10 21:57:03.193 AnV D create 8
2025-09-10 21:57:03.196 AnV D update 8
2025-09-10 21:57:03.197 AnV D release 0
2025-09-10 21:57:03.807 AnV D create 9
2025-09-10 21:57:03.809 AnV D update 9
2025-09-10 21:57:09.833 AnV D create 1
2025-09-10 21:57:09.838 AnV D update 1
2025-09-10 21:57:09.842 AnV D release 1
2025-09-10 21:57:09.893 AnV D create 0
2025-09-10 21:57:09.897 AnV D update 0
2025-09-10 21:57:09.899 AnV D release 9

注意末尾几行,我们发现,View 被 release 了。

release 0release 1 表示 0View1View 被释放了,而往回滚动列表的时候,create 0 表示 0View 又被重新创建了。

默认情况下,AndroidView 不会自动缓存或重用 View。如果将其放置在可重复使用的容器中(包括在 LazyRowLazyColumn 中),当包含 AndroidView 的组合发生变化时,即使布局结构未发生变化且 View 理论上可以被重复使用,View 实例也总会被丢弃并重新创建。

如果我们想重复使用 View,需要实现 onReset

现在,我们修改一下代码:

Kotlin 复制代码
AndroidView(
    //...
    onRelease = { 
        Log.d("AnV","release $id")
    },

    onReset = { // 省略了部分代码,这里设置了 onReset
        Log.d("AnV","reset $id")
        it.text = ""
    },
)

指定 onReset 时,如果 LazyColumn 等容器决定回收一个 item 的视图时,Compose 会尝试保留该 View 实例,并在需要显示另一个 item 时,先调用 onReset 来重置其状态,然后将其插入新的 item 中,并立即调用 update 来设置新数据。

我们来看下效果:

yaml 复制代码
2025-09-10 22:06:09.950 AnV D create 0
2025-09-10 22:06:09.974 AnV D update 0
2025-09-10 22:06:09.986 AnV D create 1
2025-09-10 22:06:09.991 AnV D update 1
2025-09-10 22:06:10.003 AnV D create 2
2025-09-10 22:06:10.007 AnV D update 2
2025-09-10 22:06:10.020 AnV D create 3
2025-09-10 22:06:10.024 AnV D update 3
2025-09-10 22:06:10.035 AnV D create 4
2025-09-10 22:06:10.039 AnV D update 4
2025-09-10 22:06:10.049 AnV D create 5
2025-09-10 22:06:10.051 AnV D update 5
2025-09-10 22:06:10.064 AnV D create 6
2025-09-10 22:06:10.067 AnV D update 6
2025-09-10 22:06:11.372 AnV D create 7
2025-09-10 22:06:11.375 AnV D update 7
2025-09-10 22:06:11.807 AnV D create 8
2025-09-10 22:06:11.810 AnV D update 8
2025-09-10 22:06:12.370 AnV D reset 0
2025-09-10 22:06:13.849 AnV D reset 1
2025-09-10 22:06:13.910 AnV D update 9
2025-09-10 22:06:15.080 AnV D reset 9
2025-09-10 22:06:15.101 AnV D update 1
2025-09-10 22:06:15.132 AnV D reset 8
2025-09-10 22:06:15.181 AnV D update 0
2025-09-10 22:06:15.212 AnV D reset 7

当列表来回滚动的时候,不再执行 release 了,取而代之的是 reset。上述 log 中,已经不会再销毁 View 了。

当我们退出页面的时候:

yaml 复制代码
2025-09-10 22:08:17.364 AnV D release 3
2025-09-10 22:08:17.365 AnV D release 1
2025-09-10 22:08:17.366 AnV D release 7
2025-09-10 22:08:17.367 AnV D release 8
2025-09-10 22:08:17.368 AnV D release 5
2025-09-10 22:08:17.370 AnV D release 6
2025-09-10 22:08:17.372 AnV D release 2
2025-09-10 22:08:17.373 AnV D release 4
2025-09-10 22:08:17.374 AnV D release 0

每个 View 都会得到释放。

总结

本文介绍了 Compose 中 AndroidView 使用方法,用于在 Compose 中嵌入传统 Android View(如 WebViewMapView

以下几点是精华:

  1. AndroidView 通过 factory 参数创建 View
  2. update 响应状态变化,确保高效更新。
  3. onRelease 用于资源释放,这对于类似播放器的 View 中尤为重要。
  4. onReset 在可复用容器(如 LazyColumn)中实现 View 的复用,避免频繁创建和销毁,提升性能。

欢迎各位关注公众号,文章同步更新:

相关推荐
珹洺3 小时前
Java-Spring入门指南(五)Spring自动装配
android·java·spring
sylvia_08153 小时前
react native 初次使用Android Studio 打包
android·react native·android studio
前行的小黑炭3 小时前
Android:在项目当中可能会遇到的ANR,应该如何解决?
android·java·kotlin
老衲不服7 小时前
android 三方sdk minSdkVersion 兼容问题处理
android
android_xc10 小时前
Android Studio国内仓库配置
android·ide·android studio
FunnySaltyFish10 小时前
Kotlin 2.2.20 上新:新contract、跨平台编译稳定、默认Swift导出……
kotlin
alexhilton10 小时前
runBlocking实践:哪里该使用,哪里不该用
android·kotlin·android jetpack
2501_9151063211 小时前
iOS 使用记录和能耗监控实战,如何查看电池电量消耗、App 使用时长与性能数据(uni-app 开发调试必备指南)
android·ios·小程序·uni-app·cocoa·iphone·webview
雨白11 小时前
深入解析 Android 多点触摸:从原理到实战
android