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

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

相关推荐
betazhou17 分钟前
MySQL相关性能查询语句
android·数据库·mysql
一起养小猫23 分钟前
Flutter for OpenHarmony 进阶:Timer组件与倒计时系统深度解析
android·网络·笔记·flutter·json·harmonyos
符哥200840 分钟前
Fastjson2.X 使用详解
android·java
月明泉清1 小时前
Android中对于点击事件的深度梳理(三)
android
电饭叔1 小时前
DataFrame和 Series 索引
android·python
lexiangqicheng1 小时前
【全网最全】React Native 安卓原生工程结构与构建机制深度解析
android·react native·react.js
数据蜂巢1 小时前
MySQL 8.0 生产环境备份脚本 (Percona XtraBackup 8.0+)
android·mysql·adb
jingling5552 小时前
uniapp | 基于高德地图实现位置选择功能(安卓端)
android·前端·javascript·uni-app
缺一句感谢和缺一句道歉2 小时前
Module was compiled with an incompatible version of Kotlin.
java·kotlin
fatiaozhang95272 小时前
晶晨S905L/S905LB-通刷-slimbox 9.19-Mod ATV-安卓9-线刷固件包
android·电视盒子·刷机固件·机顶盒刷机