写点什么

【每日学点 HarmonyOS Next 知识】自定义对话框关闭、WaterFlow 嵌套、状态栏颜色、滚动吸附、滚动动效

作者:轻口味
  • 2025-03-13
    北京
  • 本文字数:8584 字

    阅读完需:约 28 分钟

1、HarmonyOS 自定义对话框自动关闭的问题?

启动页做了个隐私协议弹窗,autoCancel 为 false。UI 中使用 Text() ContainerSpan() Span()组件,设置了点击事件,点击后使用 router.pushUrl()的方法跳转到协议页面。点击时,对话框消失了同时页面进行了跳转。点击事件中没有调用 close 操作。


使用 Stack 组件模拟实现 Dialog 的效果,参考下以下 demo:


import router from '@ohos.router';
@Entry@Componentstruct First { @State textValue: string = 'Hello World' // 显隐控制设置为不占用 @State visible: Visibility = Visibility.None
build() { // 使用stack可以实现假的dialog覆盖原页面上面 Stack() { Row() { // 初始页面 Column() { Text('Hello World') .fontSize(50) .fontWeight(FontWeight.Bold) // 触发dialog的地方 Button('click') .onClick(() => { console.log("hit me!") if (this.visible == Visibility.Visible) { this.visible = Visibility.None } else { this.visible = Visibility.Visible } }) .backgroundColor(0x777474) .fontColor(0x000000) } .width('100%') } .height('100%') .backgroundColor(0x885555) //这里开始是构造弹窗效果主要需要修改的地方,首先是加了一个半透明灰色的蒙层效果 Text('') .onClick(() => { if (this.visible == Visibility.Visible) { this.visible = Visibility.None } else { this.visible = Visibility.Visible } }) .width('100%') .height('100%') // 透明度可以自己调节 .opacity(0.16) .backgroundColor(0x000000) .visibility(this.visible)
Column() { // 这个可以调节对话框效果,栅格布局,xs,sm,md,lg分别为四种规格 // 下面的breakpoints是用来区别当前属于哪个类型的断点 // gridRow里的栅格数量为总数,gridCol里的就是偏移和假Dialog所占据的栅格数 GridRow({ columns:{xs:1 ,sm: 4, md: 8, lg: 12}, breakpoints: { value: ["400vp", "600vp", "800vp"], reference: BreakpointsReference.WindowSize }, }) { GridCol({ span:{xs:1 ,sm: 2, md: 4, lg: 8}, offset:{xs:0 ,sm: 1, md: 2, lg: 2} }){
// 这里放的就是原Dialog里的column里的东西,稍微改改应该就可以用了 Column() { Text('Change text').fontSize(20).margin({ top: 10, bottom: 10 }) TextInput({ placeholder: '', text: this.textValue }).height(60).width('90%') .onChange((value: string) => { this.textValue = value }) Text('Whether to change a text?').fontSize(16).margin({ bottom: 10 }) Flex({ justifyContent: FlexAlign.SpaceAround }) { Button('cancel') .onClick(() => { if (this.visible == Visibility.Visible) { this.visible = Visibility.None } else { this.visible = Visibility.Visible }
}).backgroundColor(0xffffff).fontColor(Color.Black) Button('jump') .onClick(() => { router.pushUrl({ url: 'pages/Second' }) }).backgroundColor(0xffffff).fontColor(Color.Red) }.margin({ bottom: 10 }) } .backgroundColor(0xffffff) .visibility(this.visible) .clip(true) .borderRadius(20) } } }.width('95%')//设置弹窗宽度 } }}
复制代码

2、HarmonyOS WaterFlow 嵌套问题?

WaterFlow 嵌套在父 List 控件中(利用的懒加载 LazyForEach 获取的数据,cachedCount 设置的是 5) 若 WaterFlow 的数据有 100 条当 List 滑动到 WaterFlow 的时候,这 100 条数据对应的 WaterFlowItem 都会加载出来(触发了 onAppear()),如果数据过多,如增长到 1000 条,这样是否会导致列表卡顿呢?是否有其他方法控制 预加载的 WaterFlowItem 的数量呢?


