根据黑马程序员(https://www.bilibili.com/video/BV1Sa4y1Z7B1?p=36&vd_source=756db0e287d884b2979ab5b54a59c305)编写
一、实战案例-食物列表设置
最终效果图:
食物列表主页(ItemIndex)设置
ItemIndex主页设置
效果图如上所示
需求:
需要展示所有食物信息(本次代码只写了全麦吐司),点击添加之后会弹出一个面板,在面板中显示食物的相应信息,还有一个输入框,用来输入吃了多少个该食物,以及两个按钮。
食物列表主代码实现(Index.ets):
import router from '@ohos.router'
import { CommonConstants } from '../common/constants/CommonConstants'
import ItemCard from '../view/item/ItemCard'
import ItemList from '../view/item/ItemList'
import ItemPanelHeader from '../view/item/ItemPanelHeader'
import NumberKeyboard from '../view/item/NumberKeyboard'
@Entry
@Component
struct ItemIndex {
@State showPanel:boolean = false
@State amount:number=1
@State value: string = ''
onPanelShow(){
//控制面板展示
this.showPanel=true
}
build() {
Column() {
//1.头部导航
this.header()
//2.列表
ItemList({showPanel:this.onPanelShow.bind(this)})
//设置列表高度,否则面板位置不确定点击按钮没有反应
.layoutWeight(1)
//3.底部面板
Panel(this.showPanel){
//3.1.顶部日期
ItemPanelHeader()
//3.2记录项卡片
ItemCard({amount:this.amount})
//3.3数字键盘
NumberKeyboard({amount:$amount,value:$value})
//3.4按钮
Row({space: CommonConstants.SPACE_6}){
Button('取消')
.width(120)
.backgroundColor($r('app.color.light_gray'))
.type(ButtonType.Normal)
.borderRadius(6)
.onClick(() => this.showPanel = false)
Button('提交')
.width(120)
.type(ButtonType.Normal)
.borderRadius(6)
.backgroundColor($r('app.color.primary_color'))
.onClick(() => this.showPanel = false)
}
.margin({top: 10})
}
.mode(PanelMode.Full)
.dragBar(false)
.backgroundMask($r('app.color.light_gray'))
.backgroundColor(Color.White)
}
.height('100%')
}
//头部
@Builder header(){
Row(){
Image($r('app.media.ic_public_back'))
.width(30)
.onClick(()=>router.back())
Blank()
Text('早餐').fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_600)
}.width(CommonConstants.THOUSANDTH_940)
.height(32)
}
}
1.头部设置(ItemPanelHeader.ets):
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct ItemPanelHeader {
build() {
Row(){
Text('2024年1月25日 早餐')
.fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_600)
Image($r('app.media.ic_public_spinner'))
.width(20)
.fillColor(Color.Black)
}
}
}
2.展示面板代码(ItemList.ets):
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct ItemList {
showPanel:()=>void
build() {
Tabs(){
//完成导航条
TabContent(){
this.TabContentBuilder()
}.tabBar('全部')
TabContent(){
this.TabContentBuilder()
}.tabBar('主食')
TabContent(){
this.TabContentBuilder()
}.tabBar('肉蛋奶')
}.width(CommonConstants.THOUSANDTH_940)
.height('100%')
}
//提出卡片,减少代码冗余
@Builder TabContentBuilder(){
List({space:CommonConstants.SPACE_10}){
//此处ForEach同上
ForEach([1,2,3,4,5,6],(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('91千卡/片').fontSize(14).fontColor($r('app.color.light_gray'))
}
Blank()
Image($r('app.media.ic_public_add_norm_filled'))
.width(18)
.fillColor($r('app.color.primary_color'))
}
.width('100%')
.padding(CommonConstants.SPACE_6)
}
.onClick(()=>this.showPanel())
})
}
.width('100%')
.height('100%')
}
}
3.输入面板实现代码(ItemCard.ets):
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct ItemCard {
@Prop amount:number
build() {
Column({space:CommonConstants.SPACE_8}){
//1.图片信息
Image($r('app.media.toast'))
.width(150)
//2.名称信息
Row(){
Text('全麦吐司').fontWeight(CommonConstants.FONT_WEIGHT_600)
}
.backgroundColor($r('app.color.lightest_primary_color'))
.padding({top:5,bottom:5,left:15,right:12})
//水平线
Divider().width(CommonConstants.THOUSANDTH_940).opacity(0.6)
//3.营养素信息
Row({space:CommonConstants.SPACE_8}){
this.NutrientInfo('热量(千卡)',91.0)
this.NutrientInfo('碳水(克)',91.0)
this.NutrientInfo('蛋白质',4.4)
this.NutrientInfo('脂肪(克)',1.3)
}
Divider().width(CommonConstants.THOUSANDTH_940).opacity(0.6)
//4.数量信息
Column({space:CommonConstants.SPACE_4}){
Text(this.amount.toFixed(1))
.fontSize(50).fontColor($r('app.color.primary_color'))
.fontWeight(CommonConstants.FONT_WEIGHT_600)
Divider().color($r('app.color.primary_color'))
}
}
}
@Builder NutrientInfo(label:string,value:number){
Column({space:CommonConstants.SPACE_8}){
Text(label).fontSize(14).fontColor($r('app.color.light_gray'))
Text((value * this.amount).toFixed(1)).fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_700)
}
}
}
4.数字键盘代码(NumberKeyboard.ets)
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct NumberKeyboard {
numbers: string[] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.']
@Link amount: number
@Link value: string
//设置样式
@Styles keyBoxStyle(){
.backgroundColor(Color.White)
.borderRadius(8)
.height(60)
}
build() {
//表格面板
Grid(){
ForEach(this.numbers, num => {
GridItem(){
Text(num).fontSize(20).fontWeight(CommonConstants.FONT_WEIGHT_900)
}
.keyBoxStyle()
.onClick(() => this.clickNumber(num))
})
GridItem(){
Text('删除').fontSize(20).fontWeight(CommonConstants.FONT_WEIGHT_900)
}
.keyBoxStyle()
.onClick(() => this.clickDelete())
}
.width('100%')
.height(280)
.backgroundColor($r('app.color.index_page_background'))
.columnsTemplate('1fr 1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.padding(8)
.margin({top: 10})
}
clickNumber(num: string){
// 1.拼接用户输入的内容
let val = this.value + num
// 2.校验输入格式是否正确
let firstIndex = val.indexOf('.')
let lastIndex = val.lastIndexOf('.')
if(firstIndex !== lastIndex || (lastIndex != -1 && lastIndex < val.length - 2)){
// 非法输入
return
}
// 3.将字符串转为数值
let amount = this.parseFloat(val)
// 4.保存
if(amount >= 999.9){
this.amount = 999.0
this.value = '999'
}else{
this.amount = amount
this.value = val
}
}
clickDelete(){
if(this.value.length <= 0){
this.value = ''
this.amount = 0
return
}
this.value = this.value.substring(0, this.value.length - 1)
this.amount = this.parseFloat(this.value)
}
parseFloat(str: string){
//判断,如果字符为空,那么将其数置为0,否则会出现NAN
if(!str){
return 0
}
if(str.endsWith('.')){
str = str.substring(0, str.length - 1)
}
return parseFloat(str)
}
}
小结
本次实现了食物列表的功能,在代码中使用了一些新的API如Grid用来实现数字输入的功能,但功能扔未完善,很多代码都是写死的,并没有完成达到最终效果。