前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >117. [HarmonyOS NEXT 实战案例:健康应用] 高级篇 - 健康数据仪表盘的高级布局与自适应设计

117. [HarmonyOS NEXT 实战案例:健康应用] 高级篇 - 健康数据仪表盘的高级布局与自适应设计

作者头像
全栈若城
发布于 2025-06-12 01:11:09
发布于 2025-06-12 01:11:09
10100
代码可运行
举报
文章被收录于专栏:若城技术专栏若城技术专栏
运行总次数:0
代码可运行

[HarmonyOS NEXT 实战案例:健康应用] 高级篇 - 健康数据仪表盘的高级布局与自适应设计

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

效果演示

引言

在前两篇教程中,我们学习了如何使用HarmonyOS NEXT的RowSplit组件构建健康数据仪表盘的基本布局,以及如何添加交互功能和状态管理。本篇教程将进一步深入,讲解健康数据仪表盘的高级布局技巧和自适应设计,使应用能够在不同尺寸的设备上提供一致且优质的用户体验。

自适应布局概述

自适应布局是指应用界面能够根据设备屏幕尺寸和方向自动调整布局,提供最佳的用户体验。在HarmonyOS NEXT中,我们可以使用以下技术实现自适应布局:

技术

描述

使用场景

媒体查询

根据设备屏幕尺寸和方向应用不同的样式

在不同尺寸的设备上使用不同的布局

百分比布局

使用百分比值设置组件尺寸

使组件尺寸相对于父容器自动调整

弹性布局

使用弹性布局使组件自动填充可用空间

使组件尺寸相对于可用空间自动调整

栅格布局

使用栅格系统组织界面元素

创建复杂且响应式的布局

高级布局技巧

1. 使用媒体查询实现响应式布局

媒体查询允许我们根据设备屏幕尺寸和方向应用不同的样式。在HarmonyOS NEXT中,我们可以使用@MediaQuery装饰器实现媒体查询:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
export struct HealthDashboard {
    @State currentTab: string = 'steps'
    @State datalist: DataItem[] = [...]
    @State Status: StatusItem[] = [...]
    
    // 添加媒体查询状态
    @State isWideScreen: boolean = false
    @State navWidth: string = '25%'
    @State contentPadding: number = 20
    
    // 媒体查询装饰器
    @MediaQuery(MediaQueryCondition.WIDE_SCREEN)
    onWideScreen(matches: boolean) {
        this.isWideScreen = matches
        this.navWidth = matches ? '20%' : '25%'
        this.contentPadding = matches ? 30 : 20
    }
    
    build() {
        // 根据屏幕宽度选择不同的布局
        if (this.isWideScreen) {
            this.buildWideScreenLayout()
        } else {
            this.buildNormalLayout()
        }
    }
    
    // 宽屏布局
    buildWideScreenLayout() {
        RowSplit() {
            // 左侧导航
            Column() {
                Text('健康数据')
                    .fontSize(24) // 增大字体
                    .fontWeight(FontWeight.Bold)
                    .margin({ top: 30, bottom: 40 }) // 增大边距

                ForEach(this.datalist, (item: DataItem) => {
                    Button(item.name)
                        .width('80%')
                        .height(60) // 增大按钮高度
                        .fontSize(18) // 增大字体
                        .backgroundColor(this.currentTab === item.id ? '#4CAF50' : 'transparent')
                        .fontColor(this.currentTab === item.id ? '#ffffff' : '#333333')
                        .onClick(() => {
                            this.currentTab = item.id
                        })
                        .margin({ bottom: 15 }) // 增大边距
                })
            }
            .width(this.navWidth)
            .backgroundColor('#f5f9f5')

            // 主内容区
            Column() {
                // 根据当前标签页显示不同内容
                if (this.currentTab === 'steps') {
                    this.buildStepsContent()
                }
                else if (this.currentTab === 'heart') {
                    this.buildHeartContent()
                }
                else {
                    this.buildSleepContent()
                }
            }
            .padding(this.contentPadding)
        }
        .height(500) // 增大高度
    }
    
