news 2026/7/5 8:36:14

ArkUI 视觉资源管理:ShowcaseCard、CardImages 与样式页图片映射

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ArkUI 视觉资源管理:ShowcaseCard、CardImages 与样式页图片映射

ArkUI 视觉资源管理:ShowcaseCard、CardImages 与样式页图片映射

HarmonyOS 应用做卡片类界面时,视觉资源很容易失控:有些页面用纯色块,有些页面用本地图片,有些页面直接写资源名,最后新增模板或样式时到处改。这个项目把视觉资源拆成两层:组件层ShowcaseCard负责渲染,资源层CardImages.ets负责 key 到 media 的映射。

为什么不用页面直接引用图片

页面直接写:

Image($r('app.media.card_template_birthday'))

短期最快,但问题很快出现:

  • 同一模板在首页、分类、详情页都要重复引用。
  • 新增模板要改多个页面。
  • 有些场景需要按分类 fallback。
  • 样式页顶部 banner 和网格 tile 比例不同。

项目改成让视图模型携带imageKey

export interface ShowcaseCardModel { id: string; title: string; subtitle: string; tone: ToneName; imageKey?: string; }

页面只传模型,组件自己解析图片。

CardImages:集中管理资源映射

CardImages.ets里定义 key:

export class CardImageKeys { static readonly heroDark: string = 'hero-dark'; static readonly marketLight: string = 'market-light'; static readonly categoryCountdown: string = 'category-countdown'; static readonly templateBirthday: string = 'template-birthday'; static readonly styleNightBanner: string = 'style-night-banner'; static readonly styleNightTile: string = 'style-night-tile'; }

再通过cardImageResource()转成真正的 media 资源:

export function cardImageResource(imageKey?: string): Resource { switch (imageKey) { case CardImageKeys.categoryCountdown: return $r('app.media.card_category_countdown'); case 'template-birthday': return $r('app.media.card_template_birthday'); case CardImageKeys.styleNightBanner: return $r('app.media.card_style_night_banner'); default: return $r('app.media.market_banner_light'); } }

页面和服务层都不直接依赖$r('app.media.xxx')

模板图片:templateId 到 imageKey

模板目录只保存templateIdcategoryId。图片 key 由 helper 生成:

export function imageKeyForTemplate(templateId: string, categoryId: CardCategoryId): string { switch (templateId) { case 'birthday': return 'template-birthday'; case 'exam-countdown': return 'template-exam-countdown'; default: return imageKeyForCategory(categoryId); } }

如果模板图片缺失,就回退到分类图。这个 fallback 能兜底,但新增模板时仍应该补齐专属图。

样式页:同一个 styleId 要区分 banner 和 tile

样式页比较特殊:顶部预览卡需要横向 banner,下方样式库需要 tile。项目用preview参数区分:

export function imageKeyForStyle(styleId: string, preview: boolean = false): string { switch (styleId) { case 'style-night': return preview ? CardImageKeys.styleNightBanner : CardImageKeys.styleNightTile; case 'style-rose': return preview ? CardImageKeys.styleRoseBanner : CardImageKeys.styleRoseTile; default: return preview ? CardImageKeys.styleRoseBanner : CardImageKeys.styleRoseTile; } }

这个设计避免“顶部换了图,下方还是旧图”的视觉割裂。

ShowcaseCard:一套组件支持多种展示

ShowcaseCard.ets支持普通卡、hero 卡、banner 图片卡和自定义图片高度:

@Component export struct ShowcaseCard { @Prop item: ShowcaseCardModel; compactBadge?: boolean; hero?: boolean; bannerImage?: boolean; imageHeight?: number; onCardClick?: (id: string) => void; }

图片是否展示由 helper 决定:

private showImage(): boolean { return this.hero === true || this.bannerImage === true || this.imageHeight !== undefined; }

图片高度也根据模式变化:

private mediaHeight(): number { if (this.hero === true) { return 86; } if (this.bannerImage === true) { return 128; } return this.imageHeight ? this.imageHeight : 0; }

这样详情页、样式页、分类页不用各写一套卡片组件。

紧凑徽标布局解决窄卡片文本被挤压

首页和分类页有两列窄卡片。如果沿用左右对称 badge 占位,标题容易被挤压。项目给ShowcaseCard增加compactBadge

private useCompactBadgeLayout(): boolean { return this.compactBadge === true && this.item.badge ? true : false; }

页面使用时:

ShowcaseCard({ item: item, compactBadge: true, onCardClick: () => { this.openShowcaseCard(item); } })

窄卡片优先保证标题和副标题可读,badge 收到右上角。

资源更新时的验证重点