数据过多是不会导致列表卡顿的,关于控制预加载的 WaterFlowItem 数量问题 可以参考此链接,其中有详细介绍:https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/performance/waterflow_optimization.md

3、HarmonyOS setWindowSystemBarProperties 修改状态栏颜色?

调用系统 api setWindowSystemBarProperties 修改状态栏颜色 , APP 上滑动,状态栏还会是当前 app 设置的颜色,这种有修改状态栏颜色的需求,是调用系统 api,还是自己写组件


可以设置下窗口为全屏布局,不去设置 setWindowSystemBarProperties


mainWindowClass.setWindowLayoutFullScreen(true, (err: BusinessError) => {  let errCode: number = err.code;  if (errCode) {    console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));    return;  }  console.info('Succeeded in setting the window layout to full-screen mode.');});
复制代码

4、HarmonyOS 仿 AppBarLayout 吸附使用 Scroll 滚动到顶时出现抖动问题?

通过 onScroll 移动 scroll 上层的组件,实现吸附效果,在滚动到顶部的场景下出现抖动问题,使用 onWillScroll,onDidScroll 问题更明显


出现抖动是因为回到顶层使用了动画,然后 Text 没有动画效果,才会出现这样,这个目前吸顶的效果不建议在 scroll 的时候 margin,可以参考下这个 demo:


