【每日学点 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
@Component
struct 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
@Component
struct 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
@Component
export 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",
}
版权声明: 本文为 InfoQ 作者【轻口味】的原创文章。
原文链接:【http://xie.infoq.cn/article/dbffe670fbd7d7b0de2ec98d9】。文章转载请联系作者。


轻口味
🏆2021年InfoQ写作平台-签约作者 🏆 2017-10-17 加入
Android、音视频、AI相关领域从业者。 欢迎加我微信wodekouwei拉您进InfoQ音视频沟通群 邮箱:qingkouwei@gmail.com
评论