前言
快递信息时间轴在购物软件中是必不可少的功能,通过时间轴可以展示快递从发货到派送的每一个环节。本篇文章通过代码的形式详细讲解在鸿蒙日常开发中如何实现时间轴的效果。(篇尾附有完整源码)
实现效果
先看最终实现的效果,下面通过代码讲解怎么一步步实现时间轴。
需求分析
- 快递信息时间轴整体包括三个状态:开始状态,当前状态,未达状态。
- 不同状态对应标题颜色及图标不同。
- 字体颜色可根据状态的不同改变。
- 提示文字可以根据具体情况是否显示
- 时间轴上的虚线可以根据当前节点布局的高度动态改变。(重点)
根据具体需求,采用 List 组件实现时间轴,然后通过控制不同 item 的状态来实现效果。
技术实现
- 定义节点数据对象,这里使用不同数字代表当前节点的状态:1 表示开始,2 表示当前,3 表示即将到达。
export class OrderDetailBean{
nodeStatus?: number //当前节点状态
nodeName?: string //节点名称
nodeNote?: string //节点备注
nodeTime?: string //节点时间
}
- 构建节点集合。
aboutToAppear(): void {
let order1 = new OrderDetailBean()
order1.nodeStatus = 1
order1.nodeName = "唐僧已经从长安出发"
order1.nodeTime = "1900-7-25 14:30:03"
order1.nodeNote = ""
let order2 = new OrderDetailBean()
order2.nodeStatus = 1
order2.nodeName = "唐僧到达五指山"
order2.nodeNote = "温馨提示:此处有妖猴出没"
let order3 = new OrderDetailBean()
order3.nodeStatus = 1
order3.nodeName = "孙悟空护送唐僧西天取经"
order3.nodeNote = "请小白龙提前准备,下一站化身白龙马。"
let order4 = new OrderDetailBean()
order4.nodeStatus = 2
order4.nodeName = "唐僧到达高老庄"
order3.nodeNote = "孙悟空大战猪八戒"
let order5 = new OrderDetailBean()
order5.nodeStatus = 3
order5.nodeName = "唐僧即将到达流沙河"
order5.nodeNote = "收服卷帘大将沙悟净。"
let order6 = new OrderDetailBean()
order6.nodeStatus = 3
order6.nodeName = "唐僧即将到达大雷音"
order6.nodeNote = "取得真经,修成正果。"
this.list.push(order1, order2, order3, order4, order5,order6)
}
- 使用 List 组件实现时间轴,鸿蒙的 List 组件原生支持横向和纵向布局,可以根据实际需求进行调整。这里使用默认纵向布局。
List(){
ForEach(this.list,(item: object, index: number) => {
this.itemView(this.list[index], index)
})
}.width("100%")
.height("100%")
- 接下来就是绘制 List 的 item,从最终的效果图来看,虚线部分只会在首尾之间显示,这里通过集合长度判断。最后一条虚线不显示,虚线可以通过设置布局边框的不同样式实现,这里使用的BorderStyle.Dashe。
// 时间轴
if (this.index < this.totalSize - 1) {
Stack()
.width(0)
.borderStyle(BorderStyle.Dashed)
.borderWidth(0.8)
.height(this.minHeight)
.borderColor($r('app.color.color_gray'))
}
- 同时虚线部分应该有最小高度,然后通过当前 Item 的高度变化动态改变虚线的高度,鸿蒙布局组件提供了 onAreaChange 方法用来监听当前布局高度的变化,通过修改最新高度来实现虚线的动态变化。注意这个 minHeight 必须使用@state修饰。
.onAreaChange((oldValue: Area, newValue: Area) => {
this.minHeight = newValue.height as number
})
- 最后使用 Row 布局将虚线布局和内容布局横向排列,就可以实现虚线跟随内容高度变化。
完整源码
@Component
struct ItemLayout {
@State bean: OrderDetailProgressBean = new OrderDetailProgressBean()
index: number = 0
totalSize: number = 0
@State minHeight: number = 54
build() {
Row() {
Column() {
// 时间轴节点
Image(this.getImage(this.bean.nodeStatus ?? 0))
.width(16)
.height(16)
.borderRadius(8)
// 时间轴
if (this.index < this.totalSize - 1) {
Stack()
.width(0)
.borderStyle(BorderStyle.Dashed)
.borderWidth(0.8)
.height(this.minHeight)
.borderColor("#BABEC4")
}
}
// 内容区域
Column() {
Text(this.bean.nodeName)
.fontSize(14)
.fontColor(this.getColor(this.bean.nodeStatus ?? 0))
.fontWeight(FontWeight.Medium)
Text(this.bean.nodeTime)
.fontSize(12)
.fontColor(Color.Gray)
.margin({
top: 4,
})
Text(this.bean.nodeNote)
.fontSize(12)
.fontColor(this.bean.nodeStatus == 1 ? Color.Gray : Color.Orange)
.margin({
top: 8,
})
}
.margin({
left: 8,
})
.alignItems(HorizontalAlign.Start)
.width("84%")
.margin({
left: 8,
})
.onAreaChange((oldValue: Area, newValue: Area) => {
this.minHeight = newValue.height as number
})
}
.alignItems(VerticalAlign.Top)
.width("100%")
}
getImage(state: number) {
if (state == 1) {
return $r("app.media.icon_complete")
} else if (state == 2) {
return $r("app.media.icon_selecte")
} else {
return $r("app.media.icon_unselecte")
}
}
getColor(state: number) {
if (state == 1) {
return $r("app.color.color_gray")
} else if (state == 2) {
return $r("app.color.color_black")
} else {
return $r("app.color.color_gray_1")
}
}
}
总结
本文的重点是知道虚线可以根据设置布局样式实现,然后就是如何实现虚线和布局动态高度变化同步,通过鸿蒙原生组件提供的方法可以实现。