ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

鸿蒙原生应用实战:用 ArkUI 搭志愿服务首页 —— 沉浸式 HeroCard 与项目推荐

鸿蒙原生应用实战:用 ArkUI 搭志愿服务首页 —— 沉浸式 HeroCard 与项目推荐 鸿蒙原生应用实战用 ArkUI 搭志愿服务首页 —— 沉浸式 HeroCard 与项目推荐App 17「志愿服务」首页HomeTab主题色#EF4444红色4 个 Tab 分别为首页、活动、记录、我的。首页采用沉浸式渐变 HeroCard智能管理 3 栏统计 快捷功能入口 本周数据可点击条形图 横向分类筛选 推荐项目列表含进度条与 HOT/NEW/精选 标签。本篇基于17-volunteer/entry/src/main/ets/pages/HomeTab.ets约 193 行逐段拆解附 4 张实机截图。一、整体结构5 个 Builder 的内容流HomeTab 的结构与 App 13/14/15/16 高度同源——都是 5 个 Builder 块拼出信息流首页build() { Column() { Scroll() { Column({ space: 14 }) { this.HeroCard() this.QuickActionRow() this.WeekChartCard() this.CategoryBar() this.ItemList() Blank().height(this.safeBottom 20) } .width(100%).padding({ left: D.pad, right: D.pad }) } .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top) } .width(100%).height(100%).backgroundColor(C.bg) }5 个块按头部 → 工具 → 数据 → 筛选 → 列表五段叙事HeroCard— 红色渐变欢迎 3 栏统计QuickActionRow— 4 个圆形入口搜索/收藏/统计/消息WeekChartCard— 本周数据可点击条形图CategoryBar— 横向分类筛选全部/推荐/热门/最新ItemList— 3 条推荐项目含进度条与标签沉浸式顶部设计HeroCard 的padding({ top: this.safeTop 20 })让红色渐变延伸到状态栏背后形成顶部品牌色块——这是本系列一直沿用的沉浸式模式。Blank().height(this.safeBottom 20)底部留白让最后一条卡片不贴到 Tab 栏加 20vp 视觉缓冲。项目源码开源https://gitee.com/codenestFlow/HarmonyOSHub二、HeroCard红色渐变 智能管理 3 栏统计HeroCard 是顶部品牌区——红色C.primary渐变到浅红C.accent形成热情公益的视觉调性Builder HeroCard() { Column({ space: 12 }) { Row({ space: 12 }) { Text().fontSize(24) Column({ space: 4 }) { Text(智能管理).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#FFFFFF) Text(高效便捷 · 智能推荐).fontSize(12).fontColor(rgba(255,255,255,0.85)) }.alignItems(HorizontalAlign.Start).layoutWeight(1) }.width(100%) Row() { ForEach(this.stats, (s: StatItem, idx: number) { Column({ space: 2 }) { Text(s.value).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#FFFFFF) Text(s.label).fontSize(10).fontColor(rgba(255,255,255,0.8)) }.alignItems(HorizontalAlign.Start).layoutWeight(1) if (idx this.stats.length - 1) { Column().width(1).height(28).backgroundColor(rgba(255,255,255,0.25)) } }, (s: StatItem) s.label) }.width(100%) } .width(100%).padding(18).padding({ top: this.safeTop 20 }) .borderRadius({ bottomLeft: D.rLg, bottomRight: D.rLg }) .linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] }) }两个 padding 写法.width(100%).padding(18).padding({ top: this.safeTop 20 })padding(18).padding({ top: this.safeTop 20 })是 ArkUI 里叠加 padding的写法——先给四面 18vp再单独把顶部覆盖为safeTop 20。这样左右下三边距统一为 18顶部则跟随状态栏高度自适应。比padding({ top: safeTop20, left: 18, right: 18, bottom: 18 })这种四值全写简洁。borderRadius({ bottomLeft: D.rLg, bottomRight: D.rLg })只圆角底部让顶部与状态栏无缝拼接沉浸式底部则形成大圆角与下方白色卡片做软分隔。上不留白、下大圆角是沉浸式 HeroCard 的标准做法。3 栏统计128 / 32 / 86%对应总数 / 今日 / 完成率——量 → 速 → 质三角总数 128——累计活动总量今日 32——当日新增完成率 86%——完成质量rgba(255,255,255,0.25)25% 不透明白色作为分隔竖线在红色背景上柔和不刺眼。if (idx this.stats.length - 1)防止最后一条也加分隔线。HeroCard 与 App 13 同源都是图标 标题 副标题 3 栏统计 135° 渐变——同一套模板换主题色紫→红 换标题文案音乐歌单→智能管理。三、QuickActionRow4 个圆形入口QuickActionRow 是 4 个常用功能搜索/收藏/统计/消息的快捷入口Builder QuickActionRow() { Row() { ForEach(this.actions, (item: QuickAction) { Column({ space: 6 }) { Row() { Text(item.icon).fontSize(22) } .width(44).height(44).backgroundColor(C.cardSoft).borderRadius(D.rSm).justifyContent(FlexAlign.Center) Text(item.title).fontSize(10).fontColor(C.textSub) } .layoutWeight(1) .onClick(() { promptAction.showToast({ message: item.title }); }) }, (item: QuickAction) item.title) } .width(100%).padding(12).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }).justifyContent(FlexAlign.SpaceAround) }44×44 圆角图标块borderRadius(D.rSm)10vp 圆角非正圆 下方 10sp 文字。justifyContent(FlexAlign.SpaceAround)让 4 个入口之间留出等距空白。promptAction.showToast({ message: item.title })是轻反馈——点击立刻弹搜索收藏等文字给用户我收到了点击的反馈。与 App 13 几乎完全同源——只有actions数组的内容略不同App 13 是 4 个 app 内功能App 17 是搜索/收藏/统计/消息。同一套圆形图标文字骨架换数据。四、WeekChartCard可点击的 7 天条形图WeekChartCard 是 7 天数据条形图点击柱子切换高亮与 App 13 相同的可交互周图Builder WeekChartCard() { Column({ space: 12 }) { Row() { Text( 本周数据).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text) Blank() Text(30 次).fontSize(13).fontWeight(FontWeight.Bold).fontColor(C.primary) }.width(100%) Row({ space: 4 }) { ForEach(this.weekBars, (b: WeekBar, idx: number) { Column({ space: 6 }) { Column() .width(18) .height(Math.max(6, b.value / 7 * 80)) .borderRadius(4) .linearGradient({ angle: 180, colors: idx this.selectedBar ? [[C.primary, 0.0], [C.accent, 1.0]] : [[C.primarySoft, 0.0], [C.primary, 1.0]] }) Text(b.day).fontSize(10) .fontColor(idx this.selectedBar ? C.primary : C.textDim) .fontWeight(idx this.selectedBar ? FontWeight.Bold : FontWeight.Normal) }.layoutWeight(1).alignItems(HorizontalAlign.Center) .onClick(() { this.selectedBar idx; }) }, (b: WeekBar) b.day) }.width(100%) } .width(100%).padding(14).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) }柱子高度 b.value / 7 * 80把 value 归一化到 80vp 上限。Math.max(6, ...)兜底最低 6vp 防止柱子消失。选中柱子用主色渐变红→浅红未选用浅红→红逆向——视觉上激活更亮。State selectedBar: number 4初始选中五idx4value4——**周五数据刚好居中**是 demo 的默认值巧思既能演示选中态又不显眼。点击this.selectedBar idx;切换高亮——State一变整张卡的渐变色和文字重渲染。点击即反馈的响应式范式。30 次汇总3526473 30——汇总 各柱之和完全成立App 13 同样是 30 次 3526473 也成立App 15/16 有口径不一致的坑App 17 与 App 13 一样严格自洽。30 次的语义是本周完成 30 次志愿活动——次数是志愿服务的核心量化指标比时长更直观。30 次 每位志愿者每天 4-5 次活动是合理的活跃水平。五、CategoryBar横向分类筛选CategoryBar 是 4 个分类全部/推荐/热门/最新的胶囊标签横滑滚动Builder CategoryBar() { Scroll() { Row({ space: 8 }) { ForEach(this.cats, (cat: string, idx: number) { Text(cat).fontSize(13) .fontColor(this.activeCat idx ? #FFFFFF : C.textSub) .backgroundColor(this.activeCat idx ? C.primary : C.card) .borderRadius(D.rSm).border({ width: 1, color: this.activeCat idx ? C.primary : C.stroke }) .padding({ left: 14, right: 14, top: 8, bottom: 8 }) .onClick(() { this.activeCat idx; }) }, (cat: string) cat) } } .scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width(100%) }选中态三件套背景/文字/边框——同系列标准做法选中主色背景红 白字 主色描边未选白底 灰字 浅灰描边Scroll包裹让 4 个分类可横滑4 个装得下但 Scroll 让未来加分类不会破版。分类条 横滑是移动端分类筛选的标准组件。onClick(() { this.activeCat idx; })切换状态——demo 没有联动 ItemList 过滤与 App 13 一样是假筛选但状态机已就绪真实项目只需加filteredItems()即可联动。六、ItemList3 条推荐项目含标签进度条ItemList 是首页的内容主体——3 条推荐项目每条都是emoji 标题 标签 描述 进度条Builder ItemList() { Column({ space: 10 }) { Row() { Text(推荐列表).fontSize(16).fontWeight(FontWeight.Bold).fontColor(C.text) Blank() Text(查看全部 ›).fontSize(11).fontColor(C.primary) }.width(100%) ForEach(this.items, (item: ListItem) { Column({ space: 10 }) { Row({ space: 12 }) { Row() { Text(item.emoji).fontSize(28) } .width(56).height(56).backgroundColor(C.cardSoft).borderRadius(D.rMd).justifyContent(FlexAlign.Center) Column({ space: 4 }) { Row() { Text(item.title).fontSize(15).fontWeight(FontWeight.Bold).fontColor(C.text).layoutWeight(1) Text(item.tag).fontSize(9).fontColor(#FFFFFF) .backgroundColor(item.tag HOT ? C.danger : item.tag NEW ? C.ok : C.accent) .padding({ left: 5, right: 5, top: 2, bottom: 2 }).borderRadius(D.rSm) }.width(100%) Text(item.desc).fontSize(12).fontColor(C.textDim) }.alignItems(HorizontalAlign.Start).layoutWeight(1) }.width(100%) Row() { Text(进度).fontSize(10).fontColor(C.textDim) Stack({ alignContent: Alignment.Start }) { Column().width(100%).height(5).backgroundColor(C.cardSoft).borderRadius(3) Column().width(${item.progress}%).height(5).borderRadius(3).backgroundColor(C.primary) }.layoutWeight(1).margin({ left: 8, right: 8 }) Text(${item.progress}%).fontSize(10).fontColor(C.primary).fontWeight(FontWeight.Bold) }.width(100%) } .width(100%).padding(12).backgroundColor(C.card).borderRadius(D.rMd) .border({ width: 1, color: C.stroke }) .onClick(() { promptAction.showToast({ message: item.title }); }) }, (item: ListItem) item.id.toString()) }.width(100%) }6.1 三种标签的语义色彩item.tag HOT ? C.danger : item.tag NEW ? C.ok : C.accent是关键三元表达式HOT→C.danger深红#FF5A6E——最显眼的热门标签NEW→C.ok绿色#2BB673——清新新标签精选→C.accent浅红#F87171——品牌色精选标签3 色映射对应 3 种语义——HOT是紧迫感红色警报、NEW是新鲜感绿色生命、精选是品牌质感主题浅色。**标签色 标签语义**是良好的视觉设计。3 条项目 热门项目一 HOT已参与 128 人进度 75%/ 推荐项目二 NEW86 人45%/ ⭐ 精选项目三 精选256 人90%——3 种标签都有数据展示。6.2 Stack 进度条双通道Row() { Text(进度).fontSize(10).fontColor(C.textDim) Stack({ alignContent: Alignment.Start }) { Column().width(100%).height(5).backgroundColor(C.cardSoft).borderRadius(3) Column().width(${item.progress}%).height(5).borderRadius(3).backgroundColor(C.primary) }.layoutWeight(1).margin({ left: 8, right: 8 }) Text(${item.progress}%).fontSize(10).fontColor(C.primary).fontWeight(FontWeight.Bold) }Stack 进度条 背景条 前景条叠加——Stack({ alignContent: Alignment.Start })让前景对齐到左边起点背景 100% 宽、前景${item.progress}%宽。width用模板字符串拼接百分比item.progress既是宽度数字又是右侧文字一处数据两处联动。进度标签 进度条 百分比文字——三处同步表达完成度进度 文字10sp 灰进度条5vp 高主色圆角75% 数字10sp 主色加粗色弱用户不靠颜色就能读靠进度和75%两处文字——颜色 文字双通道原则。项目 128/86/256 人参与的数字故事项目三精选 256 人 90% 完成度参与最多且进度最远——**参与人数与完成进度正相关**符合大众心理好项目越多人参与、参与越多越容易完成。三个数据互相印证首页数据自洽。七、内容流首页的节奏感设计App 17 首页 5 块内容从上到下有明确的节奏感——第 1 块 HeroCard红色渐变视觉冲击最强红色 135° 渐变承担品牌印象。用户在首页第 1 秒看到这块——决定这个 App 是做什么的。第 2 块 QuickActionRow4 入口中度视觉白色卡片 圆角图标承担工具导航。节奏从冲击过渡到实用。第 3 块 WeekChartCard条形图中度视觉白色卡片 数据图承担行为数据。节奏从导航过渡到数据。第 4 块 CategoryBar横向分类轻度视觉小胶囊标签承担分类筛选。节奏从数据过渡到选择——视觉重量递减给用户选择更多的心理暗示。第 5 块 ItemList推荐项目中重视觉白卡 大色块 进度条 标签承担主内容。节奏从选择过渡到消费——这是用户最终要消费的内容。节奏感 视觉重量的强 → 中 → 中 → 轻 → 中的起伏——不是统一重量从头到尾会显得平铺直叙也不是忽强忽弱会显得杂乱。先冲击后实用是设计节奏的黄金法则。对比 App 14 首页App 14HeroCard活动→ QuickActionRow4 运动→ WeeklyChart数据→ SectionTitle VenueList5 场地→ SectionTitle RankList3 排行App 17HeroCard活动→ QuickActionRow4 入口→ WeekChartCard数据→ CategoryBar分类→ ItemList3 项目两者结构完全同源——5 块节奏是系列化首页的通用骨架。八、State 的克制 主题色的力量HomeTab 只有2 个 StateactiveCat分类选中selectedBar柱子高亮。其他数据全部private不可变——会变的只有交互态是 ArkUI 健康状态管理的铁律。主题色#EF4444红色的语义红色在中国文化里热情、公益、爱心是志愿服务的经典色红十字会、志愿者红马甲。相比 App 14 绿运动活力和 App 15 绿环保App 17 的红更情感——颜色即产品调性。与 App 13 紫色对比App 13 紫 #8B5CF6音乐、浪漫、二次元App 17 红 #EF4444志愿、热情、公益——同一套代码框架换主题色 换文案 完全不同的产品。这是系列化 demo 最重要的方法论模板复用、主题演化。九、总结App 17 首页解析完毕。沉浸式红色 HeroCard 可点击周图 横向分类 标签进度条项目卡是四大亮点配合红色志愿服务调性。与 App 13 的主题复刻展示了一个重要方法论同一套 UI 框架换主题色 换文案 换数据 完全不同的产品。
返回列表