先来看看我们要实现的驾驶舱的页面是什么样的
对于这种 响应式布局的页面构建,我们的脑子里面要有一个概念,就是"分而治之"。我们把这个页面进行分割,分割成不同的块然后再来逐个实现. 不难发现,我们可以将这个看到的效果简单的分割:1.首先在顶部有个banner轮播,我们称之为顶部区域。2.紧接着有个时间筛选的区域,这个筛选区域和上面的轮播有个层叠部分,筛选区域最下面有个5个TAB切换按钮.,3. 下面经营指标的展示。
所以我们可以用一个Column控件,分成head( ) ,option( ) ,biz( ) 三块来实现。由于整改页面的内容都比较长,所以在Column的外层在用一个Scroll组件来包裹。head( )和option( ) 之间的间隔可以用.margin({ top: -20 })来实现.
接下来我们来逐个讲述一下各个区域的实现:
- head( )
head头部区域是一个轮播图,我们用一个Stack层叠布局作为根布局,然后我们有个蓝色的背景图,作为整个背景。在一个Banner控件作为页面的内容,Banner 中每个ITEM的 内容大概是 左边文字 中间图标 右边文字的组成形式
对此我们将Banner的ITEM封装成一个单独的组件 大题实现部分如下 :
export struct BannerItemView {
// @Link model: BannerItemModel
@Prop model: BannerItemModel
build() {
Stack({ alignContent: Alignment.Center }) {
// 主要内容
Row() {
// 左侧文本
Column() {
Text(this.model.leftTile).fontColor(Color.White).fontSize(16)
Text(`${this.model.leftValue}`)
.fontColor("#00d8fe")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 8 })
Text('环比').fontColor(Color.White).fontSize(14).margin({ top: 8 })
TriangleIcon({
isUp: this.model.mQOQRate > 0,
IconSize: 10
}).margin({ top: 8 })
Text(`${this.model.mQOQRate}%`).fontColor(Color.Red).fontSize(14).margin({ top: 8 })
}
.margin({ left: 15 })
.alignItems(HorizontalAlign.Start)
// 中间图片
Image($r(this.model.mCenterImage))
.objectFit(ImageFit.Contain)
.width(200)
.height(200)
// 右侧文本
Column() {
Text('完成率').fontColor(Color.White).fontSize(16)
Text(`${this.model.rightValue}%`)
.fontColor("#00d8fe")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 8 })
Text('同比').fontColor(Color.White).fontSize(14).margin({ top: 8 })
TriangleIcon({
isUp: this.model.mYOYRate > 0,
IconSize: 10
}).margin({ top: 8 })
Text(`${this.model.mYOYRate}%`).fontColor(Color.Red).fontSize(14).margin({ top: 8 })
}
.margin({ right: 15 })
.alignItems(HorizontalAlign.End)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
}
.width('100%')
.height('100%') // 可以根据需要调整高度
}
}
Banner 组件鸿蒙官方也有,这边使用的是@abner/banner ,head的 大体实现如下
@Builder
header() {
Stack({ alignContent: Alignment.Center }) {
// 背景图
Image($r('app.media.common_header_bg'))
.width('100%')
.height('100%')
.objectFit(ImageFit.Cover)
Banner({
data: this.bannerModel,
itemPage: this.itemPage,
indicator: new DotIndicator()
.itemWidth(8)
.itemHeight(8)
.selectedItemWidth(8)
.selectedItemHeight(8)
.color(Color.Gray)
.selectedColor(Color.White),
indicatorType: IndicatorType.bottomCenter,
isLoop: true,
})
// BannerItemView({ model: this.bannerModel[0] })
}.width('100%').height(300)
}
-
option()
现在我们对option区域进行庖丁解牛,可以看到页面在垂直方向 先是一个年月日切换的控件,中间区域是显示当前选定的时间,紧接着底部是指标的快速定位切换
关于第一个日月的切换控件,我们可以 用一个ROW 组件 来包裹Text组件,然后对于每个Text都占据.layoutWeight(1)
即可达到相应的效果,中间的时间选择同样的是一个Text显示开始日期中间显示"-",最后在显示一个结束日期即可
最下面用一个TABS添加5个TabContent()即可
Tabs({ barPosition: BarPosition.Start }) { TabContent().tabBar('经营').height(0) TabContent().tabBar('会员').height(0) TabContent().tabBar('直销').height(0) TabContent().tabBar("商企").height(0) TabContent().tabBar("品质").height(0) }
所以整体的代码大概是这样:
@Builder option() { Column() { Row() { ForEach(['日', '周', '月', '季', '年'], (range: string) => { Text(range) .fontColor(this.currentTimeRange === range ? "#0000AA" : Color.Gray) .fontSize(18) .onClick(() => { this.currentTimeRange = range showToast("当前点击" + range) }) .layoutWeight(1)// .fontWeight(FontWeight.Bold) .textAlign(TextAlign.Center) }) } .backgroundColor("#f0f1f5") .margin({ top: 20 }) .width('90%') .padding(10) .borderRadius(10) Row() { Column() { Text('2024-01-01') .fontSize(16) .fontColor(Color.Black) } .backgroundColor("#f0f1f5") .padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(10).layoutWeight(1) // Text('-') .fontSize(18) .margin(10) .fontColor('#0000AA') Column() { Text('2024-08-16') .fontSize(16) .fontColor(Color.Black) } .backgroundColor("#f0f1f5") .padding({ left: 20, right: 20, top: 8, bottom: 8 }).borderRadius(10).layoutWeight(1) // } .backgroundColor(Color.White) .margin({ top: 20 }) .width('90%') .justifyContent(FlexAlign.SpaceBetween) .borderRadius(10) Tabs({ barPosition: BarPosition.Start }) { TabContent().tabBar('经营').height(0) TabContent().tabBar('会员').height(0) TabContent().tabBar('直销').height(0) TabContent().tabBar("商企").height(0) TabContent().tabBar("品质").height(0) } .height(50) // 设置 Tabs 的高度,只显示 tabBar } .width('100%') .margin({ top: -20 }) .borderRadius(28) // 添加这行来设置所有四个角的圆角为 8 .backgroundColor(Color.White) .height('auto') // 或者不设置 height }
-
biz()
此处的biz的展示其实我之前就有写过2篇博客来介绍 控件的实现
自定义View上下箭头
自定义View 圆形进度条
对于此处先是Column作为最外层,紧接着展示了经营指标说明,酒店的数据量,以及就是水平 展示各个指标名称数据,增加减少,对应的完成率。
所以此处大体的代码如下:
@Builder biz() { Column() { this.commonTitle("经营指标") HotelNumView({ model: this.total_model }) CockpitProgressView({ model: $model1 }).padding({ left: 10 }) CockpitProgressView({ model: $model2 }).padding({ left: 10 }) CockpitProgressView({ model: $model3 }).padding({ left: 10 }) HotelNumView({ model: this.middle_model }) CockpitProgressView({ model: $model11 }).padding({ left: 10 }) CockpitProgressView({ model: $model12 }).padding({ left: 10 }) CockpitProgressView({ model: $model13 }).padding({ left: 10 }) HotelNumView({ model: this.low_model }) CockpitProgressView({ model: $model21 }).padding({ left: 10 }) CockpitProgressView({ model: $model22 }).padding({ left: 10 }) CockpitProgressView({ model: $model23 }).padding({ left: 10 }) } .width('100%') .margin({ top: 20 }) .borderRadius({ topLeft: 28, topRight: 28 }) .justifyContent(FlexAlign.Start) // 确保 Row 内的内容靠左对齐 .alignItems(HorizontalAlign.Start) .backgroundColor(Color.White) .height('auto') // 或者不设置 height .padding({ bottom: 20 }) }
-
我们可以看到 上述的 CockpitProgressView 中的model 都带了一个 " " 符号,因为我们这个数据是来自网络请求的,为了让父子组件之间能正常的传值,以及组件状态的刷新,我们边用 " "符号,因为我们这个数据是来自网络请求的,为了让父子组件之间能正常的传值,以及组件状态的刷新,我们边用 " "符号,因为我们这个数据是来自网络请求的,为了让父子组件之间能正常的传值,以及组件状态的刷新,我们边用""符号来设置我们的变量.
至此。我们的页面"小而全"的鸿蒙Harmony应用开发,数据驾驶舱的相关介绍已经完毕。大家可以下载相关代码进行研究!
完整项目下载地址