一、应用概述

喝水提醒(Water Reminder) 是一款健康辅助类应用,帮助用户记录每日饮水量,并以可视化水杯的形式直观展示饮水进度。用户每点击一次「喝一杯」按钮,水杯中的水位就会上升一格,同时显示累计饮水量(毫升)和鼓励性文字提示。

从健康角度看,每日饮用 8 杯水(约 2000ml)是广为流传的健康建议。该应用以此为目标,通过视觉反馈正向鼓励两个心理机制,激励用户达成每日饮水目标。从技术角度看,这是一个展示 Stack 层叠中动态高度变化条件文字渲染响应式布局的绝佳案例。

二、技术架构与组件树

2.1 整体架构

@Entry @Component struct index33
├── Column (根容器)
│   ├── Row (顶部导航栏)
│   └── Column (主内容区, 居中)
│       ├── Text ("目标: 8 杯")
│       ├── Stack (水杯可视化)
│       │   ├── Column (水杯容器, 180×200, 圆角边框)
│       │   │   └── Column (水位填充, 动态高度)
│       │   └── Column (水杯文字叠加层)
│       │       ├── Text ("🥤")
│       │       └── Text ("{count}/{target}")
│       ├── Text ("{totalMl}ml / {target*glassSize}ml")
│       ├── Text (鼓励语, 条件变化)
│       ├── Button ("🥤 喝一杯")
│       └── Row (辅助按钮)
│           ├── Button ("重置")
│           └── Button ("目标+")

2.2 状态变量设计

@State count: number = 0;       // 已饮杯数
@State target: number = 8;      // 日目标杯数
@State glassSize: number = 200; // 每杯毫升数

三个 @State 变量构成了应用的全部状态。其中 glassSize 被标记为 @State,虽然当前未通过 UI 修改它,但保留为状态变量意味着未来可以轻松添加用户自定义杯量功能。

三、核心代码深度解析

3.1 喝水逻辑与边界保护

addWater() {
  if (this.count < this.target) {
    this.count++;
  }
}

addWater() 方法包含明确的边界检查:只有当前杯数小于目标杯数时才递增。这防止了用户过度点击导致进度超过 100% 的异常情况。当 count === target 时,点击按钮不再产生任何效果——这是一种"软锁止"设计,比禁用按钮更温和,用户仍然可以感受到点击反馈。

3.2 计算属性:进度、水量与鼓励语

get progress(): number {
  return (this.count / this.target) * 100;
}

get totalMl(): number {
  return this.count * this.glassSize;
}

get encouragement(): string {
  if (this.count >= this.target) return '🎉 达标了!很棒!';
  if (this.count >= this.target * 0.75) return '💪 快达标了!';
  if (this.count >= this.target * 0.5) return '👍 继续加油!';
  if (this.count >= this.target * 0.25) return '💧 多喝点水';
  return '🚰 记得喝水';
}

三个计算属性将原始数据(counttarget)转化为用户友好的显示信息:

  • progress:以百分比形式返回进度值,可用于替代传统的环形进度条
  • totalMl:根据杯数和杯量计算总毫升数
  • encouragement:基于不同进度区间返回不同的鼓励文字,配合对应的 emoji 表情

鼓励语的区间设计合理,在 0%、25%、50%、75%、100% 五个关键节点切换文字,避免了过于频繁的变化,也让用户在每个进步阶段都能获得正向反馈。

3.3 水杯可视化——Stack 层叠的核心

Stack() {
  // 水杯背景和动态水位
  Column() {
    Text('').width(160)
      .height(this.count > 0 ? 160 * (this.count / this.target) : 0)
      .backgroundColor('#4FC3F7')
      .borderRadius({ bottomLeft: 20, bottomRight: 20 })
      .alignSelf(ItemAlign.Center)
  }
  .width(180).height(200).backgroundColor('#E1F5FE')
  .borderRadius(20).justifyContent(FlexAlign.End)
  .border({ width: 3, color: '#81D4FA' })

  // 水杯上的文字叠加
  Column() {
    Text('🥤').fontSize(24)
    Text(`${this.count}/${this.target}`).fontSize(16)
      .fontWeight(FontWeight.Bold).fontColor('#0277BD')
  }
  .width('100%').height('100%')
  .justifyContent(FlexAlign.Center)
  .alignItems(HorizontalAlign.Center)
}

水杯渲染的核心逻辑分为两层:

第一层(杯体 + 水位):

外层 Column 代表整个水杯(180×200),使用 #E1F5FE(极浅蓝色)作为空杯颜色,borderRadius(20) 创建圆角杯体,border 添加 3px 的边框模拟玻璃质感。

内层 Column 代表水位,它的 height 根据 count/target 比例动态计算:160 * (this.count / this.target)。当 count = 0 时,高度为 0——水杯看起来是空的。

这里有一个重要的布局细节:外层 Column 设置了 justifyContent(FlexAlign.End),这意味着内层水位 Column 会从底部开始排列。结合 alignSelf(ItemAlign.Center) 让水位水平居中,水的填充效果就是从杯底逐渐上升到杯顶——完美模拟了真实倒水的视觉效果。

borderRadius({ bottomLeft: 20, bottomRight: 20 }) 为水位底部添加圆角,与水杯的底部圆角匹配。水位顶部是平的,这与真实透明玻璃杯中水的视觉效果一致。

