目录
一、饮食记录页面
1、首页Tabs UI设计
饮食记录页面Tabs结构:
TabContent结构:
饮食记录页面逻辑结构:
关键技术:Tabs组件
Tabs组件可以实现页面内视图内容快速切换,包含TabBar和TabContent两个部分,其基本语法如下:
Tabs({barPosition:BarPosition.End}) {//导航栏位置下部
TabContent(){
Text('记录内容')
}
.tabBar(this.TabBarBuilder($r('app.string.tab_record'),$r('app.media.ic_calendar'),0))
TabContent(){
Text('发现内容')
}
.tabBar(this.TabBarBuilder($r('app.string.tab_user'),$r('app.media.ic_user_portrait'),1))
TabContent(){
Text('我的内容')
}
.tabBar(this.TabBarBuilder($r('app.string.tab_discover'),$r('app.media.discover'),2))
}
.width('100%')
.height('100%')
.vertical(false)//导航项水平排列
.onChangeI(index => this.currentIndex = index)
}
其中TabBarBuilder为全局自定义构建函数,用来构建自定义组件,组件内的函数和变量的调用需加this
@state currentIndex: number = 0//导航角标
@Builder TabBuilder(title: string, image: ResourceStr, index: number){
Column({ space: 4 }) {
Image(image).fillcolor(this.currentIndex === index?Color.Blue:Color.Gray)//根据角标填充颜色,选中为蓝色否则为灰色
Text(title) .fillcolor(this.currentIndex === index?Color.Blue:Color.Gray)
justifyContent(FlexAlign.Center) // 设置内容在主轴上的对齐方式为居中
}
}
运行效果:
点击切换底部导航项时,颜色也相应改变
代码如下:
import { CommonConstants } from '../common/constants/CommonConstants'
@Entry
@Component
struct Index {
@State currentIndex: number = 0
@Builder TabBarBuilder(title: ResourceStr, image: ResourceStr, index: number) {
Column({ space: CommonConstants.SPACE_8 }) {
Image(image)
.width(22)
.fillColor(this.selectColor(index))
Text(title)
.fontSize(14)
.fontColor(this.selectColor(index))
}
}
selectColor(index: number) {
return this.currentIndex === index ? $r('app.color.primary_color') : $r('app.color.gray')
}
build() {
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
Text('饮食记录页面')
}
.tabBar(this.TabBarBuilder($r('app.string.tab_record'), $r('app.media.ic_calendar'), 0))
TabContent() {
Text('发现页面')
}
.tabBar(this.TabBarBuilder($r('app.string.tab_discover'), $r('app.media.discover'), 1))
TabContent() {
Text('我的主页')
}
.tabBar(this.TabBarBuilder($r('app.string.tab_user'), $r('app.media.ic_user_portrait'), 2))
}
.width('100%')
.height('100%')
.onChange(index => this.currentIndex = index)
}
}
2、顶部搜索栏
搜索栏结构
搜索框中可设置默认占位符
Search({placeholder: '搜索饮食或运动信息'})
Imgae图片为一个邮箱,邮箱右上角通过Badge组件添加未读信息数量
Badge({count: 1, position: BadgePosition.RightTop, style: {fontSize: 12}}){
Image($r('app.media.ic_public_email'))
.width(24)
}
//消息数量:1,位置:右上角
相关代码:
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct SearchHeader {
build() {
Row({space: CommonConstants.SPACE_6}){
Search({placeholder: '搜索饮食或运动信息'})
.textFont({size: 18})
.layoutWeight(1)
Badge({count: 1, position: BadgePosition.RightTop, style: {fontSize: 12}}){
Image($r('app.media.ic_public_email'))
.width(24)
}
}
.width(CommonConstants.THOUSANDTH_940)
}
}
运行效果:
代码如下:
//Index.ets文件
TabContent() {
RecordIndex()//调用记录首页
}
//RecordIndex.ets文件
import SearchHeader from './SearchHeader'
@Component
export default struct RecordIndex {
build() {
Column(){
// 1.头部搜索栏
SearchHeader()//调用搜索栏组件
// 2.统计卡片
Text('统计卡片')
// 3.记录列表
Text('记录列表')
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.index_page_background'))
}
}
//SearchHeader.ets
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct SearchHeader {
build() {
Row({space: CommonConstants.SPACE_6}){
Search({placeholder: '搜索饮食或运动信息'})
.textFont({size: 18})
.layoutWeight(1)
Badge({count: 1, position: BadgePosition.RightTop, style: {fontSize: 12}}){
Image($r('app.media.ic_public_email'))
.width(24)
}
}
.width(CommonConstants.THOUSANDTH_940)
}
}
3、统计卡片
统计卡片结构
要实现的功能:①日期选择窗口、②滑动统计卡片
关键技术:
日期选择器DatePicker
DatePicker({
start: new Date('2020-01-01'),
end: new Date(),
selected: this.selectedDate
})
.onChange((value: DatePickerResult) => {
this.selectedDate.setFullYear(value.year, value.month, value.day)
})
//start开始日期,end结束日期,selected设置选中项的日期
运行效果:
代码如下:
Swiper组件
滑块视图容器:提供子组件滑动轮播显示的能力。
运行效果:
代码如下:
CalorieStats.ets卡路里统计
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct CalorieStats {
intake: number=192
expend: number=150
recommend: number = CommonConstants.RECOMMEND_CALORIE
remainCalorie(){
return this.recommend - this.intake + this.expend
}
build() {
Row({space: CommonConstants.SPACE_6}){
// 1.饮食摄入
this.StatsBuilder({label: '饮食摄入', value: this.intake})
// 2.还可以吃
Stack(){
// 2.1.进度条
Progress({
value: this.intake,
total: this.recommend,
type: ProgressType.Ring
})
.width(120)
.style({strokeWidth: CommonConstants.DEFAULT_10})
.color($r('app.color.primary_color'))
// 2.2.统计数据
this.StatsBuilder({label: '还可以吃', value: this.remainCalorie(),tips: `推荐${this.recommend}`})
}
// 3.运动消耗
this.StatsBuilder({label: '运动消耗', value: this.expend})
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.padding({top: 30, bottom: 35})
}
@Builder StatsBuilder($$:{label: string, value: number, tips?: string}){
Column({space: CommonConstants.SPACE_6}){
Text($$.label)
.fontColor($r('app.color.gray'))
.fontWeight(CommonConstants.FONT_WEIGHT_600)
Text($$.value.toFixed(0))
.fontSize(20)
.fontWeight(CommonConstants.FONT_WEIGHT_700)
if($$.tips){
Text($$.tips)
.fontSize(12)
.fontColor($r('app.color.light_gray'))
}
}
}
}
NutrientStats.ets营养素统计
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct NutrientStats {
carbon: number=23
protein: number=9
fat: number=7
recommendCarbon: number = CommonConstants.RECOMMEND_CARBON
recommendProtein: number = CommonConstants.RECOMMEND_PROTEIN
recommendFat: number = CommonConstants.RECOMMEND_FAT
build() {
Row({space: CommonConstants.SPACE_6}){
this.StatsBuilder({
label: '碳水化合物',
value: this.carbon,
recommend: this.recommendCarbon,
color: $r('app.color.carbon_color')
})
this.StatsBuilder({
label: '蛋白质',
value: this.protein,
recommend: this.recommendProtein,
color: $r('app.color.protein_color')
})
this.StatsBuilder({
label: '脂肪',
value: this.fat,
recommend: this.recommendFat,
color: $r('app.color.fat_color')
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.padding({top: 30, bottom: 35})
}
@Builder StatsBuilder($$:{label: string, value: number, recommend: number, color: ResourceStr}){
Column({space: CommonConstants.SPACE_6}){
Stack(){
Progress({
value: $$.value,
total: $$.recommend,
type: ProgressType.Ring
})
.width(95)
.style({strokeWidth: CommonConstants.DEFAULT_6})
.color($$.color)
Column({space: CommonConstants.SPACE_6}){
Text('摄入推荐')
.fontSize(12)
.fontColor($r('app.color.gray'))
Text(`${$$.value.toFixed(0)}/${$$.recommend.toFixed(0)}`)
.fontSize(18)
.fontWeight(CommonConstants.FONT_WEIGHT_600)
}
}
Text(`${$$.label}(克)`)
.fontSize(12)
.fontColor($r('app.color.light_gray'))
}
}
}
4、记录列表
记录列表结构
关键技术:List组件
运行效果:
代码如下:
import router from '@ohos.router'
import { CommonConstants } from '../../common/constants/CommonConstants'
@Extend(Text) function grayText(){
.fontSize(14)
.fontColor($r('app.color.light_gray'))
}
@Component
export default struct RecordList {
build() {
List({space:CommonConstants.SPACE_10}){
ForEach([1,2,3,4,5],(item)=>{
ListItem(){
Column(){
//1.分组标题
Row({space:CommonConstants.SPACE_4}){
Image($r('app.media.ic_breakfast')).width(24)
Text('早餐').fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_700)
Text('建议423-592千卡').grayText()
Blank()
Text('190').fontSize(14).fontColor($r('app.color.primary_color'))
Text('千卡').grayText()
Image($r('app.media.ic_public_add_norm_filled'))
.width(20)
.fillColor($r('app.color.primary_color'))
}
.width('100%')
//2.组内记录列表
List(){
ForEach([1,2],(item)=>{
ListItem(){
Row({space:CommonConstants.SPACE_6}){
Image($r('app.media.toast')).width(50)
Column({space:CommonConstants.SPACE_4}){
Text('全麦吐司').fontWeight(CommonConstants.FONT_WEIGHT_500)
Text('1片').grayText()
}
Blank()
Text('91千卡').grayText()
}
.width('100%')
.padding(CommonConstants.SPACE_6)
}
.swipeAction({end:this.deleteButton.bind(this)})
})
}
.width('100%')
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(CommonConstants.DEFAULT_18)
.padding(CommonConstants.SPACE_12)
}
})
}
.width(CommonConstants.THOUSANDTH_940)
.margin({top:10})
.height('100%')
}
@Builder deleteButton(){
Image($r('app.media.ic_public_delete_filled'))
.width(20)
.fillColor(Color.Red)
.margin(5)
}
}
二、遇到问题
问题:如何实现切换TabBar选项时改变选中项目颜色
解决方法:
- 定义状态变量:首先,定义一个角标状态变量来存储当前选中的标签索引。
- 修改 TabBarBuilder:更新
TabBarBuilder
方法,使其能够根据当前选中的索引来改变颜色。 - 处理 TabBar 切换事件:在
Tabs
组件中,使用.onChange
方法来监听标签页的切换事件,并更新当前选中的索引。 - 确保 TabBar 更新:当当前选中的索引发生变化时,确保 TabBar 能够响应这一变化并重新渲染。
三、总结
本次开发的饮食记录页面提供了用户友好的界面和流畅的交互体验。通过Tabs组件、搜索栏、统计卡片和记录列表,用户能够轻松地记录和管理自己的饮食情况。其中新用到了Tabs组件切换用户点击内容,无需页面路由,更加便捷,DatePicker组件生成选择日期弹窗。Swiper组件,切换记录卡片信息。