本系列为黑马程序员HarmonyOS4+NEXT实战案例跟做记录,目录:
- 鸿蒙软件开发实战案例(一)
- 鸿蒙软件开发实战案例(二)
- 鸿蒙软件开发实战案例(三)
- 鸿蒙软件开发实战案例(四)
- 鸿蒙软件开发实战案例(五)
在列表页面中实现panel底部弹窗及其相关组件的构建:
整体效果展示(.gif):
![](https://img-blog.csdnimg.cn/direct/df4a86caa1be43a5998bc02af918e7c8.gif)
相关实现代码:
itemindex:
import router from '@ohos.router'
import { CommonConstants } from '../common/constants/CommonConstants'
import itemcard from '../view/item/itemcard'
import ItemList from '../view/item/itemlist'
import itempanelhaeder from '../view/item/itempanelhead'
import numberkeybord from '../view/item/numberkeybord'
import RecordItem from '../viewmodel/Recordltem'
@Entry
@Component
export default struct Itemindex {
@State amount:number =1
@State value:string =''
@State showPanel:boolean=false
@State item:RecordItem=null
@State isFood:boolean=true
showfoots(){
let cc:any=router.getParams()
if(cc.aa===0){
return this.isFood=false
}
else {
return this.isFood=true
}
}
onPanelShow(item:RecordItem){
this.showPanel=true
this.value=''
this.item=item
this.amount=1
}
build() {
Column(){
//导航
this.Header()
//列表
ItemList({showPanel: this.onPanelShow.bind(this),isFood:this.showfoots()})
.layoutWeight(1)
//底部面板
Panel(this.showPanel){//panel使用需要传入布尔函数
//顶部日期
itempanelhaeder()
//记录项
if(this.item){//在item不为null时再渲染
itemcard({amount:this.amount,item:$item})
}
//键盘
numberkeybord({amount:$amount,value:$value})
//按钮
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)
.backgroundColor($r('app.color.primary_color'))
.type(ButtonType.Normal)//按钮类型,原型,胶囊型之类的
.borderRadius(6)
.onClick(()=>this.showPanel=false)
}
.margin({top:10})
}
.mode(PanelMode.Full)//默认打开时占满整个屏幕
.dragBar(false)//是否允许调整其高度
.backgroundMask($r('app.color.light_gray'))//设置蒙版颜色
.backgroundColor(Color.White)
}
.width('100%')
.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('94%')
.height(32)
}
}
页面卡片布局:
import { CommonConstants } from '../../common/constants/CommonConstants'
import RecordItem from '../../viewmodel/Recordltem'
@Component
export default struct itemcard {
@Prop amount:number
@Link item:RecordItem
build() {
Column({space:CommonConstants.SPACE_8}){
Image(this.item.image)
.width(150)
Row(){
Text(this.item.name)
.fontWeight(CommonConstants.FONT_WEIGHT_700)
}
.backgroundColor($r('app.color.lightest_primary_color'))
.padding({left:12,right:12,bottom:5,top:5})
Divider()//下划线
.width(CommonConstants.THOUSANDTH_940)
.opacity(0.6)//透明度
Row({space:CommonConstants.SPACE_8}){
this.nutrientInfo('热量(千卡)',this.item.calorie)
if(this.item.id<10000){
this.nutrientInfo('碳水(克)',this.item.carbon)
this.nutrientInfo('蛋白质(克)',this.item.protein)
this.nutrientInfo('脂肪(克)',this.item.fat)
}
}
Divider()//下划线
.width(CommonConstants.THOUSANDTH_940)
.opacity(0.6)//透明度
Row(){
Column({space:CommonConstants.SPACE_4}){
Text(this.amount.toFixed(1))//toFixed将数值转变为字符串的形式,()跟小数位数
.fontWeight(CommonConstants.FONT_WEIGHT_600)
.fontSize(50)
.fontColor($r('app.color.primary_color'))
Divider()
.color($r('app.color.primary_color'))
}
.width('40%')
.justifyContent(FlexAlign.Center)
Text(this.item.unit)
.fontWeight(CommonConstants.FONT_WEIGHT_600)
.fontColor($r('app.color.light_gray'))
}
}
}
@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))//toFixed将数值转变为字符串的形式,()跟小数位数
.fontSize(18)
.fontWeight(CommonConstants.FONT_WEIGHT_700)
}
}
}
数字键盘布局及其相应规则实现:
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct numberkeybord {
numbers:string[]=['1','2','3','4','5','6','7','8','9','0','.']
@Link amount:number
@Link value:string
@Styles keybordStyles(){
.backgroundColor(Color.White)
.height(60)
.borderRadius(8)
}
build() {
Grid(){//网格组件,详见api文档
ForEach(this.numbers,num=>{
GridItem(){
Text(num)
.fontSize(20)
.fontWeight(CommonConstants.FONT_WEIGHT_900)
}
.keybordStyles()
.onClick(()=>this.clicknumer(num))
})
GridItem(){
Text('删除')
.fontSize(20)
.fontWeight(CommonConstants.FONT_WEIGHT_900)
}
.onClick(()=>this.clickdelete())
.keybordStyles()
}
.width('100%')
.backgroundColor($r('app.color.light_gray'))
.height(280)
.columnsTemplate('1fr 1fr 1fr')
.columnsGap(8)//列、行、内间距
.rowsGap(8)
.padding(8)
}
clicknumer(num:string){
//拼接输入内容
let val =this.value+num
//校验输入格式
let firstIndex=val.indexOf('.')//从前面记录第一个小数点的脚标
let lastIndex=val.lastIndexOf('.')//从后面面记录第一个小数点的脚标
if(firstIndex!==lastIndex||lastIndex!=-1&&firstIndex<val.length-2)//校验是否存在两个小数点或者是小数点超过后两位
{
return
}
//转数值
let amount = this.parsefloat(val)//如果字符串最后一位是否为'.'的话会直接报错无法转换需要重定义函数进行检测修改
//保存
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){
if(!str){
return 0//当str的值为空时返回0,不然会显示NaN
}
if(str.endsWith('.')){//检验字符串最后一位是否为'.'
str=str.substring(0,str.length-1)//将最后的'.'去除
}
return parseFloat(str)
}
}