    // 普通布局
    buildNormalLayout() {
        RowSplit() {
            // 左侧导航
            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(this.navWidth)
            .backgroundColor('#f5f9f5')

            // 主内容区
            Column() {
                // 根据当前标签页显示不同内容
                if (this.currentTab === 'steps') {
                    this.buildStepsContent()
                }
                else if (this.currentTab === 'heart') {
                    this.buildHeartContent()
                }
                else {
                    this.buildSleepContent()
                }
            }
            .padding(this.contentPadding)
        }
        .height(400)
    }
    
    // 步数内容构建方法
    buildStepsContent() {
        Column() {
            Text('今日步数')
                .fontSize(this.isWideScreen ? 22 : 18)
                .margin({ bottom: this.isWideScreen ? 30 : 20 })

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

            Text('7500/10000 步')
                .fontSize(this.isWideScreen ? 18 : 16)
                .margin({ top: this.isWideScreen ? 15 : 10 })
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
    
    // 心率内容构建方法
    buildHeartContent() {
        Column() {
            Text('心率监测')
                .fontSize(this.isWideScreen ? 22 : 18)
                .margin({ bottom: this.isWideScreen ? 30 : 20 })

            Image($r('app.media.big20'))
                .width('90%')
                .height(this.isWideScreen ? 250 : 200)

            Text('平均心率: 72 bpm')
                .fontSize(this.isWideScreen ? 18 : 16)
                .margin({ top: this.isWideScreen ? 25 : 20 })
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
    
    // 睡眠内容构建方法
    buildSleepContent() {
        Column() {
            Text('睡眠分析')
                .fontSize(this.isWideScreen ? 22 : 18)
                .margin({ bottom: this.isWideScreen ? 30 : 20 })

            // 在宽屏模式下使用水平布局,在普通模式下使用垂直布局
            if (this.isWideScreen) {
                Row() {
                    // 睡眠状态图表
                    this.buildSleepChart()
                    
                    // 睡眠状态图例
                    this.buildSleepLegend()
                }
                .width('100%')
                .justifyContent(FlexAlign.SpaceBetween)
            } else {
                Column() {
                    // 睡眠状态图表
                    this.buildSleepChart()
                    
                    // 睡眠状态图例
                    this.buildSleepLegend()
                        .margin({ top: 20 })
                }
                .width('100%')
            }
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
    
    // 睡眠图表构建方法
    @Builder
    buildSleepChart() {
        Stack() {
            // 使用饼图展示睡眠状态
            Row() {
                ForEach(this.Status, (item: StatusItem) => {
                    Column() {
                        Stack() {
                            Circle()
                                .width(this.isWideScreen ? 150 : 120)
                                .height(this.isWideScreen ? 150 : 120)
                                .fill('#f0f0f0')
                            
                            Arc()
                                .width(this.isWideScreen ? 150 : 120)
                                .height(this.isWideScreen ? 150 : 120)
                                .startAngle(0)
                                .sweepAngle(item.value * 3.6) // 将百分比转换为角度
                                .stroke(item.color)
                                .strokeWidth(this.isWideScreen ? 20 : 15)
                        }
                    }
                })
            }
        }
        .width(this.isWideScreen ? '50%' : '100%')
    }
    
    // 睡眠图例构建方法
    @Builder
    buildSleepLegend() {
        Column() {
            ForEach(this.Status, (item: StatusItem) => {
                Row() {
                    Circle()
                        .width(this.isWideScreen ? 20 : 15)
                        .height(this.isWideScreen ? 20 : 15)
                        .fill(item.color)
                        .margin({ right: this.isWideScreen ? 15 : 10 })

                    Text(`${item.label}: ${item.value}%`)
                        .fontSize(this.isWideScreen ? 16 : 14)
                }
                .margin({ bottom: this.isWideScreen ? 10 : 5 })
            })
        }
        .width(this.isWideScreen ? '40%' : '100%')
    }
}
2. 使用栅格布局组织复杂界面

栅格布局是一种将界面划分为网格的布局方式,可以帮助我们创建复杂且响应式的布局。在HarmonyOS NEXT中,我们可以使用GridRowGridCol组件实现栅格布局:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
struct GridLayoutExample {
    build() {
        Column() {
            GridRow() {
                GridCol({ span: 12 }) {
                    Text('健康数据仪表盘')
                        .fontSize(24)
                        .fontWeight(FontWeight.Bold)
                        .width('100%')
                        .textAlign(TextAlign.Center)
                        .margin({ bottom: 20 })
                }
                
                GridCol({ span: 4 }) {
                    this.buildNavigation()
                }
                
                GridCol({ span: 8 }) {
                    this.buildContent()
                }
            }
            .gutter(16) // 设置列间距
        }
        .width('100%')
        .padding(20)
    }
    
    @Builder
    buildNavigation() {
        // 导航内容
    }
    
    @Builder
    buildContent() {
        // 主内容
    }
}
3. 使用@BuilderParam实现布局定制

@BuilderParam装饰器允许我们将UI构建逻辑作为参数传递给组件,从而实现高度定制化的布局:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
struct DashboardCard {
    @Prop title: string
    @BuilderParam content: () => void
    @BuilderParam header?: () => void
    @BuilderParam footer?: () => void
    
    build() {
        Column() {
            // 卡片头部(可选)
            if (this.header) {
                this.header()
            } else {
                Text(this.title)
                    .fontSize(18)
                    .fontWeight(FontWeight.Bold)
                    .margin({ bottom: 16 })
            }
            
            // 卡片内容
            this.content()
            
            // 卡片底部(可选)
            if (this.footer) {
                this.footer()
            }
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#ffffff')
        .borderRadius(8)
        .shadow({ radius: 4, color: '#0000001A', offsetX: 0, offsetY: 2 })
    }
}

使用这个组件可以创建高度定制化的仪表盘卡片:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DashboardCard({
    title: '今日步数',
    header: () => {
        Row() {
            Text('今日步数')
                .fontSize(18)
                .fontWeight(FontWeight.Bold)
            
            Blank()
            
            Button('查看详情')
                .fontSize(14)
                .height(32)
                .backgroundColor('#4CAF50')
        }
        .width('100%')
        .margin({ bottom: 16 })
    },
    content: () => {
        Column() {
            Progress({
                value: 7500,
                total: 10000,
                type: ProgressType.Linear
            })
                .width('100%')
                .height(20)
                
            Text('7500/10000 步')
                .fontSize(16)
                .margin({ top: 10 })
        }
    },
    footer: () => {
        Text('目标完成率: 75%')
            .fontSize(14)
            .fontColor('#666666')
            .margin({ top: 16 })
    }
})

高级组件封装

1. 数据仪表盘组件

我们可以创建一个通用的数据仪表盘组件,用于展示各种类型的健康数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
struct DataDashboard {
    @Prop title: string
    @Prop value: number
    @Prop unit: string
    @Prop maxValue: number
    @Prop color: string = '#4CAF50'
    @Prop type: string = 'circle' // 'circle' | 'linear' | 'arc'
    
    build() {
        Column() {
            Text(this.title)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
                .margin({ bottom: 16 })
            
            if (this.type === 'circle') {
                this.buildCircleProgress()
            } else if (this.type === 'linear') {
                this.buildLinearProgress()
            } else {
                this.buildArcProgress()
            }
            
            Text(`${this.value} ${this.unit}`)
                .fontSize(18)
                .fontWeight(FontWeight.Bold)
                .margin({ top: 16 })
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#ffffff')
        .borderRadius(8)
        .shadow({ radius: 4, color: '#0000001A', offsetX: 0, offsetY: 2 })
    }
    
    @Builder
    buildCircleProgress() {
        Stack() {
            Circle()
                .width(120)
                .height(120)
                .fill('#f0f0f0')
            
            Progress({
                value: this.value,
                total: this.maxValue,
                type: ProgressType.Ring
            })
                .width(120)
                .height(120)
                .color(this.color)
        }
    }
    
    @Builder
    buildLinearProgress() {
        Progress({
            value: this.value,
            total: this.maxValue,
            type: ProgressType.Linear
        })
            .width('100%')
            .height(20)
            .color(this.color)
    }
    
    @Builder
    buildArcProgress() {
        Stack() {
            Arc()
                .width(120)
                .height(120)
                .startAngle(180)
                .sweepAngle(180)
                .stroke('#f0f0f0')
                .strokeWidth(10)
            
            Arc()
                .width(120)
                .height(120)
                .startAngle(180)
                .sweepAngle(180 * this.value / this.maxValue)
                .stroke(this.color)
                .strokeWidth(10)
        }
    }
}

使用这个组件可以创建各种类型的数据仪表盘:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Row() {
    DataDashboard({
        title: '今日步数',
        value: 7500,
        unit: '步',
        maxValue: 10000,
        color: '#4CAF50',
        type: 'linear'
    })
    .layoutWeight(1)
    .margin({ right: 8 })
    
    DataDashboard({
        title: '平均心率',
        value: 72,
        unit: 'bpm',
        maxValue: 100,
        color: '#F44336',
        type: 'circle'
    })
    .layoutWeight(1)
    .margin({ left: 8 })
}
.width('100%')
.margin({ bottom: 16 })

Row() {
    DataDashboard({
        title: '睡眠时长',
        value: 7.5,
        unit: '小时',
        maxValue: 10,
        color: '#2196F3',
        type: 'arc'
    })
    .layoutWeight(1)
    .margin({ right: 8 })
    
    DataDashboard({
        title: '卡路里',
        value: 350,
        unit: 'kcal',
        maxValue: 500,
        color: '#FF9800',
        type: 'circle'
    })
    .layoutWeight(1)
    .margin({ left: 8 })
}
.width('100%')
2. 图表组件

我们可以创建一个通用的图表组件,用于展示各种类型的健康数据趋势:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
struct ChartComponent {
    @Prop title: string
    @Prop data: Array<{ label: string, value: number }>
    @Prop color: string = '#4CAF50'
    @Prop type: string = 'bar' // 'bar' | 'line' | 'pie'
    
    @State private maxValue: number = 0
    @State private chartWidth: number = 0
    @State private chartHeight: number = 200
    
    aboutToAppear() {
        // 计算最大值
        this.maxValue = Math.max(...this.data.map(item => item.value))
    }
    
    build() {
        Column() {
            Text(this.title)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
                .margin({ bottom: 16 })
            
            if (this.type === 'bar') {
                this.buildBarChart()
            } else if (this.type === 'line') {
                this.buildLineChart()
            } else {
                this.buildPieChart()
            }
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#ffffff')
        .borderRadius(8)
        .shadow({ radius: 4, color: '#0000001A', offsetX: 0, offsetY: 2 })
        .onAreaChange((oldArea: Area, newArea: Area) => {
            this.chartWidth = newArea.width as number - 32 // 减去内边距
        })
    }
    
    @Builder
    buildBarChart() {
        Column() {
            // Y轴最大值
            Text(`${this.maxValue}`)
                .fontSize(12)
                .fontColor('#666666')
                .textAlign(TextAlign.End)
                .width('100%')
            
            // 图表区域
            Row() {
                ForEach(this.data, (item, index) => {
                    Column() {
                        // 柱形
                        Stack() {
                            Rectangle()
                                .width(20)
                                .height(this.chartHeight)
                                .fill('#f0f0f0')
                            
                            Rectangle()
                                .width(20)
                                .height(this.chartHeight * item.value / this.maxValue)
                                .fill(this.color)
                                .alignSelf(ItemAlign.End)
                        }
                        .height(this.chartHeight)
                        
                        // 标签
                        Text(item.label)
                            .fontSize(12)
                            .fontColor('#666666')
                            .margin({ top: 8 })
                    }
                    .margin({ right: index < this.data.length - 1 ? (this.chartWidth - 20 * this.data.length) / (this.data.length - 1) : 0 })
                })
            }
            .width('100%')
            .justifyContent(FlexAlign.SpaceBetween)
            
            // X轴
            Divider()
                .width('100%')
                .color('#f0f0f0')
                .margin({ top: 8 })
        }
    }
    
    @Builder
    buildLineChart() {
        // 线形图实现
        // 由于HarmonyOS NEXT目前没有直接的线形图组件,这里可以使用Path组件绘制折线
        // 或者使用第三方图表库
    }
    
    @Builder
    buildPieChart() {
        // 饼图实现
        // 可以使用多个Arc组件组合实现饼图
        // 或者使用第三方图表库
    }
}

使用这个组件可以创建各种类型的图表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ChartComponent({
    title: '一周步数趋势',
    data: [
        { label: '周一', value: 8000 },
        { label: '周二', value: 7500 },
        { label: '周三', value: 9000 },
        { label: '周四', value: 8500 },
        { label: '周五', value: 7000 },
        { label: '周六', value: 6500 },
        { label: '周日', value: 8000 }
    ],
    color: '#4CAF50',
    type: 'bar'
})
.margin({ bottom: 16 })

主题切换

我们可以实现主题切换功能,允许用户在浅色主题和深色主题之间切换:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
export struct HealthDashboard {
    @State currentTab: string = 'steps'
    @State datalist: DataItem[] = [...]
    @State Status: StatusItem[] = [...]
    
    // 添加主题状态
    @State isDarkMode: boolean = false
    @State themeColors: { [key: string]: string } = {
        // 浅色主题颜色
        light: {
            background: '#ffffff',
            navBackground: '#f5f9f5',
            text: '#333333',
            secondaryText: '#666666',
            primary: '#4CAF50',
            secondary: '#8BC34A',
            tertiary: '#CDDC39',
            cardBackground: '#ffffff',
            divider: '#f0f0f0'
        },
        // 深色主题颜色
        dark: {
            background: '#121212',
            navBackground: '#1e1e1e',
            text: '#ffffff',
            secondaryText: '#aaaaaa',
            primary: '#81C784',
            secondary: '#AED581',
            tertiary: '#DCE775',
            cardBackground: '#1e1e1e',
            divider: '#333333'
        }
    }
    
    // 获取当前主题颜色
    get colors() {
        return this.isDarkMode ? this.themeColors.dark : this.themeColors.light
    }
    
    build() {
        Column() {
            // 主题切换按钮
            Row() {
                Text('主题模式')
                    .fontSize(16)
                    .fontColor(this.colors.text)
                
                Blank()
                
                Toggle({ type: ToggleType.Switch, isOn: this.isDarkMode })
                    .onChange((isOn: boolean) => {
                        this.isDarkMode = isOn
                    })
            }
            .width('100%')
            .padding(16)
            
            // 健康数据仪表盘
            RowSplit() {
                // 左侧导航
                Column() {
                    Text('健康数据')
                        .fontSize(20)
                        .fontWeight(FontWeight.Bold)
                        .fontColor(this.colors.text)
                        .margin({ top: 20, bottom: 30 })

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

                // 主内容区
                Column() {
                    // 根据当前标签页显示不同内容
                    if (this.currentTab === 'steps') {
                        this.buildStepsContent()
                    }
                    else if (this.currentTab === 'heart') {
                        this.buildHeartContent()
                    }
                    else {
                        this.buildSleepContent()
                    }
                }
                .padding(20)
                .backgroundColor(this.colors.background)
            }
            .height(400)
        }
        .width('100%')
        .backgroundColor(this.colors.background)
    }
    
    // 步数内容构建方法
    buildStepsContent() {
        Column() {
            Text('今日步数')
                .fontSize(18)
                .fontColor(this.colors.text)
                .margin({ bottom: 20 })

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

            Text('7500/10000 步')
                .fontSize(16)
                .fontColor(this.colors.text)
                .margin({ top: 10 })
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
    
    // 心率内容构建方法
    buildHeartContent() {
        // 类似地,更新心率内容的颜色
    }
    
    // 睡眠内容构建方法
    buildSleepContent() {
        Column() {
            Text('睡眠分析')
                .fontSize(18)
                .fontColor(this.colors.text)
                .margin({ bottom: 20 })

            Stack() {
                ForEach(this.Status, (item: StatusItem, index) => {
                    Row() {
                        Circle()
                            .width(15)
                            .height(15)
                            .fill(index === 0 ? this.colors.primary : (index === 1 ? this.colors.secondary : this.colors.tertiary))
                            .margin({ right: 10 })

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

多语言支持

我们可以实现多语言支持,允许应用在不同语言环境下显示不同的文本:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
export struct HealthDashboard {
    @State currentTab: string = 'steps'
    @State datalist: DataItem[] = [...]
    @State Status: StatusItem[] = [...]
    
    // 添加语言状态
    @State currentLanguage: string = 'zh'
    @State translations: { [key: string]: { [key: string]: string } } = {
        // 中文翻译
        zh: {
            title: '健康数据',
            steps: '步数',
            heart: '心率',
            sleep: '睡眠',
            todaySteps: '今日步数',
            stepsUnit: '步',
            heartRate: '心率监测',
            averageHeartRate: '平均心率',
            bpm: 'bpm',
            sleepAnalysis: '睡眠分析',
            deepSleep: '深睡',
            lightSleep: '浅睡',
            awake: '清醒'
        },
        // 英文翻译
        en: {
            title: 'Health Data',
            steps: 'Steps',
            heart: 'Heart Rate',
            sleep: 'Sleep',
            todaySteps: 'Today\'s Steps',
            stepsUnit: 'steps',
            heartRate: 'Heart Rate Monitor',
            averageHeartRate: 'Average Heart Rate',
            bpm: 'bpm',
            sleepAnalysis: 'Sleep Analysis',
            deepSleep: 'Deep Sleep',
            lightSleep: 'Light Sleep',
            awake: 'Awake'
        }
    }
    
    // 获取当前语言的翻译
    get t() {
        return this.translations[this.currentLanguage]
    }
    
    // 初始化数据
    aboutToAppear() {
        this.updateDatalist()
        this.updateStatus()
    }
    
    // 更新导航菜单数据
    updateDatalist() {
        this.datalist = [
            { icon: $r('app.media.01'), name: this.t.steps, id: 'steps' },
            { icon: $r('app.media.02'), name: this.t.heart, id: 'heart' },
            { icon: $r('app.media.03'), name: this.t.sleep, id: 'sleep' }
        ]
    }
    
    // 更新睡眠状态数据
    updateStatus() {
        this.Status = [
            { value: 30, color: '#4CAF50', label: this.t.deepSleep },
            { value: 50, color: '#8BC34A', label: this.t.lightSleep },
            { value: 20, color: '#CDDC39', label: this.t.awake }
        ]
    }
    
    build() {
        Column() {
            // 语言切换按钮
            Row() {
                Text('Language / 语言')
                    .fontSize(16)
                
                Blank()
                
                Button(this.currentLanguage === 'zh' ? 'English' : '中文')
                    .onClick(() => {
                        this.currentLanguage = this.currentLanguage === 'zh' ? 'en' : 'zh'
                        this.updateDatalist()
                        this.updateStatus()
                    })
            }
            .width('100%')
            .padding(16)
            
            // 健康数据仪表盘
            RowSplit() {
                // 左侧导航
                Column() {
                    Text(this.t.title)
                        .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')

                // 主内容区
                Column() {
                    // 根据当前标签页显示不同内容
                    if (this.currentTab === 'steps') {
                        this.buildStepsContent()
                    }
                    else if (this.currentTab === 'heart') {
                        this.buildHeartContent()
                    }
                    else {
                        this.buildSleepContent()
                    }
                }
                .padding(20)
            }
            .height(400)
        }
        .width('100%')
    }
    
    // 步数内容构建方法
    buildStepsContent() {
        Column() {
            Text(this.t.todaySteps)
                .fontSize(18)
                .margin({ bottom: 20 })

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

            Text(`7500/10000 ${this.t.stepsUnit}`)
                .fontSize(16)
                .margin({ top: 10 })
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
    
    // 心率内容构建方法
    buildHeartContent() {
        Column() {
            Text(this.t.heartRate)
                .fontSize(18)
                .margin({ bottom: 20 })

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

            Text(`${this.t.averageHeartRate}: 72 ${this.t.bpm}`)
                .fontSize(16)
                .margin({ top: 20 })
        }
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
    
    // 睡眠内容构建方法
    buildSleepContent() {
        Column() {
            Text(this.t.sleepAnalysis)
                .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)
    }
}

总结

在本教程中,我们学习了健康数据仪表盘的高级布局技巧和自适应设计,但案例仍有完善空间

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
SASS相关知识
Sass(Syntactically Awesome Style Sheets)是一种CSS预处理器,用于增强CSS的功能和灵活性。它扩展了CSS,并引入了许多有用的功能,如变量、嵌套、混合、继承以及模块化的结构。
FGGIT
2024/12/20
1400
前端架构师技术之Sass
CSS 仅仅是一个标记语言,不是编程语言,因此不可以自定义变量,也不可以引用。CSS 主要有以下缺点。
张哥编程
2024/12/13
1090
【Sass/SCSS】预加载器中的“轩辕剑”
博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 随着前端技术发展的越来越迅速,前端的样式也需要更加贴近时代的审美,那么CSS就需要承担更多的工作,(强调!这不是煽情!这是宣讲背景。😄),为了给CSS怼上去,预加载器出现了,没错,CSS用上了武器。Sass/SCSS——预加载器中的“轩辕剑”,这也不是我帮它吹,是它自己说的,下图为例。 官网地址:SASS中文网 什么是Sass,它与SCSS是啥关系 感觉这里有点绕,这是怎么回事,
用户4268038
2021/11/18
8620
CSS预处理器入门:Sass/SCSS的实用指南
随着网页开发越发复杂,CSS 的可维护性也越显重要,但在 CSS 语法仍然受限的状况下,发展出了 CSS Preprocessor(CSS 预处理器)来扩展更多的写法。
写bug的高哈哈
2025/01/26
3730
CSS预处理器入门:Sass/SCSS的实用指南
CSS预处理器的对比 — sass、less和stylus
本文根据Johnathan Croom的《sass vs. less vs. stylus: Preprocessor Shootout》所译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://net.tutsplus.com/tutorials/html-css-techniques/sass-vs-less-vs-stylus-a-preprocessor-shootout/,以及作者相关信息 ——作者:Johnathan Croom
laixiangran
2018/04/11
4.7K0
CSS预处理器的对比 — sass、less和stylus
Sass常用语法
选择所有跟在article后的同层article元素,不管它们之间隔了多少其他元素:
码客说
2024/07/26
1280
如何使用SASS编写可重用的CSS
Sass 是一个CSS预处理程序,至今使用广泛,它之所以流行,是因为它修复了几个CSS缺陷:
前端小智@大迁世界
2022/06/15
8K0
如何使用SASS编写可重用的CSS
Sass(Scss)、Less的区别与选择 + 基本使用
Sass(Scss)、Less 都是 CSS 预处理器,他们定义了一种新的语言,其基本思想是,用一种专门的编程语言为 CSS 增加了一些编程的特性,将 CSS 作为目标生成文件,然后开发者就只要使用这种语言进行 CSS 的编码工作。
老猫-Leo
2023/12/11
1.9K0
前端入门23-CSS预处理器(Less&Sass)声明正文-CSS预处理(less&Sass)
这里就讲讲这两个问题,写过 CSS 应该就会比较清楚,虽然我才刚入门,但在写一些练手时就已经有点感觉了:写 CSS 后,很难维护,维护基本要靠注释来,而且由于 HTML 文档中标签的嵌套层次复杂,导致写 CSS 的选择器时也很费劲,尤其是在后期为某部分标签新增样式时,总会不知道到底应该在 CSS 文件中哪里写这个选择器,这个选择器是否会与前面冲突。
请叫我大苏
2018/12/26
1.7K0
Sass/SCSS 简明入门教程
Sass(Syntactically Awesome StyleSheets) 是 CSS 的一种扩展,是 CSS的 超集(通过编译生成浏览器可以处理传统 CSS)。Sass 的出现是为了解决在大型项目中传统 CSS 会遇到的重复、可维护性差等问题(添加了嵌套的规则、变量、mixins、选择器继承等特性)。让开发者可以编写简洁、富语意(expressive )、可复用、可维护性和可延展性性佳的 CSS 代码。
疯狂的技术宅
2021/03/16
3K0
Sass/Scss、Less 是什么?
Sass (Syntactically Awesome Stylesheets) 是一种动态样式语言,Sass 语法属于缩排语法,比 css 比多出好些功能 (如变量、嵌套、运算,混入 (Mixin)、继承、颜色处理,函数等),更容易阅读。
CRMEB商城源码
2022/08/02
1.3K0
Sass:强大而灵活的CSS预处理器详解
在前端开发的世界里,CSS(层叠样式表)作为样式描述语言,为我们提供了丰富的样式定义和布局方式。然而,随着项目规模的不断扩大和复杂度的提升,原生CSS的编写和维护逐渐变得繁琐。为了解决这个问题,Sass(Syntactically Awesome Stylesheets)作为CSS的预处理器应运而生,它为我们提供了许多强大的功能和工具,使得CSS的编写更加高效和灵活。
九转成圣
2024/05/14
4250
CSS 预编译语言 Sass 快速入门教程
CSS 作为一门样式语言,语法简单,易于上手,但是由于不具备常规编程语言提供的变量、函数、继承等机制,因此很容易写出大量没有逻辑、难以复用和扩展的代码,在日常开发使用中,如果没有完善的编码规范,编写的 CSS 代码会非常冗余且难以维护。
学院君
2020/10/30
7.4K0
CSS 预编译语言 Sass 快速入门教程
Sass 快速入门学习
  众所周知css并不能算是一们真正意义上的“编程”语言,它本身无法未完成像其它编程语言一样的嵌套、继承、设置变量等工作。
笔阁
2018/09/04
1.2K0
Sass 快速入门学习
Sass-学习笔记【基础篇】
最下边附结构图 在线编辑器网址如下:http://sassmeister.com/  注意编写的时候,符号千万别用了中文的:、;、。...之类的,会报错,Sass也转换不成css。 less和sass的区别: 博客园文章—http://www.cnblogs.com/wangpenghui522/p/5467560.html 本章主要内容:变量 、混合宏 、继承 、占位 、插值 、运算 、数据类型 1:定义 Sass是css预处理器的一种,也是最早的css预处理语言。Sass采用Ruby语言编写,为css增
xing.org1^
2018/05/17
5.1K0
sass 基础——回顾
1.webstorm 自动编译SASS   下载安装包 http://rubyinstaller.org/downloads/   然后点击安装,路径为默认路径就行, 勾选以下两项     add Ruby executables to your PATH     Associate .rb and rbw files with this Ruby information   安装完,打开命令行 输入 gem install sass   webstorm 配置 点击setting选择tool下的f
用户1197315
2018/01/22
1.1K0
sass 基础——回顾
vue2.0以上版本安装sass(scss)
1、异同:1)简言之可以理解scss是sass的一个升级版本,完全兼容sass之前的功能,又有了些新增能力。语法形式上有些许不同,最主要的就是sass是靠缩进表示嵌套关系,scss是花括号
IT工作者
2022/02/17
2.7K0
SASS详解@mixins/@include/@extend/@at-root
Sass 支持所有CSS3的 @规则,以及一些已知的其他特定的Sass "指令"。这些在 Sass 都有对应的效果,更多资料请查看 控制指令 (control directives) 。今天重点讲mixins/include/extend
周陆军博客
2023/05/14
1.2K0
【Sass学习笔记】001-Sass简介
CSS 预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为 CSS 增加了一些编程的特性,将 CSS 作为目标生成文件,然后开发者就只要使用这种语言进行编码工作。
訾博ZiBo
2025/01/06
1720
Sass 基础(一)
  css 是一些非常简单得语句的组合,既然简单的语句,就不可避免的有很多重复的,冗余的东西,而且没有传统 编程语言变量,控制语句等高级特性,所以造成了css 编写低效,往往需要查找替换,大量复制来修改或者编写。Sass 是用 弥补这些缺陷的,使开发更加的方便快捷,更加方便管理。   1.Sass和SCSS的区别。     文件扩展名不同,Sass 是以“.sass”后缀为扩展名,而 SCSS 是以“.scss”后缀为扩展名     语法书写方式不同,Sass 是以严格的缩进式语法规则来书写,不带
用户1197315
2018/01/19
8280
相关推荐
SASS相关知识
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • [HarmonyOS NEXT 实战案例:健康应用] 高级篇 - 健康数据仪表盘的高级布局与自适应设计
    • 效果演示
    • 引言
    • 自适应布局概述
    • 高级布局技巧
      • 1. 使用媒体查询实现响应式布局
      • 2. 使用栅格布局组织复杂界面
      • 3. 使用@BuilderParam实现布局定制
    • 高级组件封装
      • 1. 数据仪表盘组件
      • 2. 图表组件
    • 主题切换
    • 多语言支持
    • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档