第二层(文字叠加):

第二个 Column 使用 position 叠加在水杯之上(Stack 的特性),显示 🥤 表情和 count/target 计数。justifyContent(Center)alignItems(Center) 确保文字在杯体中央显示。

3.4 激励按钮与辅助操作

Button('🥤 喝一杯').width(200).height(56).borderRadius(28)
  .backgroundColor('#4FC3F7').fontSize(22).margin({ top: 12 })
  .onClick(() => this.addWater())

Row({ space: 8 }) {
  Button('重置').width(90).height(40).borderRadius(20)
    .backgroundColor('#9E9E9E')
    .onClick(() => { this.count = 0 })
  Button('目标+').width(90).height(40).borderRadius(20)
    .backgroundColor('#FF9800')
    .onClick(() => { this.target++ })
}.margin({ top: 8 })

三个按钮的设计体现了清晰的信息层级

  1. 主按钮「🥤 喝一杯」:最大的尺寸(200×56),最醒目的颜色(蓝色系),是用户最频繁的操作
  2. 次按钮「重置」:中等大小,灰色,用于重置杯数
  3. 次按钮「目标+」:橙色,用于增加每日目标

「目标+」按钮允许用户灵活调整目标——如果某天想多喝水,可以增加目标杯数。但需要注意的是,当前版本没有「目标-」按钮,这可以作为一个改进点。

四、HarmonyOS 特性分析

4.1 动态高度的响应式渲染

本例最值得关注的技术点是基于 @State 的动态高度变化。当 count 递增时:

.height(this.count > 0 ? 160 * (this.count / this.target) : 0)

ArkTS 的响应式系统会自动重新计算高度值并重新渲染。这种"数据驱动高度"的模式适用于:

  • 进度条/进度环
  • 可伸缩面板
  • 加载动画
  • 数据可视化的柱状图

4.2 borderRadius 的精细控制

.borderRadius({ bottomLeft: 20, bottomRight: 20 })

ArkTS 的 borderRadius 支持对象参数,可以对四个角分别设置圆角值。这是 CSS 中 border-radius 的增强版本,比传统的四值简写更直观。

4.3 语义化颜色系统

应用使用了统一的蓝色系配色:

  • 背景:#E1F5FE(极浅蓝)
  • 水位:#4FC3F7(中蓝,与主按钮一致)
  • 边框:#81D4FA(浅蓝)
  • 文字:#0277BD(深蓝)

这种单色系设计让视觉风格高度统一,用户看到蓝色系就能联想到"水"的主题。在 HarmonyOS 中,颜色值支持六位十六进制(#RRGGBB)或八位(#AARRGGBB)格式。

五、UI/UX 设计与最佳实践

5.1 视觉反馈的心理学应用

应用的核心理念是通过即时视觉反馈激励用户行为:

  1. 水位上升的满足感:每次点击,蓝色水位都会增高,这种可视化的进步给人成就感
  2. 计数实时更新:水杯中央的 count/target 数字实时变化
  3. 鼓励语的阶段性变化:从「记得喝水」到「达标了!很棒!」,五种文字对应五个进步阶段

5.2 交互反馈的细节

  • 按钮溢出保护:当达到目标后,点击主按钮无效而不是报错
  • 温和的视觉风格:圆角按钮、柔和的蓝色系、大号字体,给人友好的感觉
  • 重置安全:重置仅清空杯数(count),不影响目标(target)和杯量(glassSize),避免用户误操作导致所有设置丢失

5.3 无障碍设计建议

对于喝水提醒这类高频使用的健康应用,建议增加:

  • 系统通知提醒:通过 @ohos.notification 定时发送喝水通知
  • 振动反馈:每次喝水时提供短暂振动
  • 语音播报:鼓励语通过 TTS 语音读出来

六、可扩展思路

  1. 历史记录与统计:使用 @ohos.data.preferences 持久化每日饮水数据,显示周/月趋势图。
  2. 自定义杯量:允许用户设置不同的杯量(如 250ml、300ml),适配不同水杯。
  3. 定时提醒:利用后台任务定时发送喝水通知,每隔 1-2 小时提醒一次。
  4. 多主题:支持不同的水杯样式和背景主题,增加趣味性。
  5. 喝水挑战:加入连续达标天数的统计,用游戏化方式激励用户。

七、总结

喝水提醒应用以 83 行简洁代码,实现了一个完整的健康追踪工具。它的核心价值在于:

  • Stack 层叠实现复杂视觉:水杯 + 水位 + 文字的叠加,展示了 Stack 在可视化组件中的强大能力
  • 动态高度驱动进度显示:用 @State 控制 Column 高度,替代了传统的 Progress 组件
  • 计算属性简化业务逻辑progresstotalMlencouragement 三个 getter 将原始数据转化为展示信息
  • 正向激励设计:通过鼓励语和可视化进度,利用心理学原理帮助用户养成好习惯

这个应用的代码模式——状态驱动的动态尺寸变化 + 层叠布局的可视化——可以轻松复用到电池电量显示、存储空间指示器、技能升级条等场景。

下一期我们将继续健康主题,探讨一个更具互动性的应用——计步器(Pedometer),学习如何使用 Progress 的环形样式和 Ring 类型,打造精美的运动数据看板。


本文是 HarmonyOS 应用开发实战系列第 33 期,更多内容请关注专栏。

Logo

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

更多推荐