先聊一个我最近在技术社群里看到的讨论。有人发帖问:"现在还有必要学 XML 布局吗?感觉 Compose 才是趋势。"底下吵了三百多楼,一派说 XML 稳如老狗,存量项目根本动不了;一派说 Compose 大势所趋,新项目谁用 XML 谁落伍。说实话,两边都不完全对,但也都不完全错。让我从一线开发的视角聊聊真实体感。
其实从去年开始,圈子里陆续流行一个说法叫A2UI——AI to UI。意思很直白:用自然语言描述、设计稿截图、或者标注图,直接生成一套可运行的 Compose 界面代码。这意味着以前从设计稿到手写 XML、再逐个控价适配的流程,正在被整体重写。如果你现在还在用 XML 布局"养老",这篇文章建议耐心看完。
1. 先泼一盆冷水:XML 没死,但它的"舒适区"正在缩小
1.1 XML 为什么让人产生"养老"错觉:存量项目与肌肉记忆
我必须先说明,XML 布局并没有"被判死刑"。恰恰相反,市面上的存量 Android 项目里,百分之七八十的主界面还是 XML 写的。大型 App 的业务模块动辄几十上百个页面,全面迁移到 Compose 需要排期、需要回归测试、需要处理第三方 View 体系的历史包袱。很多团队的选择是:老模块继续用 XML 维护,新模块开始尝试 Compose。这本身就是一个理性决策。
但"理性决策"和"可以一直躺平"是两回事。XML 之所以给不少人带来"养老"的错觉,是因为它已经被 Android Studio 打磨得非常成熟:拖拽控件、实时预览、ConstraintLayout 辅助线、Layout Inspector 调试,这套工具的熟练度一旦建立,写起来确实像在舒适区里滑行。再加上团队里只要有一个"XML 熟练工",新人上手也快,维护成本看起来没那么高。
问题在于,舒适区是会被边界顶住的。
1.2 真正让你难受的 XML 场景:嵌套层级、传参、ViewBinding 的板结
如果你只在简单的静态页面里用 XML,你确实感受不到痛点。但稍微复杂一点的业务场景,XML 的软肋就会暴露出来:
- 嵌套层级失控。为了做一个圆角卡片加阴影,你需要 FrameLayout 套 LinearLayout 再套 CardView,最后发现层级树比业务树还复杂。布局嵌套过深,measure 和 layout 的开销成倍增长,卡顿就是这么一点点积累出来的。
- 状态和 UI 分离带来的样板代码。一个列表页面,你要写 Adapter、ViewHolder、item layout XML、空布局 XML、加载更多 footer XML,再手动处理 notifyDataSetChanged。UI 上的一个小改动,经常要动三个文件。
- ViewBinding 缓解了一部分问题,但没有消除"绑定"这个动作。你依然需要手动感知 View 的生命周期,手动在 onDestroy 里置空,手动处理复用逻辑。这不能说是错,但在业务迭代快的团队里,它确实是纯消耗。
我不是为了踩 XML 而踩 XML。我想表达的其实是:XML 这套体系不是不好,而是它的抽象层级太低了。它把"界面长什么样"和"界面如何跟数据互动"这两件事强行用两个语言体系分开,开发者的心智负担始终在切换。而 Compose 用 Kotlin 一套语言把状态、布局、交互统一起来,这恰恰是面向 UI 开发效率的底层重构。
2. A2UI 是什么:AI 生成 Compose 界面的工作流正在成型
2.1 从设计稿到 UI 代码,中间那层"脏活"终于有人接了
先解释一下 A2UI 这组缩写。我第一次听到是在一次线下 Android Meetup 上,一个做前端工程化的老哥提的。他的定义是:AI to UI,也就是用 AI 模型把设计输入(截图、PSD、Figma 链接、甚至一句需求描述)直接转换成可运行的 UI 代码。它不是某个单一产品的名字,而是一类工作流的统称。
为什么这件事和 Compose 绑在一起说?因为 Compose 本身就是 Kotlin 代码,天然适合作为 AI 生成的目标语言。XML 虽然也能生成,但 XML 只是"布局结构",你生成完还要自己去写绑定逻辑、写事件、写状态。Compose 不一样,它把布局、样式、状态、交互全写在一段 Kotlin 代码里,AI 一旦理解了你的设计稿,它可以一次性输出一个"能跑的页面",而不是一张"静态图"。
我试过几个在线 AI 生成 UI 的工具,输入一张电商首页截图,输出就是一段 Compose 代码:顶部搜索栏是 Row + OutlinedTextField,商品卡片是 Card + Column,Banner 轮播是 HorizontalPager。直接粘进项目,稍微调一下间距和主题色,就能在模拟器里看到和设计稿八成像的页面。这个效率,比起当年对着设计稿一格一格拉 ConstraintLayout 约束,确实是指数级的变化。
2.2 A2UI 的几种典型形态:代码生成、预览驱动、注解驱动
A2UI 并不是单一形态,我把它分成下面几类,方便你理解这个领域正在往哪些方向走:
| 形态 | 输入方式 | 输出 | 典型使用场景 |
|---|---|---|---|
| 对话式生成 | 自然语言描述 | 完整 Composable 函数 | 快速搭建原型页面 |
| 截图转代码 | 设计稿/截图 | 近似设计稿的 Compose 代码 | 前端联调、Activity 快速搭框架 |
| 预览驱动 | 拖拽 + AI 辅助 | 实时 Compose 预览与代码同步 | 需要精确调整布局细节 |
| 注解驱动 | 代码中加注解标签 | 自动生成样板代码 | 简化重复的列表、表单逻辑 |
聊到注解驱动,就必须提一下热搜里那个"android compose 注解标签 中文版本"。我在中文社区里见过有人把 Compose 的注解用法整理成中文速查表。什么叫注解标签?简单说,Compose 大量依赖 Kotlin 注解和编译器插件的协同工作,比如@Composable是标记一个函数"可以被组合"、@Preview是让 Android Studio 在 IDE 里渲染预览、@Stable和@Immutable是告诉编译器"这个类型可以安全跳过重组"。
AI 生成代码的时候,@Preview这种注解经常被漏掉,结果你生成了一堆函数却没法在 IDE 里快速预览,还得手动补。所以我在实际使用 A2UI 工具时,第一个习惯就是检查它有没有给可组合函数加@Preview。这一点说起来简单,不写的话调试效率差很多。
3. Compose 迁移实战:从一颗按钮开始的渐进式路线
3.1 渐进式迁移的关键前提:Compose 和 View 可以共存
很多人不敢碰 Compose,是担心"要么全换,要么不换"。这是个误解。官方从 Compose 1.0 开始就支持在既有 View 体系里渐进式接入。你可以把 Compose 塞进ComposeView,也可以反过来在 Compose 里通过AndroidView添加传统 View 控件。这意味着迁移可以是一点一点来的,不需要搞什么"原子弹式重写"。
我在实际项目里看过的比较稳的迁移方案是:先把不需要复杂触摸事件、不需要深度定制交互的页面切到 Compose。比如用户协议页、隐私政策页、纯展示的静态页面。这些页面用 XML 写也可能就是几个 TextView 加一个 Button,换成 Compose 毫无压力,而且能立刻让团队建立对 Compose 的信心。
第二步,把列表页迁过来。列表是 Compose 的强项:LazyColumn对比 RecyclerView,最大的优势是不需要 Adapter 和 ViewHolder。你只需要写一个数据集合,再写一个 item 的 Composable 函数,剩下的交给LazyColumn处理。少了整整一层样板代码。我见过一个社区项目,把一个 20 行的 RecyclerView 列表改装成 LazyColumn,代码量少了将近一半。
3.2 第一批适合迁移的组件:静态页面、列表、表单
再具体一点,我建议按这个顺序做迁移试点:
- 静态内容页:聚焦在文案展示、图片展示,没有复杂状态。
- 列表页:用 LazyColumn 代替 RecyclerView,测试性能和状态管理的差异。
- 表单页:输入校验、按钮状态切换,体验 MutableState 管理交互状态。
- 首页 Banner + 宫格:需要 ViewPager 的场景,Compose 用 HorizontalPager 实现,代码更直观。
其中表单页是最容易感受到 Compose 魅力的场景。以前用 XML 写一个登录页,EditText 的背景、错误提示、登录按钮的可点击状态,这些都要做 Selector 文件、写监听、手动更新 UI。Compose 里可以直接用一个MutableState<String>保存输入内容,用if (password.length < 6)这种普通 Kotlin 表达式控制错误提示显隐,UI 自动跟着状态变化。对做过多年 XML 的老开发来说,第一次看到这种写法的确会有点"我原来在写啥"的感觉。
3.3 迁移中容易踩的坑:状态提升、重组、生命周期
迁移不是无痛的,我讲几个实际踩过的点:
- 状态提升粒度。Compose 要求"状态尽可能上提,避免重复持有"。一开始接手时,我喜欢把状态存在每个 Composable 函数内部,结果多个子组件要同步同一个开关状态时,只能到处传回调。后来老老实实把状态提升到父级,使用
remember和rememberSaveable管理。这个概念如果你没有系统学过 Compose,会非常反直觉。 - 重组不是回调。在 Compose 里,界面会因为状态变化被反复执行(重组)。如果你在 Composable 函数体里直接做网络请求、文件读写、数据库查询,重组一次就执行一次,后果不堪设想。正确的做法是把副作用放进
LaunchedEffect或rememberCoroutineScope里。 - 生命周期不是 Activity 生命周期。Compose 的
DisposableEffect、LifecycleEventEffect可以感知宿主生命周期,但它的触发时机会比你想的更多。我在迁一个视频列表时,就因为没有妥善处理DisposableEffect的清理逻辑,导致页面在快速滑动时出现播放器资源未释放的警告。这类问题在 XML 时代根本不需要你想,因为 View 体系的回调把时机都定义好了,Compose 把这层"保护"去掉了,你必须自己掌控。
4. 我实测生成的 Compose 代码:能用,但要会"收拾"
4.1 生成 UI 的常见翻车点:命名、硬编码、状态缺失
我特地做了一个不严谨但真实的实验:拿一张外卖 App 门店列表截图喂给一个代码生成工具,让它输出 Compose 实现,然后把生成代码放进 Android Studio 编译运行。结论是"骨架能跑,细节需要人改"。
先说翻车点:
- 命名完全不可控。生成出来的 Composable 函数名可能是
Item()、Content()这种没有信息量的名字,你只能自己改成语义化的RestaurantCard()、DistanceBadge()。 - 硬编码严重。间距 16.dp、颜色
Color(0xFFF5F5F5)、字号 14.sp 全部焊死在代码里。虽然跑起来没问题,但这和项目已有的主题体系完全脱节。我在项目里强制要求所有颜色从MaterialTheme.colorScheme取,间距从Dimens对象取,生成代码显然做不到这一点。 - 状态处理缺失。生成出来的列表是静态的,点击事件、加载状态、错误重试这些一概没有。也就是说,A2UI 解决的是"静态 UI 快速成型",离"业务可用"还有相当一段距离。
我整理了一个更直观的对照表,方便你评估生成结果的可用度:
| 维度 | 生成代码表现 | 需要人工收拾的工程量 |
|---|---|---|
| 布局还原度 | 较高,常见组件能识别 | 微调间距与对齐 |
| 主题与资源 | 极低,硬编码居多 | 全面接入项目主题体系 |
| 状态管理 | 几乎没有 | 手动设计状态与事件流 |
| 可维护性 | 低,命名混乱 | 重构函数名,拆分组件 |
| 特殊效果 | 差,复杂动画基本失效 | 需要手写动画逻辑 |
4.2 怎么把生成物改造成生产级代码
我自己总结了一套"收拾"生成代码的流程,现在分享出来,也许对你也有用:
第一步,先跑通再重构。不要一开始就去改代码风格,先把生成代码粘进项目,确认它能编译、能在模拟器上显示出来。跑通的前提是项目已经完成 Compose 的基础配置,包括依赖、Compiler 插件、主题接入。这一步出问题的话,先解决环境,不要和代码较劲。
第二步,命名语义化。把Item()、ColumnScope.EmptyLayout()这类名字全部替换成业务可读的名称。这一步不艰难,但会产生大量 diff,建议用 IDE 的重构功能逐层处理。
第三步,资源外置。把硬编码的颜色、字体、间距抽到主题和常量里。这一步可以和设计师对齐设计规范,把生成代码当作视觉基准,而不是最终答案。
第四步,补状态与事件。确认每个可交互元素都有独立的状态管理。比如商品卡片的加号按钮,要不要禁用、点击后要不要弹 Toast、数量上限是多少,这些必须你写,AI 帮不了你。
最后一步,对照设计稿走查。在@Preview里渲染多种尺寸的设备,或者直接用 Layout Inspector 检查实际渲染树的层级和尺寸。尤其注意文本溢出、图片缩放模式、无障碍语义这些细节。
整个过程下来,我通常在二十分钟到半小时能做出一版"能见客户"的页面。对比以前手写 XML 加适配的时间,效率提升还是比较明显的。但它不能替代开发和设计师的判断力,它放大的是你的生产力,而不是你的思考力。
5. 关于 XML 和 Compose 的最终判断:不是谁取代谁,而是你的技能树
5.1 学习 Compose 的推荐路线(按角色)
如果这篇文章被刚入行的同学看到,我会特别想提醒一句:别在 XML 上投入太多新学习精力了,把时间留给 Compose 和 Kotlin 本身。这并不是说 XML 完全没有学习的价值,而是同样的时间投入在 Compose 上,当前的回报率明显更高。你要理解 XML 至少得会看,能维护老项目;但你要开发新功能,Compose 是更值得的投入对象。
不同角色的同学,我建议这样安排学习路线:
- 应届生 / 转行新人:直接学 Compose,练习项目也用 Compose 写。学的时候顺带理解 View 和 XML 的概念即可,因为你入职的公司大概率有存量项目,不可能完全绕开 XML。重点是"看得懂 XML",而不是"精通 XML"。
- 中级开发:掌握 Compose 的
remember、mutableStateOf、LaunchedEffect、derivedStateOf这几大核心,把 Compose 的"状态驱动"思维刻进脑子里。这是和 XML 时代最大的思维差异。 - 高级 / 架构岗:关注 Compose 的稳定性、性能优化、自定义布局、
Modifier底层原理。招聘的时候,我看到熟练掌握 Compose 的候选人,会优先考虑,因为他有可能会是团队里推动 UI 基础设施改革的人。
5.2 面试和简历上,Compose 的分量
说到招聘,我接触过不少候选人的简历,有个让我很在意的倾向:很多人把"熟悉 XML"写得掷地有声,但问 Compose 的时候只能答出"用过 LazyColumn"。这不是说 XML 经验无用,而是在行业应用层面,Compose 已经成为新项目的主流选择。
从热搜关键词都能看出来,越来越多的人开始搜索"android compose 注解标签 中文版本""svg → compose imagevector kotlin""android 动态图标主题"。这说明什么?说明生态里的工具链正在快速从"面向 View"转向"面向 Compose"。矢量图标、主题动态化、AI 生成 UI,这些新东西的原生支持几乎都是优先围绕 Compose 展开的。你现在重新去研究 XML 的高级技巧,短期内也许有用,但长期回报会越来越低。
写到这里我想起一个真实的项目经历。去年团队里有一个老工程师,写 XML 写了八年,是组里最熟练的 ConstraintLayout 玩家。Z 世代产品侧要做一个新的导流活动页,设计稿只给了 Figma 链接,要求两天内上线。传统思路是导出切图、写 XML、调整不同机型的适配,但时间太紧。另一个同事用了 A2UI 工具直接生成 Compose 代码,半天就出了能交互的 Demo,又花半天接入统计和接口,第二天如期上线。那位老工程师后来和我说,他第一次感受到"手写 XML 多巴胺"被真正替代了,不是被某个工具替代,而是被一套更高效的抽象方式替代了。
我个人现在的态度是:XML 是好东西,它是 Android 历史上的功臣,但它的抽象层级停留在"静态描述"阶段;Compose 把 UI 变成"可编程的状态流",这才是面向复杂业务和高迭代速度的正确姿态。你没有必要因为热爱 XML 而排斥 Compose,但如果你连 Compose 都不愿意尝试,那就不是热爱,而是框住自己手脚的惰性了。
最后分享一个我自己的小习惯:我把之前练手的 XML 项目里两个高频页面(一个商品列表、一个个人中心)直接用 Compose 重写了一遍,把重写前后的代码量、文件数、调试时间记在笔记里。结果不是用来证明 Compose 比 XML 强,而是给自己留下一份"转型记录"。等哪天团队需要决策是否全面迁移时,这份对比数据会比情绪更有说服力。