enum ScrollPosition {  start,  center,  end}
class ItemClass { content: string = ''; color: Color = Color.White;}
@Entry@Componentstruct NestedScrollDemo { @State listPosition: number = ScrollPosition.start; // 0代表滚动到List顶部,1代表中间值,2代表滚动到List底部。 @State scrollPosition: number = ScrollPosition.start; // 0代表滚动到页面顶部,1代表中间值,2代表滚动到页面底部。 @State showTitle: boolean = false; @State currentYOffset: number = 0; private arr: ItemClass[] = []; private colorArr: Color[] = [Color.White, Color.Blue, Color.Brown, Color.Green, Color.Gray]; private scrollerForScroll: Scroller = new Scroller(); private scrollerForList: Scroller = new Scroller(); private scrollerForTitle: Scroller = new Scroller(); @State currentIndex: number = 0;
aboutToAppear() { for (let i = 0; i < 6; i++) { let data: ItemClass = { content: i.toString(), color: this.colorArr[i % 5] } this.arr.push(data); } }
@Builder myBuilder() { Row() { List({ space: 2, initialIndex: 0, scroller: this.scrollerForTitle }) { ForEach(this.arr, (item: ItemClass, index) => { ListItem() { Column() { Text(item.content); Divider() .color('#000000') .strokeWidth(5) .visibility(index == this.currentIndex ? Visibility.Visible : Visibility.Hidden) } .width('25%') .height(50) .onClick(() => { this.scrollerForList.scrollToIndex(index) this.scrollerForScroll.scrollEdge(Edge.Bottom) }) } }) } .listDirection(Axis.Horizontal) .scrollBar(BarState.Off) } .backgroundColor('#ffe2d0d0') .alignItems(VerticalAlign.Center) }
build() { Stack({ alignContent: Alignment.Top }) { Scroll(this.scrollerForScroll) { Column() { Image($r('app.media.app_icon')) .width("100%") .height("40%") this.myBuilder();
List({ space: 10, scroller: this.scrollerForList }) { ForEach(this.arr, (item: ItemClass, index) => { ListItem() { Column() { Text(item.content) //添加其他内容 } .width('100%') .height(500) .backgroundColor(item.color) }.width("100%").height(500) .onVisibleAreaChange([0.8], (isVisible) => { if (isVisible) { this.currentIndex = index; this.scrollerForTitle.scrollToIndex(this.currentIndex); } }) }, (item: ItemClass) => item.content) } .padding({ left: 10, right: 10 }) .width("100%") .edgeEffect(EdgeEffect.None) .scrollBar(BarState.Off) .onReachStart(() => { this.listPosition = ScrollPosition.start }) .onReachEnd(() => { this.listPosition = ScrollPosition.end }) .onScrollFrameBegin((offset: number, state: ScrollState) => { // 滑动到列表中间时 if (!((this.listPosition == ScrollPosition.start && offset < 0) || (this.listPosition == ScrollPosition.end && offset > 0))) { this.listPosition = ScrollPosition.center }
// 如果页面已滚动到底部,列表不在顶部或列表有正向偏移量 if (this.scrollPosition == ScrollPosition.end && (this.listPosition != ScrollPosition.start || offset > 0)) { return { offsetRemain: offset }; } else { this.scrollerForScroll.scrollBy(0, offset) return { offsetRemain: 0 }; } }) .width("100%") .height("calc(100% - 50vp)") .backgroundColor('#F1F3F5') } } .scrollBar(BarState.Off) .width("100%") .height("100%") .onScroll((xOffset: number, yOffset: number) => { this.currentYOffset = this.scrollerForScroll.currentOffset().yOffset;
// 非(页面在顶部或页面在底部),则页面在中间 if (!((this.scrollPosition == ScrollPosition.start && yOffset < 0) || (this.scrollPosition == ScrollPosition.end && yOffset > 0))) { this.scrollPosition = ScrollPosition.center } }) .onScrollEdge((side: Edge) => { if (side == Edge.Top) { // 页面在顶部 this.scrollPosition = ScrollPosition.start } else if (side == Edge.Bottom) { // 页面在底部 this.scrollPosition = ScrollPosition.end } }) .onScrollFrameBegin(offset => { if (this.scrollPosition == ScrollPosition.end) { return { offsetRemain: 0 }; } else { return { offsetRemain: offset }; } }) } .width('100%') .height('100%') .backgroundColor(0xDCDCDC) }}
复制代码

5、HarmonyOS Grid 上下滑动的动效和需求的差距很大?

Grid 上下滑动的动效和需求的差距很大


参考 DEMO:


@Entry@Componentexport struct Page240605101412064_temp {  weekDatas: string[] = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];  // private startDay: Date = new Date(2024, 4, 1) // 当前显示的月份的第一天  data: Array<CalendaEntity> = [];  @State gridTop: number = 0  @State blankTop: number = 0  private endBlankY: number = 0  private endGridY: number = 0  private isStart: boolean = true  private scroller: Scroller = new Scroller()  private today: Date = new Date()  private startDay: Date = new Date(this.today.getFullYear(), this.today.getMonth(), 1)
aboutToAppear(): void { const monthDataWithWeekday = this.calcDatas(this.startDay) monthDataWithWeekday.forEach(item => { console.log("日期是》》》》》》》" + item.year, ' ', item.month, ' ', item.day, ' ', item.weekday); });
}
calcDatas(startDate: Date): Array<CalendaEntity> { const endDate = new Date(startDate.getFullYear(), startDate.getMonth() + 1, 0); let currentDate = new Date(startDate); while (currentDate <= endDate) { const weekday = currentDate.getDay(); const weekdayNames = [0, 1, 2, 3, 4, 5, 6]; const weekdayStr = weekdayNames[weekday]; let year = currentDate.getFullYear() let month = currentDate.getMonth() + 1 let day = currentDate.getDate().toString() if (day === this.today.getDate().toString()) { day = '今'; } this.data.push({ year, month, day, weekday: weekdayStr }); currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1) } if (this.data.length !== 0) { let firstWeek = this.data[0].weekday; let tempData: Array<CalendaEntity> = []; while (firstWeek > 0) { tempData.push(new CalendaEntity()); firstWeek--; } this.data = tempData.concat(this.data); let lastWeek = this.data[this.data.length - 1].weekday; while (lastWeek < 6) { this.data.push(new CalendaEntity()) lastWeek++; } } return this.data; }
build() { Column() { Row() { ForEach(this.weekDatas, (item: string) => { Text(item) .textAlign(TextAlign.Center) .fontColor('#9E9E9E') .fontSize(14) .layoutWeight(1) }) } // .backgroundColor() .alignItems(VerticalAlign.Center) .height(60)
Scroll(this.scroller) { Column() { Grid() { ForEach(this.data, (item: CalendaEntity) => { GridItem() { Stack() { Image("") .backgroundColor('#35cfba') .borderRadius(100) .width(34) .height(34) .visibility((item.day === "0" || item.day !== '今') ? Visibility.Hidden : Visibility.Visible) Column() { Text(item.day === "0" ? " " : item.day) .fontSize(14) .fontColor(Color.Gray) .width('14%') .textAlign(TextAlign.Center) Text() .backgroundColor(Color.Red) .width(4) .height(4) .borderRadius(50) .visibility(item.day === "0" ? Visibility.Hidden : Visibility.Visible) } } }.onClick(() => { console.log("dddddddddd"); }) }) }.rowsGap(20) // .columnsGap(10) .backgroundColor('#fff') .margin({ top: this.gridTop }) .onAreaChange((oldValue: Area, newValue: Area) => { console.log('h', newValue.height) // h 304 })

Blank().layoutWeight(1).backgroundColor(Color.Gray) .margin({ top: this.blankTop }) }.height('calc(100% + 560vp)') } .height('100%') .scrollBar(BarState.Off) .onReachStart(() => { this.isStart = true }) .onScrollFrameBegin((offset: number, state: ScrollState) => { console.log('offset', offset) if (this.isStart) { return { offsetRemain: 0 } } if (!this.scroller.currentOffset().yOffset) { this.isStart = false } return { offsetRemain: offset } }) .parallelGesture( PanGesture({ direction: PanDirection.Vertical, distance: 1 }) .onActionStart((event: GestureEvent) => { console.info('Pan start') }) .onActionUpdate((event: GestureEvent) => { console.log('event.offsetY', event.offsetY) console.log('this.isStart', this.isStart) if (event && this.isStart) { if (this.blankTop == 0) { if (this.endGridY == 0) { this.gridTop = this.endGridY + event.offsetY < -(304 / 6 * 2) ? -(304 / 6 * 2) : this.endGridY + event.offsetY > 0 ? 0 : this.endGridY + event.offsetY console.log('this.gridTop', this.gridTop) } else { this.gridTop = this.endBlankY + this.endGridY + event.offsetY < -(304 / 6 * 2) ? -(304 / 6 * 2) : this.endBlankY + this.endGridY + event.offsetY > 0 ? 0 : this.endBlankY + this.endGridY + event.offsetY console.log('this.gridTop', this.gridTop) }
} if (this.gridTop == -(304 / 6 * 2)) { if (this.endBlankY == 0) { this.blankTop = this.endBlankY + event.offsetY + (304 / 6 * 2) < -(304 / 6 * 3) ? -(304 / 6 * 3) : this.endBlankY + event.offsetY + (304 / 6 * 2) > 0 ? 0 : this.endBlankY + event.offsetY + (304 / 6 * 2) console.log('this.blankTop', this.blankTop) } else { this.blankTop = this.endBlankY + event.offsetY < -(304 / 6 * 3) ? -(304 / 6 * 3) : this.endBlankY + event.offsetY > 0 ? 0 : this.endBlankY + event.offsetY console.log('this.blankTop', this.blankTop) }
} } }) .onActionEnd((event: GestureEvent) => { this.endBlankY = this.blankTop this.endGridY = this.gridTop console.log('this.endBlankY', this.endBlankY) console.log('this.endGridY', this.endGridY) }) )
}.width('100%')
// .height(330)
}}
export class CalendaEntity { year: number = 0; month: number = 0; day: string = "0"; weekday: number = -1;}
export enum OptMode { WEEK = "week", MONTH = "month",}
复制代码


发布于: 刚刚阅读数: 4
用户头像

轻口味

关注

🏆2021年InfoQ写作平台-签约作者 🏆 2017-10-17 加入

Android、音视频、AI相关领域从业者。 欢迎加我微信wodekouwei拉您进InfoQ音视频沟通群 邮箱:qingkouwei@gmail.com

评论

发布
暂无评论
【每日学点HarmonyOS Next知识】自定义对话框关闭、WaterFlow嵌套、状态栏颜色、滚动吸附、滚动动效_HarmonyOS_轻口味_InfoQ写作社区