HarmonyOS 校园应用系列之鸿蒙原生实战:用 ArkUI 搭自习室首页 —— 可交互柱状图与横向分类栏

应用背景:09 自习室预约(study-lounge)是鸿蒙原生校园工具系列第 9 个应用。本文逐行拆解 HomeTab.ets(193 行),聚焦 HeroCard 渐变统计卡可交互式 WeekChartCard横向滚动 CategoryBar 三大核心组件。青绿色主题 #14B8A6,视觉清新学术。

应用效果图

一、整体架构概览

自习室首页延续经典 "Hero Card → 快捷入口 → 数据图表 → 分类筛选 → 列表" 架构,三处差异:

  1. 青绿色主题#14B8A6(teal),视觉更清新学术
  2. HeroCard 统计项改为三列:128 总数/32 今日/86% 完成率
  3. WeekChart 默认高亮周五selectedBar 初始值 4)

页面根布局为 Scroll 包裹 Column.scrollBar(BarState.Off) 隐藏滚动条。

项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub

配图

二、HeroCard —— 渐变头部统计卡

2.1 结构分层

Column({ space: 12 }) {
  // Row: 标题行(图标+标题+副标题)
  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: 统计数据行(三列)
  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 })
.linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
.borderRadius({ bottomLeft: D.rLg, bottomRight: D.rLg })

外层 Column 设 .linearGradient(...) 从青绿 #14B8A6(C.primary)过渡到亮青 #2DD4BF(C.accent)。圆角仅设底部——顶部顶到屏幕顶端与状态栏衔接,底部圆角与内容区形成卡片边界。

2.2 内联分隔线统计

HeroCard 的核心亮点是 三项统计数据之间用半透明白色竖线分隔

if (idx < this.stats.length - 1) {
  Column().width(1).height(28).backgroundColor('rgba(255,255,255,0.25)')
}

通过 if (idx < length-1) 在相间插入 1vp 宽、28vp 高的半透明竖向 Column。这种 "内联分隔线" 让统计像仪表盘刻度分区。

2.3 数据驱动渲染

stats 数组定义三项指标:

字段含义
value'128' / '32' / '86%'显示值
label总数 / 今日 / 完成率标签

ForEach 遍历生成三列,每列内部纵向排列 value/label。每列 .layoutWeight(1) 等分宽度。

2.4 渐变配置

.linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] }) 从青绿 #14B8A6 过渡到亮青 #2DD4BF。135 度对角线方向,符合视觉动线习惯。

三、QuickActionRow —— Emoji 四宫格

四宫格用 Row + ForEach + layoutWeight(1) 实现(非 Grid):

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)

四个入口为 搜索/收藏/统计/消息(🔍⭐📊🔔)。本页用 Emoji 字符 作图标:零资源依赖,代价是无法控制 fillColor。

首页 - 列表底部

四、WeekChartCard —— 可交互式渐变柱状图

这是本页技术含量最高的组件——柱体点击选中后切换渐变色

4.1 柱体动态样式

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)

选中态由 selectedBar 决定:三元切换渐变与字色,默认 4(周五)高亮。键值生成器用 b.day 而非索引,保证数据变化时正确复用。

4.2 高度映射公式

Math.max(6, b.value/7*80):原值(07)映射 080vp。

4.3 底部标签联动

Text(b.day).fontSize(10)
  .fontColor(idx === this.selectedBar ? C.primary : C.textDim)
  .fontWeight(idx === this.selectedBar ? FontWeight.Bold : FontWeight.Normal)

标签同步响应——选中态主色加粗,未选中次要色常规。柱体与标签同在 Column({ space: 6 }) 内纵向排布。

4.4 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(...) { /* 柱体+标签 */ }
  }.width('100%')
}
.width('100%').padding(14).backgroundColor(C.card).borderRadius(D.rMd)
.border({ width: 1, color: C.stroke })

标题行是一个 Text('📊 本周数据',Bold 字重)加右侧硬编码的汇总数值 '30 次'(无 totalWeek 变量)。柱图区 Row({ space: 4 }),每根柱 .layoutWeight(1) 等分宽度。

首页 - 推荐列表完整视图

五、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%')

注意 scrollable 是链式属性而非构造参数。外层 Scroll 水平滚动;选中态"反色+描边"双重标记——border 始终 width: 1,仅颜色在 C.primary/C.stroke 间切换。四个分类:全部 / 推荐 / 热门 / 最新。

首页 - 柱状图与分类栏

六、ItemList —— 推荐列表

列表项包含三层信息:

  1. 首行:图标 + 标题 + StatusTag(HOT 红/NEW 绿/精选 青)
  2. 次行:描述文字 + 参与人数
  3. 进度条:Stack 轨道+填充,自动对齐左边缘

StatusTag 条件着色:item.tag === 'HOT' ? C.danger : item.tag === 'NEW' ? C.ok : C.accent(红/绿/青对应热门/新上线/精选)。

进度条用 Stack 实现——轨道+填充,自动对齐左边缘。

首页 - HeroCard 与快捷入口

七、Theme.ets 双类设计令牌

Theme.ets 用 D 类 集中管理尺寸令牌:

令牌用途
D.rLg20大圆角(HeroCard 底部圆角、头像)
D.rMd14中圆角(卡片、列表项)
D.rSm10小圆角(图标底、胶囊、标签)
D.pad16页面水平边距

安全区偏移不走 D 类——safeTop/safeBottom 由 EntryAbility 写入 AppStorage,各页用 @StorageProp 读取。

颜色/尺寸双类分离 是企业级标准,换肤改 C 类,调密度改 D 类,互不干扰。

八、总结

自习室首页的三大技术要点:

  1. HeroCard 内联分隔线if (idx < length-1) + 1vp 宽半透明 Column 实现仪表盘式统计区
  2. 可交互式柱状图@State selectedBar 驱动渐变/字色/字重三重联动,默认选中周五(idx 4)
  3. CategoryBar 横向 Scroll:选中态反色 + 自动隐藏滚动条

可交互式柱状图 最具学习价值——@State 与视觉效果的深度绑定。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