HarmonyOS中的半模态转场原先由Pannel组件提供该能力,但通过官方文档可知,从API Version 12 开始,该组件不再维护,推荐使用通用属性bindSheet。当然,本篇文章探讨的是列表项中的半模态转场问题。
开发环境
- HarmonyOS NEXT Developer Beta3 SDK,基于OpenHarmony SDK Ohos_sdk_public 5.0.0.36 (API Version 12 Beta3)
- DevEco Studio NEXT Developer Beta3
需求
一个列表项(以List+ListItem+ForEach实现),每一项包含一个按钮,点击按钮弹出模态框,展示每一项的详细信息。
解决方案
Pannel实现
@Entry
@Component
struct PanelExample {
@State show: boolean = false
build() {
Column() {
List(){
ForEach([1, 2, 3, 4, 5], (item: number) => {
ListItem() {
Image($r('app.media.ic_public_add_norm_filled'))
.width(18)
.onClick((event: ClickEvent) => {
this.show = true
})
}
})
}
Panel(this.show) { // 展示日程
Column() {
// 自定义内容
}
}
.type(PanelType.Foldable)
.mode(PanelMode.Half)
.dragBar(true) // 默认开启
.halfHeight(500) // 默认一半
.showCloseIcon(true) // 显示关闭图标
.onChange((width: number, height: number, mode: PanelMode) => {
console.info(`width:${width},height:${height},mode:${mode}`)
})
}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })
}
}
bindSheet属性实现
@Entry
@Component
struct PanelExample {
@State show: boolean = false
@Builder
sheetBuilder() {
Column() {
Text('测试').fontSize(18)
}
}
build() {
List({ space: 4 }) {
ForEach([1, 2, 3, 4, 5], (item: number) => {
ListItem() {
Image($r('app.media.ic_public_add_norm_filled'))
.fillColor($r('app.color.primary_color'))
.width(18)
.onClick((event: ClickEvent) => {
this.isShowSheet = true
})
.bindSheet($$this.show, this.sheetBuilder(), {
detents: ['94%'],
backgroundColor: Color.White,
blurStyle: BlurStyle.Thick,
showClose: false,
})
}
})
}
}
}
问题分析
Panel
组件自然可以实现需求,反观bindSheet
属性不出意外得出意外了。经测试,点击按钮会弹出很多模态框,再点击关闭按钮或者遮罩,只有最顶层的模态框被关闭了。代码取自官方文档示例,但为什么会产生bug呢?其实产生这个bug的矛头来自于列表项
。比如列表项包含3条记录,代码中我们给每一条记录的按钮都绑定了bindSheet
,但我们只用了一个状态布尔值show
来控制模态框的显示和隐藏,因此点击按钮将this.show=true
等同于展示所有绑定的模态框。
解决方案
-
为了解决这个问题,我们可以定义三个变量
show1
、show2
、show3
分别控制列表项模态框的显示和隐藏。不过显然这不是一个明智的选择,如果列表项变多,显然无法满足我们的需求。 -
类似于
JavaScript事件委托-为父级添加监听并作用到子级
,可以将bindSheet
绑定到最外层的List
组件中,这样就完美地实现了需求,代码如下:@Entry @Component struct PanelExample { @State show: boolean = false @Builder sheetBuilder() { Column() { Text('测试').fontSize(18) } } build() { List({ space: 4 }) { ForEach([1, 2, 3, 4, 5], (item: number) => { ListItem() { Image($r('app.media.ic_public_add_norm_filled')) .fillColor($r('app.color.primary_color')) .width(18) .onClick((event: ClickEvent) => { this.isShowSheet = true }) } }) } .bindSheet($$this.show, this.sheetBuilder(), { detents: ['94%'], backgroundColor: Color.White, blurStyle: BlurStyle.Thick, showClose: false, }) } }