
这一篇不讲业务逻辑专门讲“看起来像不像成品”。旅行 App 很吃图片和启动体验资源命名、图片裁切、渐变遮罩、启动窗口背景和应用内启动页都会影响用户第一印象。项目落点图片资源entry/src/main/resources/base/media启动窗口entry/src/main/module.json5启动背景色entry/src/main/resources/base/element/color.json应用内启动页LaunchSplash()图片展示ImageFit.Cover、主题色兜底、渐变层读完这一篇你可以复用一套离线图片管理思路图片走资源系统数据层保存ResourceUI 层统一裁切和遮罩启动体验保持前后一致。本文解决什么把本地图片放进资源系统避免页面拼文件路径。让启动窗口背景、应用内启动页和首屏色调保持一致减少冷启动跳变。用主题色、Cover 裁切和渐变遮罩统一不同来源图片的观感。旅行 App 的一半体验在图片旅行攻略应用和普通工具应用最大的区别是图片非常重要。《疆域纪行》内置了大量本地图片资源entry/src/main/resources/base/media/ ├── img_kanas.jpg ├── img_sayram.jpg ├── img_duku.jpg ├── img_kashgar_oldtown.jpg ├── img_dapanji.jpg ├── splash_fullscreen.jpg ├── splash_poster.png ├── startIcon.png └── ...这些图片覆盖了景点、美食、路线、启动页和图标。对离线 App 来说本地资源管理就是产品体验的一部分。图片资源如何引用在数据层图片通过Resource引用image: $r(app.media.img_sayram)模型中对应字段是image: Resource;这让数据和 UI 之间的关系非常直接。页面组件不需要拼路径也不需要关心图片文件名只要消费item.image。例如卡片里Image(item.image) .width(100%) .height(150) .objectFit(ImageFit.Cover)详情页头图里this.DetailHero(item.color, item.image)这就是把资源引用纳入数据模型的好处。启动页不要让冷启动像白屏项目自己做了应用内启动页State showLaunchSplash: boolean true;build()里先判断if (this.showLaunchSplash) { this.LaunchSplash() } else { this.MainShell() }启动页组件Builder LaunchSplash() { Image($r(app.media.splash_fullscreen)) .width(100%) .height(100%) .objectFit(ImageFit.Cover) .backgroundColor(#071D32) .onAppear(() { setTimeout(() { this.showLaunchSplash false; }, 1800); }) }这里用一张全屏图片做过渡1.8 秒后进入主页面。对内容型 App 来说启动页有两个作用让品牌和场景先出现。避免图片资源加载时的突兀空白。不过要注意应用内启动页不宜过长。用户不是来看广告的1-2 秒足够。系统启动窗口配置除了应用内启动页module.json5里还有系统启动窗口配置startWindowIcon: $media:startIcon, startWindowBackground: $color:start_window_background颜色定义在{ color: [ { name: start_window_background, value: #071D32 } ] }这里的背景色和应用内启动页保持一致。这样从系统启动窗口到 App 首屏视觉不会跳变。图片裁切统一用 Cover项目中大部分图片展示都使用.objectFit(ImageFit.Cover)它的作用是让图片填满容器超出部分裁切。旅行素材的比例经常不同景点横图美食近景城市街区图路线封面图如果直接等比完整显示卡片高度会不稳定如果强行拉伸图片会变形。Cover是列表卡片和头图里最稳的选择。用主题色兜底图片观感许多卡片先放主题色再叠加图片Column() .width(100%) .height(150) .backgroundColor(item.color) Image(item.image) .width(100%) .height(150) .objectFit(ImageFit.Cover) .opacity(0.55)这样做有三个好处图片未加载前不会是纯白空块。不同图片在透明度叠加后更统一。文字覆盖在图片上时可读性更好。尤其是本地素材来源不完全一致时这种“主题色 半透明图片 渐变”的组合能快速提升整体一致性。渐变层保证文字可读FeatureCard里有渐变遮罩Column() .width(100%) .height(150) .linearGradient({ angle: 180, colors: [[#00000011, 0.0], [#000000CC, 1.0]] })很多图片上半部分亮、下半部分暗或者主体颜色复杂。如果直接放文字白字可能在雪山、湖面、天空上消失。渐变层的作用不是装饰而是可读性保障。详情页头图可以更“沉浸”详情页头图组件接收颜色和图片Builder DetailHero(color: string, image: Resource) { Stack() { Column() .width(100%) .height(244) .backgroundColor(color) Image(image) .width(100%) .height(244) .objectFit(ImageFit.Cover) .opacity(0.82) } .height(244) }头图比列表图更高因为详情页的目的不是快速扫读而是让用户进入一个目的地内容。旅行类详情页可以大胆使用更高的头图但要注意两个问题内容面板要向上衔接避免上下割裂。顶部返回按钮要保证在图片上可见。图片命名要有规则项目里的图片基本遵循img_地点或内容.jpg例如img_kanas.jpg img_sayram.jpg img_kashgar_oldtown.jpg img_dapanji.jpg img_laghman.jpg命名规则看似小事但资源一多就很重要。建议遵守全小写。单词用下划线。景点用英文或拼音稳定命名。不用1.jpg、new.jpg、test.jpg。启动页、图标单独命名。后续维护内容库时资源名能直接对应数据 id效率会高很多。离线图片的包体权衡离线 App 的图片都随包发布因此要关注包体积。这个项目的素材单张图片大多在几十 KB 到几百 KB 之间适合 MVP。后续如果内容扩展到数百张图片需要考虑压缩 JPG 质量。控制图片尺寸不要直接放超大原图。区分缩略图和详情图。对路线、城市复用同一张封面。后续版本考虑可选离线包下载。图片越多越要避免“每个页面都上一张 2MB 原图”的做法。场景推荐资源策略原因首页卡片中等尺寸 JPG列表多图优先控制加载压力详情头图较高清封面用户停留时间长视觉收益高启动页单张压缩图避免冷启动阶段等待过久商店截图单独导出 PNG/JPEG不和应用运行资源混在一起离线 App 的图片策略要同时考虑视觉和包体。素材不是越大越好而是要匹配显示位置。验证清单检查项操作方式通过标准所有 Markdown 配图都是本地 PNG对照项目文件或真机/模拟器操作结果符合文章描述App 图片资源都能通过 $r 引用对照项目文件或真机/模拟器操作结果符合文章描述启动背景色和启动页主色一致对照项目文件或真机/模拟器操作结果符合文章描述卡片图片使用固定容器和 Cover对照项目文件或真机/模拟器操作结果符合文章描述图片上文字有遮罩保证可读对照项目文件或真机/模拟器操作结果符合文章描述常见问题和处理问题现象优先排查处理方式启动瞬间白屏明显检查 startWindowBackground应用内启动页背景色要一致图片被拉伸检查是否使用 Cover不要只给宽度不给高度包体积增长快检查图片尺寸和质量缩略图与详情图可分层管理本篇小结资源与图片体验的关键点图片资源通过$r(app.media.xxx)引用。数据模型直接保存Resource。系统启动窗口和应用内启动页保持色彩一致。图片展示统一使用ImageFit.Cover。主题色和渐变层用于提高视觉一致性和文字可读性。图片命名要规则化为后续扩展做准备。下一篇我们看适配和质量如何处理长文案、空状态、固定栏遮挡、平板设备和可发布前的视觉稳定性。