视觉资源变化不一定影响编译,但很容易影响运行效果。项目里形成了几个检查点:

  1. 新增模板时,检查TEMPLATE_CATALOGCardImages.ets是否同步。
  2. 样式页同一styleId同时检查 preview/banner 和 tile。
  3. 图片中的主要文字不要烧录在边缘,避免ImageFit.Cover裁切。
  4. 小卡片使用imageHeight或固定比例,避免布局跳动。
  5. 分类图和模板图都要在真机或模拟器截图中检查。

常见坑

  • 只补 media 文件,不补cardImageResource()映射。
  • 只给样式页顶部预览换图,忘了下方 tile。
  • 模板 ID 改了,但图片 key 仍然旧值。
  • 详情页使用模板图时没有 fallback 到分类图。
  • 图片高度靠内容撑开,导致不同卡片高度不一致。

基础链路小结

这个项目的视觉资源管理思路是:页面传imageKey,组件统一渲染,资源层集中映射。模板图片、分类图片、样式图片都走同一套 key 体系。

对 ArkUI 卡片类应用来说,这种方式能让资源替换、模板扩展和页面复用都更可控。尤其是样式页这类同时存在 banner 和 tile 的场景,一定要把同一个业务 ID 的多套图片映射维护清楚。

图片资源章节要讲“资源 key、业务 id、兜底图”三件事

图片章节如果只讲“把 PNG 放进 media 目录”,就达不到第四章标准。Project028 的关键是CardImages.ets把业务 id、样式 id、图片 key 和资源对象隔离开了。页面拿到的通常是imageKey,最终通过cardImageResource()解析成$r('app.media.xxx')。这种间接层可以让页面不关心资源文件名,也能处理缺图兜底。

主题、样式、详情头图、市场头图都走同一套资源入口。比如桌面 Form 里保存的是themeImageKey,详情页里根据卡片类型解析头图,市场页摘要卡没有图片时回退marketLight。这些都说明图片资源不能散落在各页面,否则后续替换图片或生成新封面会非常难查。

Image(cardImageResource(this.themeImageKey)) .width('100%') .height('100%') Image(cardImageResource(this.heroImageKey())) .width('100%') .height('100%')

真实项目里还要考虑审核截图。Project028 后续为了 AGC 宣传图、应用图标、桌面卡片预览生成了多批图片资产。这里需要把边界讲清楚:CSDN 展示图、AGC 宣传图、应用内资源图不要混用。CSDN 图偏讲解,AGC 图偏审核和商店展示,应用内图要控制尺寸、命名和资源引用稳定性。

落地检查清单

  • 是否说明图片资源不要从页面硬编码$r()
  • 是否解释imageKey和业务 id 的区别。
  • 是否覆盖缺图兜底,避免空白区域。
  • 是否提示 AGC 宣传图和应用内资源图的用途不同。
  • 是否覆盖真实路径:CardImages.etsThemeStorePage.etsCardStylePage.etsDesktopCardForm.ets
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 8:35:36

Three.js 粒子烟花教程

粒子烟花 Fire ▶ 在线运行案例 案例合集: 三维可视化功能案例(threehub.cn)开源仓库github地址: https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 ShaderMaterial 自定义着色器…

作者头像 李华
网站建设 2026/7/5 8:34:27

想了解乡墅赋能培训?这家超靠谱的乡墅赋能培训机构推荐给你!

在乡村振兴的大背景下,乡墅市场迎来了前所未有的发展机遇。然而,许多从业者在实际运营中面临着诸多挑战,乡墅赋能培训应运而生。湖北乡墅研究中心便是一家在该领域表现出色的机构。乡墅行业现状与痛点行业报告显示,当前乡墅行业发…

作者头像 李华
网站建设 2026/7/5 8:34:14

Python A2A实战:为AI Agent创建专属邮箱与多智能体协作网络

1. 先搞清楚 A2A 和 AI Agent 邮箱到底要解决什么问题如果你最近在关注 AI Agent 开发,大概率会看到“A2A时代”和“为 AI Agent 申请专属邮箱”这类说法。这背后其实是一个很实际的问题:当多个 AI Agent 需要像人一样协作时,它们之间如何可靠…

作者头像 李华
网站建设 2026/7/5 8:33:32

性能测试结果深度解读:从指标分析到瓶颈定位实战指南

1. 性能测试结果解读:从数据迷雾到系统真相刚做完一轮性能压测,看着JMeter或LoadRunner生成的那一堆花花绿绿的图表和密密麻麻的数字,是不是感觉头都大了?响应时间、TPS、错误率、CPU使用率……每个指标好像都在说话,但…

作者头像 李华
网站建设 2026/7/5 8:31:29

静态文件服务器XSS攻击:文件上传场景下的安全盲区与防御实践

1. 项目概述:一个被忽视的“安全盲区”“静态文件服务器”和“XSS攻击”,这两个词放在一起,很多开发者第一反应可能是:“这俩有关系吗?” 在很多人的认知里,静态文件服务器,比如Nginx、Apache直…

作者头像 李华