HarmonyOS 3D 拆解分析:发现卡片的个性化改造

📅 2026/7/9 3:44:18
HarmonyOS 3D 拆解分析:发现卡片的个性化改造
前言上一版的发现页有一个问题所有卡片都用同一个图标。不管是 iPhone 拆解还是无人机电路分析左边的徽章都是一个扳手。用户扫一眼列表很难快速区分这是什么东西的拆解。这次改造的核心是让每个项目有自己的图标——iPhone 用MacBook 用AirPods 用。用户不需要读标题看图标就知道是什么设备。DiscoverItem 的字段升级旧版interfaceDiscoverItem{title:stringdesc:stringlikes:string// 128color:string}新版interfaceDiscoverItem{title:stringdesc:stringicon:string// ⌨️likes:string// 128不再带 emojicolor:string}两个变化新增icon字段设备专属的 emojilikes简化去掉了前缀只保留数字字符串为什么去掉 likes 的 emoji 前缀因为卡片上已经加了一个心形图标sys.symbol.heart不需要再用 emoji 表示点赞。如果心形图标 128同时出现信息重复。Mock 数据每个设备一个图标privategetMockDiscover():DiscoverItem[]{return[{title:iPhone 15 拆解,desc:...,icon:,likes:128,color:#FF6B6B},{title:MacBook 主板分析,desc:...,icon:,likes:96,color:#5DADE2},{title:AirPods 内部结构,desc:...,icon:,likes:203,color:#00B894},{title:Switch 手柄拆解,desc:...,icon:,likes:74,color:#FECA57},{title:机械键盘轴体,desc:...,icon:⌨️,likes:51,color:A},{title:无人机电路分析,desc:...,icon:,likes:37,color:#6C5CE7}]}六个设备六个不同的 emoji设备Emoji为什么选这个iPhone手机的通用符号MacBook笔记本电脑AirPods耳机Switch游戏手柄键盘⌨️键盘本身无人机直升机/飞行器这些 emoji 都是 Unicode 标准的一部分在所有设备上都能正常显示。DiscoverCard 的重构旧版传四个参数BuilderDiscoverCard(title:string,desc:string,likes:string,color:string){Row(){Column(){}.width(44).height(44).borderRadius(12).backgroundColor(color18)Stack(){Text().fontSize(18)// 所有卡片都是}// ...}}新版传整个对象BuilderDiscoverCard(item:DiscoverItem){Row(){// 设备图标徽章Column(){Text(item.icon).fontSize(24)}.width(48).height(48).borderRadius(14).backgroundColor(item.color15).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)// 标题和描述Column(){Text(item.title).fontSize(15).fontWeight(FontWeight.Bold).fontColor(T1)Text(item.desc).fontSize(11).fontColor(T2).margin({top:2})}// 点赞和箭头Column(){Row({space:3}){SymbolGlyph($r(sys.symbol.heart)).fontSize(11).fontColor([item.color])Text(item.likes).fontSize(12).fontWeight(FontWeight.Medium).fontColor(item.color)}SymbolGlyph($r(sys.symbol.chevron_right)).fontSize(12).fontColor([#DDD]).margin({top:6})}}.width(100%).padding(14).backgroundColor(CW).borderRadius(16).border({width:1,color:#F0EEF6}).shadow({radius:4,color:rgba(0,0,0,0.02),offsetY:1})}为什么改成传对象可扩展性后续加字段比如viewCount、author不需要改 Builder 的参数列表类型安全传对象时 TypeScript 会检查字段名是否正确传参数时不会代码可读性item.icon比icon参数更明确地表达了这是什么图标徽章的变化// 旧版.width(44).height(44).borderRadius(12)Text().fontSize(18)// 新版.width(48).height(48).borderRadius(14)Text(item.icon).fontSize(24)三个变化尺寸从 44×44 增大到 48×48——图标是卡片的视觉焦点大一点更醒目圆角从 12 增大到 14——和卡片的圆角16更协调字号从 18 增大到 24——emoji 在更大的容器里需要更大的字号才能居中背景色透明度微调// 旧版.backgroundColor(color18)// 约 9% 透明度// 新版.backgroundColor(item.color15)// 约 8% 透明度从1824/255 ≈ 9.4%降到1521/255 ≈ 8.2%。因为图标从变成了彩色 emoji——emoji 本身已经有颜色背景色再深就会和图标抢色。点赞区域心形图标Row({space:3}){SymbolGlyph($r(sys.symbol.heart)).fontSize(11).fontColor([item.color])Text(item.likes).fontSize(12).fontWeight(FontWeight.Medium).fontColor(item.color)}用系统心形图标sys.symbol.heart 数字的组合替代了旧版的 128。好处心形图标和卡片主题色一致视觉更协调数字更紧凑不会因为 emoji 宽度导致对齐问题sys.symbol.heart是矢量图标在不同分辨率下都清晰卡片阴影// 旧版无阴影// 新版.shadow({radius:4,color:rgba(0,0,0,0.02),offsetY:1})极淡的阴影2% 透明度给卡片一个微微浮起的感觉。在紫色背景上白色卡片 极淡阴影 层次感。调用方式的变化旧版ForEach(this.discoverItems,(item:DiscoverItem){this.DiscoverCard(item.title,item.desc,item.likes,item.color)})新版ForEach(this.discoverItems,(item:DiscoverItem){this.DiscoverCard(item)})从传四个参数变成传一个对象。调用代码更简洁Builder 的参数列表也更短。视觉效果对比旧版卡片┌─────────────────────────────┐ │ ┌────┐ │ │ │ │ iPhone 15 拆解 │ │ └────┘ 专业维修指南... │ │ 128 │ │ │ └─────────────────────────────┘所有卡片左边都是需要读标题才能区分。新版卡片┌─────────────────────────────┐ │ ┌────┐ │ │ │ │ iPhone 15 拆解 │ │ └────┘ 专业维修指南... │ │ ♡ 128 │ │ │ └─────────────────────────────┘ ┌─────────────────────────────┐ │ ┌────┐ │ │ │ │ MacBook 主板分析 │ │ └────┘ 芯片级拆解... │ │ ♡ 96 │ │ │ └─────────────────────────────┘每个卡片左边是不同的设备图标扫一眼就能区分。踩坑记录1. emoji 的宽度不一致不同 emoji 的显示宽度不同—— 比 宽⌨️ 比 窗。在 48×48 的容器里有些 emoji 会显得挤有些会显得空。解决方案用fontSize(24)统一字号让 emoji 自然居中。如果某个 emoji 显示效果不好可以微调fontSize。2. sys.symbol.heart 的颜色SymbolGlyph($r(sys.symbol.heart)).fontSize(11).fontColor([item.color])心形图标的颜色跟随卡片主题色。但如果主题色是黄色#FECA57心形图标在白色背景上对比度不够。这是多色系统的一个已知限制——亮色系的主题色在小图标上可读性较差。3. ForEach 的 keyGenerator当前的ForEach没有指定keyGenerator。ArkUI 会用默认的索引作为 key。如果列表顺序变化比如排序、插入可能导致动画异常。建议后续加上ForEach(this.discoverItems,(item:DiscoverItem){this.DiscoverCard(item)},(item:DiscoverItem)item.title)// 用标题作为 key4. 异步数据和 mock 数据的切换ForEach(this.discoverItems.length0?this.discoverItems:this.getMockDiscover(),(item:DiscoverItem){this.DiscoverCard(item)})getMockDiscover()在ForEach的三元表达式中被调用——每次渲染都会创建新数组。如果discoverItems为空比如 API 还没返回每次渲染都会重新创建 6 个 mock 对象。这个问题在上一篇文章中已经提过这次仍然存在。建议在aboutToAppear中预计算 mock 数据。写在最后发现卡片的个性化改造解决了一个核心问题信息辨识度。旧版的图标让用户需要读文字才能区分项目新版的设备 emoji 让用户扫一眼就知道这是什么。图标的选择遵循一个原则用最常见的符号。代表手机、代表电脑、代表耳机——这些符号已经被用户广泛认知不需要学习成本。后续如果接入真实 API后端需要为每个拆解项目提供icon字段。可以是预定义的 emoji 列表也可以是自定义图标需要处理跨平台兼容性。