如果你的 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 的复用,避免频繁创建和销毁,提升性能。

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

相关推荐
wb0430720115 小时前
如何开发一个 IDEA 插件通过 Ollama 调用大模型为方法生成仙侠风格的注释
人工智能·语言模型·kotlin·intellij-idea
用户416596736935515 小时前
Kotlin Coroutine Flow 深度解析:剖析 `flowOn` 与上下文切换的奥秘
android
2501_9159214315 小时前
运营日志驱动,在 iOS 26 上掌握 App 日志管理实践
android·macos·ios·小程序·uni-app·cocoa·iphone
沐怡旸15 小时前
【Android】详细讲解ViewDragHelper的实现原理(不含代码版)
android
cding15 小时前
Flutter 开发环境搭建
android·flutter
Digitally16 小时前
如何将一加手机的照片传输到笔记本电脑?
android
fatiaozhang952717 小时前
晶晨S905L3SB芯片_安卓9.0_高安版_支持外置WIFI_线刷固件包
android·华为·电视盒子·刷机固件·机顶盒刷机
旋律逍遥17 小时前
《AOSP上手》 2、Framework 开发小需求 “去掉原生 Launcher 中的 google 搜索栏”
android
liulilittle18 小时前
在 Android Shell 终端上直接运行 OPENPPP2 网关路由配置指南
android·linux·开发语言·网络·c++·编程语言·通信
低调小一18 小时前
KuiklyUI 科普:UI 如何映射到 Android View 并完成渲染
android·windows·ui