[HarmonyOS NEXT 实战案例:健康应用] 基础篇 - 水平分割布局打造健康数据仪表盘

2025-06-11 23:28:30
133次阅读
0个评论

[HarmonyOS NEXT 实战案例:健康应用] 基础篇 - 水平分割布局打造健康数据仪表盘

项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star

效果演示

image.png

引言

健康应用是移动应用开发中的重要场景之一,用户可以通过健康应用查看自己的健康数据,如步数、心率、睡眠等。一个设计良好的健康数据仪表盘需要清晰展示各类健康数据,并提供便捷的导航和切换功能。本教程将详细讲解如何使用HarmonyOS NEXT的RowSplit组件构建一个健康数据仪表盘,通过水平分割布局将界面分为导航区域和数据展示区域。

组件概述

在本案例中,我们将使用以下HarmonyOS NEXT组件:

组件名称 功能描述
RowSplit 水平分割布局容器,将界面分为左右两部分
Column 垂直布局容器,用于垂直排列子组件
Row 水平布局容器,用于水平排列子组件
Text 文本组件,用于显示标题、数值等文本信息
Button 按钮组件,用于导航菜单项
Progress 进度条组件,用于显示步数完成情况
Image 图片组件,用于显示心率图表
Circle 圆形组件,用于创建睡眠分析的图例
Stack 堆叠布局容器,用于将多个组件叠放在一起
ForEach 循环渲染组件,用于渲染导航菜单项和睡眠数据

数据模型

在本案例中,我们定义了两个接口来描述数据结构:

interface DataItem {
    icon: Resource
    name: string
    id: string
}

interface StatusItem {
    value: number
    color: string
    label: string
}

DataItem接口用于描述导航菜单项,包含图标、名称和ID。StatusItem接口用于描述睡眠状态数据,包含数值、颜色和标签。

在组件中,我们定义了三个状态变量:

@Component
export struct HealthDashboard {
    @State currentTab: string = 'steps'
    @State datalist: DataItem[] = [
        { icon: $r('app.media.01'), name: '步数', id: 'steps' },
        { icon: $r('app.media.02'), name: '心率', id: 'heart' },
        { icon: $r('app.media.03'), name: '睡眠', id: 'sleep' }
    ]
    @State Status: StatusItem[] = [
        { value: 30, color: '#4CAF50', label: '深睡' },
        { value: 50, color: '#8BC34A', label: '浅睡' },
        { value: 20, color: '#CDDC39', label: '清醒' }
    ]
    
    build() {
        // 组件内容
    }
}

这些状态变量的作用如下:

  • currentTab:记录当前选中的标签页,初始值为'steps'。
  • datalist:导航菜单项数据数组,包含步数、心率和睡眠三个选项。
  • Status:睡眠状态数据数组,包含深睡、浅睡和清醒三种状态的比例。

代码实现

外层容器

我们使用RowSplit组件作为最外层容器,将界面分为左右两部分:

RowSplit() {
    // 左侧导航
    Column() {
        // 导航内容
    }
    .width('25%')
    .backgroundColor('#f5f9f5')

    // 主内容区
    Column() {
        // 数据展示内容
    }
    .padding(20)
}
.height(400)

RowSplit设置了400的高度,确保整个界面有足够的显示空间。左侧的导航区域占总宽度的25%,背景色为浅绿色。右侧的主内容区占剩余宽度,设置了20的内边距。

左侧导航

左侧导航区域包含一个标题和三个导航按钮:

Column() {
    Text('健康数据')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 30 })

    ForEach(this.datalist, (item: DataItem) => {
        Button(item.name)
            .width('80%')
            .height(50)
            .fontSize(16)
            .backgroundColor(this.currentTab === item.id ? '#4CAF50' : 'transparent')
            .fontColor(this.currentTab === item.id ? '#ffffff' : '#333333')
            .onClick(() => {
                this.currentTab = item.id
            })
            .margin({ bottom: 10 })
    })
}
.width('25%')
.backgroundColor('#f5f9f5')

在这个部分:

  1. 我们首先添加了一个标题文本"健康数据",设置了字体大小、字体粗细和上下边距。
  2. 然后使用ForEach循环渲染datalist数组中的导航按钮。
  3. 每个按钮都设置了宽度、高度、字体大小、背景色、文本颜色和下边距。
  4. 使用条件表达式this.currentTab === item.id ? '#4CAF50' : 'transparent'this.currentTab === item.id ? '#ffffff' : '#333333'来动态设置按钮的背景色和文本颜色,当按钮对应的ID与currentTab相等时,按钮显示为绿底白字,否则显示为透明底黑字。
  5. onClick事件处理函数中使用this.currentTab = item.id来更新当前选中的标签页。

主内容区

主内容区根据currentTab的值显示不同的内容:

