根据黑马程序员(https://www.bilibili.com/video/BV1Sa4y1Z7B1?p=36&vd_source=756db0e287d884b2979ab5b54a59c305)编写
一、实战案例-饮食记录页面
Index(首页)基础设置
Index主页设置
主页效果图:
需求:三个图片及其对应文本,点击之后可以跳转页面
代码实现(Index.ets):
import { CommonConstants } from '../common/constants/CommonConstants'
import RecordIndex from '../view/record/RecordIndex'
@Entry
@Component
struct Index {
@State currentIndex: number = 0
@State isPageShow: boolean = false
onPageShow(){
this.isPageShow = true
}
onPageHide(){
this.isPageShow = false
}
@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() {
RecordIndex()
}
.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%')
.vertical(false)
.onChange(index => this.currentIndex = index)
}
}
饮食记录页面
1.顶部搜索栏设置
搜索栏效果图:
需求:一个搜索栏,一个图标
实现代码(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)
}
}
2.统计卡片页面设置
效果图:
需求:时间选择栏,一个大卡片中包含的三项内容,且卡片可以滑动
时间选择栏
在上面的效果图有时间选择栏,其中包含日期,和选择项如下图所示,所以需要进行日期以及两个按钮的设置。
日期和按钮设置(DataPickDialog.ets):
import { CommonConstants } from '../../common/constants/CommonConstants'
@CustomDialog
export default struct DatePickDialog {
controller: CustomDialogController
selectedDate: Date = new Date()
build() {
Column({space: CommonConstants.SPACE_12}){
// 1.日期选择器
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)
})
// 2.按钮
Row({space:CommonConstants.SPACE_12}){
Button('取消')
.width(120)
.backgroundColor($r('app.color.light_gray'))
.onClick(() => this.controller.close())
Button('确定')
.width(120)
.backgroundColor($r('app.color.primary_color'))
.onClick(() => {
// 1.保存日期到全局存储
AppStorage.SetOrCreate('selectedDate', this.selectedDate.getTime())
// 2.关闭窗口
this.controller.close()
})
}
}
.padding(CommonConstants.SPACE_12)
}
}
统计卡片设置
效果图如统计卡片页面设置的效果图所示
在统计卡片中有日期信息和统计信息的设置,日期信息在上面已经完成了大部分设置,统计信息则还需要设置
统计卡片代码(StatsCard.ets):
import { CommonConstants } from '../../common/constants/CommonConstants'
import DateUtil from '../../common/utils/DateUtil'
import CalorieStats from './CalorieState'
import DatePickDialog from './DatePickDialog'
import NutrientStats from './NutrientState'
@Component
export default struct StatsCard {
//读取时间,获得当前日期
@StorageProp('selectedDate') selectedDate: number = DateUtil.beginTimeOfDay(new Date())
//定义controller,使用DataPickDialog中设置的builder
controller: CustomDialogController = new CustomDialogController({
builder: DatePickDialog({selectedDate: new Date(this.selectedDate)})
})
build() {
Column(){
// 1.日期信息
Row(){
Text(DateUtil.formatDate(this.selectedDate))
.fontColor($r('app.color.secondary_color'))
Image($r('app.media.ic_public_spinner'))
.width(20)
.fillColor($r('app.color.secondary_color'))
}
.padding(CommonConstants.SPACE_8)
.onClick(() => this.controller.open())
//2.统计信息
//swiper为滑动组件
Swiper(){
// 2.1.热量统计
CalorieStats()
// 2.2.营养素统计
NutrientStats()
}
}
.width(CommonConstants.THOUSANDTH_940)
.backgroundColor($r('app.color.stats_title_bgc'))
.borderRadius(CommonConstants.DEFAULT_18)
}
}
在页面中,我们需要热量统计和营养素统计的功能
热量统计效果图如上图所示,营养素统计如下:
营养素效果图:
热量统计代码(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
}
//抽取函数方便调用
@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'))
}
}
}
build() {
Row({space: CommonConstants.SPACE_6}){
// 1.饮食摄入
this.StatsBuilder('饮食摄入', 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('还可以吃', this.remainCalorie(), `推荐${this.recommend}`)
}
// 3.运动消耗
this.StatsBuilder( '运动消耗', this.expend)
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
//设置边距
.padding({top: 30, bottom: 35})
}
}
营养素统计代码(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
//抽取组件方便调用
@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'))
}
}
build() {
Row({space: CommonConstants.SPACE_6}){
this.StatsBuilder(
'碳水化合物',
this.carbon,
this.recommendCarbon,
$r('app.color.carbon_color')
)
this.StatsBuilder(
'蛋白质',
this.protein,
this.recommendProtein,
$r('app.color.protein_color')
)
this.StatsBuilder(
'脂肪',
this.fat,
this.recommendFat,
$r('app.color.fat_color')
)
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.padding({top: 30, bottom: 35})
}
}
3.记录列表
效果图
需求:循环显示标题以及里面的内容信息和添加删除按钮
记录列表代码(RecordList.ets):
import { CommonConstants } from '../../common/constants/CommonConstants'
//对Text组件提出,方便以后调用,减少代码冗余
@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是暂时先渲染一样的,后面方便进行更改
ForEach([1,2,3,4],(item) => {
ListItem(){
Column({space: CommonConstants.SPACE_8}){
// 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同上
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)
.height('100%')
.margin({top: 10})
}
//对删除按钮进行设置,但此处还未实现删除功能
@Builder deleteButton(){
Image($r('app.media.ic_public_delete_filled'))
.width(20)
.fillColor(Color.Red)
.margin(5)
}
}
整合代码(Recordlndex.ets)
import RecordList from './RecordList'
import SearchHeader from './SearchHeader'
import StatsCard from './StatsCard'
@Component
export default struct RecordIndex{
build(){
Column(){
//1.头部搜索栏
SearchHeader()
//2.统计卡片
StatsCard()
//3.纪录列表
RecordList()
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.index_page_background'))
}
}
小结
此次实现了首页的基础和饮食记录页面,在饮食记录页面之中还有一些功能仍未完善有待改进,如记录列表中的循环现在还是固定的,没有进行修改,早餐前面的图片可以根据后面的千卡进行动态增加图片里面的颜色以及应该给删除图标增加删除功能,所以此次只是基础功能的实现