尧图精选

Jetpack Compose 预览(@Preview)全面指南:从零到交互式 UI 调试

🕒 发布时间:2026/10/2 10:32:39 📁 来源:尧图网络
1. 先说清楚此 Compose 非彼 Compose如果你是在搜索框里直接输入Compose三个字母大概率会看到两种完全不同的东西一种叫 Docker Compose一种叫 Jetpack Compose。我见过不少人在群里问Compose 怎么部署 nacos 3.x也有人问Compose 预览为什么白屏两边聊了半天才发现根本不是同一个东西。Docker Compose 是容器编排工具用 YAML 描述一组服务docker compose up一拉全起它所谓的预览通常指docker compose config这类解析检查而 Jetpack Compose 是 Android 的新一代声明式 UI 工具包用 Kotlin 写界面它真正意义上的预览是 Android Studio 里的实时渲染功能。这篇文章要讲的是后者Jetpack Compose 的完整预览教程。它解决的是 Android 开发里最磨人的一个问题改完 UI 代码想看一眼效果到底要不要启动模拟器、要不要打包装到真机上传统 XML 时代Layout Editor 勉强能看但慢、笨、不够实时。Compose 的预览把这件事做到了极致——你在代码里写一个Preview注解右侧面板立刻渲染出组件效果改参数、改间距、换主题几乎零延迟。这个功能对开发效率的提升是肉眼可见的尤其适合做多设备适配、深浅色模式、多语言切换这类需要反复比对效果的场景。不管你是刚接触 Compose 的新手还是已经写了一阵子 UI 但没用透预览的老手这篇内容都值得完整看一遍。你可能还会看到一句眼熟的警告你尝试预览的文件可能对你的计算机有害。如果你信任此文件以及其来源请打开此文件。这是浏览器或系统对可疑附件的拦截提示。预览这个概念本身其实很安全Compose 预览也同理——它只是在 IDE 里渲染 UI不会执行业务逻辑不会发网络请求也不会写文件。所以放心大胆地多用预览它不会打开一个炸弹只会帮你提前排除 UI 问题。2. 预览到底解决了什么问题2.1 传统 UI 调试的痛点写过 XML 布局的人都有体会layout文件改完切到 Design 页要看半天有时还要Build一下才刷新。真要验证交互就得跑模拟器冷启动一次少说 20 秒热启动也要好几秒App 一大了更是折磨。而且 XML 加各种match_parent、wrap_content嵌套嵌套多了布局层级深性能问题也难查。写代码和看效果之间的反馈回路太长人很容易烦躁效率自然上不去。Compose 预览的核心价值就是把改代码—看效果的回路压缩到几乎为零。它是 IDE 层面的即时渲染改动 Kotlin 代码后Design 面板里对应组件立刻更新不需要重新打包不需要装模拟器。这种体验有点像在文档工具里编辑富文本所见即所得。2.2 预览的适用场景日常开发里预览最能发力的场景是这几个纯静态 UI 的验证卡片布局、按钮样式、列表项、空态、加载态这些不需要真实数据的界面预览直接渲染一眼看出间距、字体、颜色对不对。多状态展示同一个组件在普通态、深色态、大字体态、不同语言下的表现用多个预览函数组合一次全看。多设备适配手机、折叠屏、平板不同尺寸预览支持模拟不同设备提前发现布局溢出或拉伸问题。组件库开发如果你在做团队内部的 Compose 组件库预览就是组件的文档配合group参数把组件分类Design 面板里可以按分组筛选效果比翻文档直观多了。换句话说真机调试不是不需要而是应该留到做真实交互、数据链路、性能检测的时候再去跑。纯 UI 层面的事预览全包了。2.3 预览的边界预览再强也有它的边界。凡是涉及真实的生命周期、网络请求、传感器、复杂状态流转的逻辑预览是模拟不了的。比如LaunchedEffect里的网络加载、ViewModel的数据驱动更新预览环境不会执行这些逻辑。理解这条边界很重要否则你会花很多时间在预览里折腾一个永远跑不起来的交互最后发现方向就不对。预览负责看长相真机负责验动作两者分工不同。3. 从零开始Preview 注解的完整参数拆解3.1 最小可用的预览长什么样先看最基础的写法。定义一个Composable函数给它加上Preview注解就能在 Design 面板里预览。Preview Composable fun GreetingPreview() { MyAppTheme { Greeting(Android) } }这里有一个很容易被忽略但很重要的点预览函数内部最好用你的应用主题MyAppTheme包一层。如果不包编译是能过的预览也能显示但组件会使用 Material 默认主题的配色和字体风格和你 App 实际运行时的观感可能差很远。尤其是你用到了自定义ColorScheme、动态取色这类主题逻辑不包 Theme 的预览基本等于白看。所以我的习惯是所有预览函数都套一层应用主题宁可多敲几个字也不要看到假效果。还有一个细节Preview注解标注的函数不能有参数否则 IDE 没法决定传什么数据进去。如果组件本身需要参数怎么办后面第 5 节会讲PreviewParameter这个解法。3.2 常用参数逐条解读Preview不是空注解它自带十几个参数每个参数对应一种预览行为。我把最常用的整理成一张表参数类型作用典型写法nameString预览卡片显示的名称name 深色模式卡片groupString预览分组Design 面板可按组筛选group 列表组件showBackgroundBoolean是否显示浅色/深色背景区showBackground truebackgroundColorLong自定义背景色ARGB 格式backgroundColor 0xFF202124showSystemUiBoolean是否显示状态栏、导航栏等系统窗口showSystemUi truewidthDpFloat预览宽度单位是 dp 的数值widthDp 360fheightDpFloat预览高度heightDp 640flocaleString模拟地区语言locale zh-rCNfontScaleFloat字体缩放倍数fontScale 1.5fuiModeIntUI 模式深色/浅色/夜间等uiMode Configuration.UI_MODE_NIGHT_YESthemeKClass指定预览使用的主题类theme MyAppTheme::classdeviceString模拟设备规格device id:pixel_8_pro我单说几个常见的坑。widthDp和heightDp接收的是 Float不是Dp类型。新手最容易在这里翻车写了widthDp 360.dp编译直接报错。正确的写法是widthDp 360f因为注解参数必须用编译期常量不能是运行时对象。showSystemUi这是个好东西。默认情况下预览只画组件本身不显示系统状态栏和导航栏看起来有点像悬浮的 UI 碎片。当你想模拟一个 Activity 里完整页面的真实观感把这个参数设成true预览顶部会出现状态栏、底部出现手势导航条整体效果更接近真实设备。backgroundColor要注意格式。它接收的是 Long 类型的 ARGB 值0xFF202124表示不透明的深色前面两位FF是 Alpha 通道。很多人写0x202124丢掉了透明度前缀结果是展示成半透明甚至全透明背景一片奇怪的颜色。group参数是团队协作里的隐藏神器。一个项目里可能有几十个预览函数全部堆在 Design 面板里乱成一团。给它们分好组比如登录组件首页卡片个人中心入口Design 面板的筛选器就能快速定位到某一组看代码 Review 的时候也非常清爽。3.3 多预览组合的实战写法实际开发里一个组件往往需要覆盖多个状态。比如一张商品卡片至少要看默认态、深色态、大字体态和英文态。你可以写四个独立的预览函数也可以拆开组合Preview( name 商品卡片-默认, group 商品, showBackground true ) Preview( name 商品卡片-深色, group 商品, uiMode Configuration.UI_MODE_NIGHT_YES, showBackground true ) Preview( name 商品卡片-大字体, group 商品, fontScale 1.5f, showBackground true ) Preview( name 商品卡片-英文, group 商品, locale en-rUS, showBackground true ) Composable fun ProductCardPreview() { MyAppTheme { ProductCard( title 无线降噪耳机, price 1999, image painterResource(R.drawable.ic_product) ) } }注意这些注解是叠在一个Composable函数上的。Android Studio 会为每个注解各生成一张预览卡片所以你可以在 Design 面板里同时看到四个卡片。我最常用的组合是默认 深色 大字体基本上能覆盖 90% 的 UI 走查需求。这种堆叠注解的方式代码看起来是有点冗余但换来的是一次写四张卡片效率很值。4. 多设备、多主题与无障碍预览4.1 用 device 参数模拟不同设备手机厂商的屏幕尺寸五花八门折叠屏、平板也早已普及。靠真机一台台去适配不现实预览的device参数就是为了解决这个场景。Preview( name Pixel 8 Pro, device id:pixel_8_pro, showBackground true ) Preview( name 折叠屏展开态, device spec: width673dp,height841dp,orientationlandscape, showBackground true ) Composable fun HomePagePreview() { MyAppTheme { HomePageContent() } }device参数支持两种写法。一种是id:xxx直接引用 Android Studio 内置的设备定义比如pixel_8_pro、pixel_5、nexus_5等。另一种是spec:xxx自己定义设备规格语法如下spec: width360dp,height640dp,orientationportrait,roundtrue,chin_size24dpspec的可选键包括width、height、orientation、round、chin_size等。orientation可以写portrait或landscaperound表示是否模拟圆形屏幕chin_size是屏幕下巴高度。这种方式非常灵活比如你想模拟一块 1:1 的方形屏幕直接写spec: width400dp,height400dp就行比找对应设备 id 省事得多。不过我得提醒一个实战里的教训device预览不要贪多。以前我就一个列表组件挂了八个设备预览结果每次改代码都要等十几秒编译刷新反而拖慢了节奏。现在的做法是固定两个一个主流手机Pixel 8 Pro 这类尺寸一个自己的目标设备。真要做全设备适配不如用 Compose 的BoxWithConstraints或者 WindowSizeClass 之类的方案做响应式布局再配合少量关键设备预览来验证。4.2 深浅色一键双看PreviewLightDark 与 PreviewDark如果你的 App 做了深色模式适配那么每个组件至少得看一遍深色效果。手动写uiMode Configuration.UI_MODE_NIGHT_YES虽然能看但每次只显示一张卡片想对比深浅色差异还要来回切。Compose 提供的PreviewLightDark注解可以直接解决这个问题。PreviewLightDark Composable fun ColorfulCardPreview() { MyAppTheme { ColorfulCard() } }这个注解会自动生成两张预览卡片一张亮色模式一张暗色模式。你不需要传任何参数也不用手动指定uiMode。原理上它其实是一个组合注解内部展开了两次预览配置一次用UI_MODE_NIGHT_NO一次用UI_MODE_NIGHT_YES。类似的还有一个PreviewDark只生成暗色一张。如果你只关心深色是否正常用PreviewDark就够了预览面板能少一张卡片干净一些。这里也回应一下热词里那句中英文对照的疑问。Compose 有一系列注解标签中文社区里经常有人整理成中文版注解标签Preview是预览PreviewLightDark是亮暗预览PreviewParameter是预览参数注入PreviewWallpaper是预览壁纸SampleData是示例数据。搞清楚这些注解的中文含义记起来会顺手很多。4.3 动态主题预览theme 参数的正确打开方式如果你的 App 支持动态取色Dynamic Color或者有多套主题可以在运行时切换预览时最好显式指定主题类。Preview( name 新版主题-亮色, theme MyAppTheme::class, showBackground true ) Composable fun SettingsPagePreview() { SettingsContent() }注意theme MyAppTheme::class的写法这里传的是 KClass 引用不是实例。它的作用相当于在预览环境里设定一个CompositionLocalProvider级别的主题上下文让组件渲染时优先使用你指定的主题。如果你的 App 只有一套主题不写这个参数其实问题不大因为你的预览函数内部一般也会包MyAppTheme。但如果有多套主题或者你在做主题切换功能建议把theme参数用起来否则预览里的配色可能和你预期的主题不匹配排查起来很费劲。4.4 字体缩放与多语言预览国际化 App 里字体缩放和语言切换是两个高频验证场景。fontScale参数直接控制预览里的字体放大倍数注意它是相对数值1.0f是默认1.3f模拟系统大字体2.0f是极端放大。国内不少用户会把系统字体调到最大如果你的布局用了固定高度或固定宽度放大字体会出现文字截断。这类问题在真机上很难提前发现因为很少有人随身带一台大字体设置好的测试机但预览里一个参数就能看到。语言切换用locale参数值格式是语言代码-地区代码比如zh-rCN表示简体中文、en-rUS表示美式英文、ja-rJP表示日文。写完一段文案后直接加上locale en-rUS看一眼英文状态下的换行比真机切系统语言快得多。5. 可交互预览与动态数据注入5.1 可交互预览预览面板里的真机Android Studio 的预览面板右上角有一个可交互模式按钮图标是一个小手加一个圆圈点了之后预览卡片就变成一个可以点击的界面。你可以在里面点按钮、切换 Switch、输入文字、滚动列表。这个模式对我这种日常做表单类页面的开发非常有用以前验证一个按钮的点击态、一个输入框的焦点态都得跑一次真机现在直接在预览面板里完成。Preview( name 登录表单-交互, showBackground true ) Composable fun LoginFormPreview() { MyAppTheme { LoginScreen() } }需要注意的是可交互预览只能识别组合函数内部的mutableStateOf状态变化。如果你在代码里直接写了网络请求、数据库访问这些在预览环境不会真的执行。另外可交互预览对动画的支持取决于 Android Studio 版本我用过的 Electric Eel 之后的版本基本都能流畅预览AnimatedVisibility这类简单动画。如果你的预览面板里可交互按钮是灰色的先检查 Android Studio 版本是不是太老。5.2 PreviewParameter给预览批量喂数据很多Composable函数是有参数的比如一个用户信息卡片接收User对象一个新闻列表接收ListNews。Preview要求函数无参那怎么预览带参组件答案是PreviewParameter。class PreviewUsers : PreviewParameterProviderUser { override val values sequenceOf( User(张三, 在线, true), User(李四, 离线, false), User(王五, 勿扰, true) ) } Preview Composable fun UserCardPreview( PreviewParameter(PreviewUsers::class) user: User ) { MyAppTheme { UserCard(user) } }PreviewParameterProvider是接口实现values属性返回一个数据序列。预览时IDE 会把你提供的每一个数据项都渲染成一张卡片。比如上面这个例子里有三个User对象预览面板就会生成三张用户卡片一眼看完正常态、离线态、勿扰态三个分支。用这个 API 有两点要注意。第一values是一个Sequence惰性求值数量控制好。我一般控制在 5 个以内太多会拖慢预览构建。第二PreviewParameterProvider最好不要在生产代码里直接引用真实业务数据类更不要让 provider 里的数据来自网络。它的作用只是喂假数据给预览看如果哪一天有人不小心在生产环境的调用链里初始化了这个 provider容易出问题。所以我习惯把它定义在 preview 相关的包或者文件里跟正式代码隔离。5.3 SampleData 与动画预览的进阶玩法SampleData是另一个预览数据注入手段它配合 Android Studio 的 Sample Data 功能使用可以从 JSON 文件或资源文件里读取示例数据作为预览输入。它的好处是数据不用写在 Kotlin 代码里适合数据量比较大、字段比较多的场景。动画预览方面早先的预览确实只能看静态后来 Android Studio 逐步增强了Preview对动画的支持。rememberInfiniteTransition这类无限动画在可交互模式下已经可以播放但性能一般复杂动画还是建议真机看。我自己的经验是动画预览最适合验证动画是否存在元素是否按照预期位移/淡入淡出不适合做性能调优。真机上的帧率、卡顿预览还是力不从心。5.4 SVG 转 ImageVector 与预览图标不显示的坑热词里有一条svg → compose imagevector kotlin这个话题和预览的关系非常密切。Compose 里不能用传统的Bitmap直接当图标而是要用ImageVector这样的矢量图形对象。如果设计给的资源是 SVG你需要转成 Compose 能用的格式。常见的转换途径有两种。一种是 Android Studio 自带的 Vector Asset 工具在 res 目录右键 New Vector Asset选择本地 SVG 文件IDE 会自动生成对应的 drawable XML 资源然后在 Compose 里用painterResource(R.drawable.ic_xxx)加载。这种方式生成的实际上是 Android 的 VectorDrawable 资源预览一般能正常显示。另一种是使用三方工具把 SVG 直接转成 Kotlin 文件ImageVector的构造代码生成的是纯 Kotlin 对象适合打包体积敏感的团队。预览里图标不显示大部分时候不是转换的问题而是资源路径或构建缓存的问题。改完ImageVector或 SVG 后预览面板里图片还是空的我会先做一次 Rebuild再看资源文件是否放在正确的目录。另外如果你用的是动态生成的ImageVector代码注意group和path的命名不能重复否则合并时会被 IDE 合并导致显示异常。6. Android Studio 预览面板的完整操作技巧6.1 Design / Split / Code 三种模式打开一个带Preview的 Kotlin 文件编辑器右上角有四个图标Code、Split、Design、Preview。Code 是纯代码视图Design 只显示预览面板Split 是代码和预览上下分屏。我强烈建议日常开发用 Split 模式左边码代码右边看效果改一个参数就变一下。Preview 的意思是只显示预览面板而不进入设计画布适合单独审查 UI 的时候用。如果你在编辑器里找不到设计面板大概率是当前文件没有任何Preview注解或者 Android Studio 正卡在构建。检查一下右下角的进度条等构建完成再操作。6.2 预览工具栏的隐藏功能预览面板顶部有一条工具栏第一个是刷新按钮两个箭头绕圈的图标点击会强制重新构建当前预览。旁边是设备下拉框你可以在这里临时切换预览设备。再右边是缩放比例可以从 25% 到 200%放大镜看细节很有用。还有一个旋转按钮点击可以把预览方向在横竖屏间切换。这些操作最大的价值在于它们不会污染你的代码。你不需要为了看一眼横屏效果去改某个device参数然后再改回来直接在工具栏里切就行。6.3 手动触发预览更新大多数时候 IDE 会自动增量编译并刷新预览但总有抽风的时候。比如你改了一个资源 XML或者改了一个自定义Composable函数的文件名预览会卡在旧版本上。这时候先点刷新按钮不行就Build Rebuild Project再不行就File Invalidate Caches and Restart。我遇到预览不更新的情况90% 是增量编译缓存出问题Rebuild 一下基本都能解决。另外预览构建也依赖 Gradle 同步。如果build.gradle.kts里新增了依赖比如 Compose Material3 的某个新版本最好先 Sync 一次再回来看预览。Preview背后不是魔法的根源就在这里它本质上是 IDE 调用 Gradle 编译产物来渲染所以构建链路任何一环出问题预览都会挂。7. 常见问题与避坑实录预览用久了多少会碰到一些让人觉得莫名其妙的问题。我把这些年踩过的坑整理成一个速查表基本覆盖了 90% 的预览疑难杂症。现象可能原因解决方案预览面板空白函数没有Preview注解检查注解是否添加报错 Preview requires at least one函数不是Composable给函数加Composable修饰符预览不刷新Gradle 增量构建缓存问题点刷新或 Rebuild或 Invalidate Caches预览颜色和真机不一致预览函数没包 Theme用MyAppTheme包裹或指定theme参数widthDp编译报错传了Dp类型改成 Float 数值如360f深色模式不生效忘了uiMode或PreviewLightDark补上对应注解或参数大字体下文字截断固定高度容器导致真机或预览中查看布局约束改用自适应高度多 device 预览卡顿预览注解太多精简到 2-3 个关键设备可交互模式按钮灰色IDE 版本过旧或当前组件不支持升级 Android Studio检查组件是否使用真实状态图标不显示ImageVector 资源路径或缓存问题检查资源文件Rebuild 后再看分组筛选找不到卡片group参数拼写或大小写不一致统一分组名规范建议英文常量这里重点讲一下预览颜色和真机不一致的问题。Color 在 Compose 中有很多来源MaterialTheme.colorScheme、Color(0xFF....)、resource(R.color.xxx)。如果你在预览函数里直接写死Color(0xFF333333)那预览显示的就是这个颜色和真机必然一致。但如果走的是主题色比如MaterialTheme.colorScheme.primary而你的主题里定义了动态取色或品牌色替换逻辑预览和你当前系统的动态取色结果就可能不一致。解决办法就是前面反复强调的预览函数外面包一层和 App 一致的MyAppTheme。还有一个很容易忽略的预览里用R.drawable.xxx加载的图片资源如果是 WebP 或者较大尺寸的 PNG预览构建会明显变慢。建议预览状态的图片占位图尽量用纯色或ImageVector减少解码开销真机跑的时候再替换成真实资源。最后再分享一个我自己的习惯所有预览相关的数据类、Provider 类统一命名为PreviewXxx或XxxPreviewData放在项目的preview包下。这样即使用户误引用了预览数据代码审查的时候一眼就能看到。我还习惯在Preview注解的name里写清楚这是哪个页面、哪个状态比如登录页-密码错误提示配合group分组整个 Design 面板就好像一个可视化的组件文档别人接手代码时能少问很多问题。预览功能我从 Compose 还叫 Alpha 的时候就开始用了这几年看着它从只能看静态快照进化到可以交互、可以模拟多设备、可以喂假数据。平时真机跑得再多也不如花十分钟把各种状态都堆到预览面板里一次看完来得高效。如果你现在还是写完代码直接跑真机的习惯我建议从下一个组件开始先写Preview再看效果慢慢你就会发现真机调试的次数会断崖式下降但代码质量和走查效率反而上来了。Compose 的版本更新很快预览相关的注解和 IDE 功能也在持续迭代遇到不认识的参数或按钮先看一眼当前版本的官方更新说明别在旧习惯里打转。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →