Compose 与 View 互操作
本教程共 100 篇 · 第 55 篇 · 更新于 2026-07-28 · 约 7 分钟阅读
55. Compose 与 View 互操作
本节目标:学会在 Compose 中嵌入传统 View(AndroidView)、在 View 体系中嵌入 Compose(ComposeView),理解迁移策略和互操作注意事项。
为什么要互操作
很多项目不是从零开始用 Compose,而是从传统 View 体系逐步迁移。迁移过程中,新旧代码需要共存:
- 新写的 Compose 页面里要嵌入一个还没迁移的老地图组件
- 老的 Fragment 里想用 Compose 写一小块界面
- 第三方库只提供 View,没有 Compose 版本
互操作 API 让两套体系能无缝混合。
AndroidView:在 Compose 里嵌入 View
AndroidView 把一个传统 View 嵌入 Compose 组合树:
@Composable
fun MapViewComposable() {
AndroidView(
factory = { context ->
// 创建 View,只在首次进入组合时调用
MapView(context).apply {
onCreate(null)
getMapAsync { googleMap ->
googleMap.moveCamera(
CameraUpdateFactory.newLatLngZoom(LatLng(39.9, 116.4), 10f)
)
}
}
},
modifier = Modifier.fillMaxSize()
)
}
factory:创建 View 的 lambda,只在首次组合时执行一次。modifier:像普通 Compose 组件一样设置尺寸和位置。
用 update 响应状态变化
当 Compose 状态变化需要更新 View 时,用 update:
@Composable
fun TextViewInCompose(text: String, color: Color) {
AndroidView(
factory = { context -> TextView(context) },
update = { textView ->
// 每次重组时调用,同步最新状态到 View
textView.text = text
textView.setTextColor(color.toArgb())
textView.textSize = 16f
},
modifier = Modifier.fillMaxWidth()
)
}
Note
factory只在 View 创建时调用一次,update每次重组都调用。创建放factory,状态同步放update。别在update里创建新 View。
嵌入 WebView
@Composable
fun WebViewComposable(url: String) {
AndroidView(
factory = { context ->
WebView(context).apply {
settings.javaScriptEnabled = true
webViewClient = WebViewClient()
loadUrl(url)
}
},
modifier = Modifier.fillMaxSize()
)
}
ComposeView:在 View 体系里嵌入 Compose
反过来,在 XML 布局或 Fragment 里嵌入 Compose:
<!-- XML 布局里 -->
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// 代码里设置内容
val composeView = findViewById<ComposeView>(R.id.compose_view)
composeView.setContent {
MyTheme {
Greeting("Android")
}
}
或者直接在代码里创建:
class MyFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
setContent {
MyTheme {
MyScreen()
}
}
}
}
}
ViewCompositionStrategy 生命周期策略
ComposeView 需要配置何时销毁 Compose 内容:
| 策略 | 销毁时机 |
|---|---|
DisposeOnViewTreeLifecycleDestroyed | 父 ViewTree 的 LifecycleOwner 销毁时(默认) |
DisposeOnLifecycleDestroyed | 指定的 Lifecycle 销毁时(需传入 Lifecycle 对象) |
DisposeOnDetachedFromWindow | View 从窗口分离时 |
composeView.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
Warning
- 在 Fragment 里用 ComposeView 时,推荐用
DisposeOnViewTreeLifecycleDestroyed。如果用DisposeOnDetachedFromWindow,在 Fragment 的 View 被销毁但 Fragment 还活着时(比如加回返回栈),Compose 内容会被销毁,回来后重建可能丢失状态。
AbstractComposeView 自定义组合 View
如果要做一个可复用的「Compose 版自定义 View」:
class RatingBarView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : AbstractComposeView(context, attrs, defStyle) {
var rating by mutableStateOf(0f)
var onRatingChange by mutableStateOf<(Float) -> Unit>({})
@Composable
override fun Content() {
Row {
repeat(5) { index ->
Icon(
if (index < rating.toInt()) Icons.Default.Star
else Icons.Default.StarBorder,
contentDescription = null,
modifier = Modifier.clickable {
onRatingChange(index + 1f)
}
)
}
}
}
}
这样在 XML 里就能直接用 <RatingBarView />,属性通过代码设置。
互操作注意事项
主题不统一
Compose 的 MaterialTheme 和 View 的 XML 主题是两套体系。在 View 里嵌 Compose 时,记得给 Compose 内容套上主题:
// 正确:套主题
composeView.setContent {
MyAppTheme {
MyComposable()
}
}
// 错误:没主题,颜色和字体不对
composeView.setContent {
MyComposable()
}
性能
AndroidView嵌入的 View 不会参与 Compose 的智能重组优化,每次update都会执行。- 频繁变化的状态驱动
AndroidView可能比纯 Compose 慢。 - 能用 Compose 原生组件替代的,尽量别用
AndroidView。
生命周期和保存状态
AndroidView 嵌入的 View 的生命周期跟随组合。如果 View 需要保存状态(如 WebView 的滚动位置),用 rememberSaveable 配合 View 的 onSaveInstanceState。
迁移建议
- 从叶子组件开始:先迁移独立的卡片、列表项等小组件,风险低。
- 新页面直接用 Compose:新功能用 Compose 写,老页面保持不变。
- 先 Fragment 后 Compose 导航:迁移初期继续用 Fragment 导航,每个 Fragment 内部用
ComposeView。全迁移完再换 Navigation Compose。 - 共用 ViewModel:Compose 和 View 可以共享同一个 ViewModel,数据层不用改。
Tip
- 迁移是渐进的,别试图一次性全换。新旧共存期间,保持数据层统一(ViewModel + Repository),UI 层逐步替换。
小结
AndroidView 在 Compose 里嵌入 View,ComposeView 在 View 体系里嵌入 Compose,两者让渐进迁移成为可能。注意主题统一、生命周期策略、性能差异。迁移从叶子组件开始,新页面直接用 Compose,最终把导航也换成 Navigation Compose。下一节学习 Compose 性能优化。