Column() {
    if (this.currentTab === 'steps') {
        Column() {
            Text('今日步数')
                .fontSize(18)
                .margin({ bottom: 20 })

            Progress({
                value: 7500,
                total: 10000,
                type: ProgressType.Linear
            })
                .width('90%')
                .height(20)

            Text('7500/10000 步')
                .fontSize(16)
                .margin({ top: 10 })
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
    else if (this.currentTab === 'heart') {
        Column() {
            Text('心率监测')
                .fontSize(18)
                .margin({ bottom: 20 })

            Image($r('app.media.big20'))
                .width('90%')
                .height(200)

            Text('平均心率: 72 bpm')
                .fontSize(16)
                .margin({ top: 20 })
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
    else {
        Column() {
            Text('睡眠分析')
                .fontSize(18)
                .margin({ bottom: 20 })

            Stack() {
                ForEach(this.Status, (item: StatusItem) => {
                    Row() {
                        Circle()
                            .width(15)
                            .height(15)
                            .fill(item.color)
                            .margin({ right: 10 })

                        Text(`${item.label}: ${item.value}%`)
                            .fontSize(14)
                    }
                    .margin({ bottom: 5 })
                })
            }
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
}
.padding(20)

在这个部分:

  1. 我们使用条件语句根据currentTab的值渲染不同的内容。
  2. currentTab为'steps'时,显示步数信息,包括一个标题、一个进度条和一个步数文本。
  3. currentTab为'heart'时,显示心率信息,包括一个标题、一个心率图表图片和一个平均心率文本。
  4. currentTab为'sleep'时,显示睡眠分析信息,包括一个标题和一个睡眠状态列表。
  5. 每个内容区域都使用Column组件垂直排列内容,并设置height('100%')justifyContent(FlexAlign.Center)使内容垂直居中。

布局技巧

1. 水平分割布局

使用RowSplit组件可以轻松实现左右分割的布局效果,适合导航菜单和内容区域的布局。通过设置子组件的宽度比例,可以控制左右两部分的大小关系。

2. 条件渲染

在本案例中,我们使用条件语句根据currentTab的值渲染不同的内容:

if (this.currentTab === 'steps') {
    // 显示步数信息
}
else if (this.currentTab === 'heart') {
    // 显示心率信息
}
else {
    // 显示睡眠信息
}

这种条件渲染的方式使得我们可以在同一个区域根据用户选择显示不同的内容,提高界面的灵活性和交互性。

3. 垂直居中

在主内容区的每个内容块中,我们使用了以下设置使内容垂直居中:

.height('100%')
.justifyContent(FlexAlign.Center)

这种设置使得内容在垂直方向上居中显示,提高了界面的美观性和一致性。

4. 动态样式

在导航按钮中,我们使用条件表达式动态设置按钮的背景色和文本颜色:

.backgroundColor(this.currentTab === item.id ? '#4CAF50' : 'transparent')
.fontColor(this.currentTab === item.id ? '#ffffff' : '#333333')

这种动态样式的设置使得当前选中的按钮高亮显示,提供了清晰的视觉反馈。

交互实现

1. 导航切换

通过按钮的onClick事件实现导航切换,点击按钮时更新currentTab状态:

.onClick(() => {
    this.currentTab = item.id
})

currentTab状态变化时,主内容区会根据条件语句显示对应的内容,按钮的样式也会相应地更新。

2. 进度条展示

在步数信息中,我们使用Progress组件展示步数完成情况:

Progress({
    value: 7500,
    total: 10000,
    type: ProgressType.Linear
})
    .width('90%')
    .height(20)

Progress组件接受三个参数:

  • value:当前值,这里是7500步
  • total:总值,这里是10000步
  • type:进度条类型,这里是线性进度条

通过这些参数,进度条会自动计算并显示当前步数占目标步数的比例。

3. 睡眠数据展示

在睡眠分析中,我们使用ForEach循环渲染睡眠状态数据:

Stack() {
    ForEach(this.Status, (item: StatusItem) => {
        Row() {
            Circle()
                .width(15)
                .height(15)
                .fill(item.color)
                .margin({ right: 10 })

            Text(`${item.label}: ${item.value}%`)
                .fontSize(14)
        }
        .margin({ bottom: 5 })
    })
}

每个睡眠状态项包含一个颜色圆点和一个文本,文本使用模板字符串${item.label}: ${item.value}%显示状态标签和比例。

样式设计

1. 颜色搭配

在本案例中,我们使用了以下颜色:

  • 主色调:#4CAF50(绿色),用于选中状态的按钮背景和深睡状态的颜色
  • 次要色调:#8BC34A(浅绿色)和#CDDC39(黄绿色),用于浅睡和清醒状态的颜色
  • 背景色:#f5f9f5(浅绿色),用于左侧导航区域的背景
  • 文本颜色:#333333(深灰色)用于主要文本,#ffffff(白色)用于选中状态的按钮文本

这种颜色搭配符合健康应用的视觉风格,以绿色为主色调,传达健康、活力的感觉。

2. 间距设置

在布局中,我们使用了一致的间距设置:

  • 主内容区设置了20的内边距
  • 标题下方设置了20的下边距
  • 导航按钮之间设置了10的下边距
  • 睡眠状态项之间设置了5的下边距

这种一致的间距设置使得界面看起来整洁有序。

3. 字体大小

在本案例中,我们使用了不同的字体大小来区分不同层级的信息:

  • 导航区域标题:20
  • 内容区域标题:18
  • 导航按钮文本:16
  • 数据文本:14-16

这种字体大小的层次设置使得用户可以快速识别重要信息。

总结

在本教程中,我们学习了如何使用HarmonyOS NEXT的RowSplit组件构建一个健康数据仪表盘。通过水平分割布局,我们将界面分为导航区域和数据展示区域,使得用户可以方便地切换不同类型的健康数据。

收藏00

登录 后评论。没有帐号? 注册 一个。