HarmonyOS鸿蒙Next中半模态框中使用列表组件,如何实现下滑滚动到顶部后,继续下滑可以下滑关闭模态框
HarmonyOS鸿蒙Next中半模态框中使用列表组件,如何实现下滑滚动到顶部后,继续下滑可以下滑关闭模态框 半模态框中使用列表组件,如何实现下滑滚动到顶部后,继续下滑可以下滑关闭模态框。
要求:
1.半模态框中的列表要有滚动条显示。
2.关闭效果和拖动模态框标题出关闭效果一样。
class ListDataSource implements IDataSource {
private list: number[] = [];
private listeners: DataChangeListener[] = [];
constructor(list: number[]) {
this.list = list;
}
totalCount(): number {
return this.list.length;
}
getData(index: number): number {
return this.list[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
}
@Entry
@Component
struct bindSheetExample {
@State isShow: boolean = false;
private arr: ListDataSource = new ListDataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
@Builder
myBuilder() {
Column() {
List({ space: 20, initialIndex: 0 }) {
LazyForEach(this.arr, (item: number) => {
ListItem() {
Text('' + item)
.width('100%').height(100).fontSize(16)
.textAlign(TextAlign.Center).borderRadius(10).backgroundColor(Color.Blue)
}
}, (item: number) => item.toString())
}
.scrollBar(BarState.On)
// .layoutWeight(1)
// .width('100%')
.height('100%')
}
}
build() {
Column() {
Button("xxxxx")
.onClick(() => {
this.isShow = true;
})
.fontSize(20)
.margin(10)
.bindSheet($$this.isShow, this.myBuilder(), {
height: SheetSize.MEDIUM,
blurStyle: BlurStyle.Thick,
showClose: true,
title: { title: "xxxxx"},
preferType: SheetType.CENTER,
keyboardAvoidMode: SheetKeyboardAvoidMode.RESIZE_ONLY,
})
}
}
}
更多关于HarmonyOS鸿蒙Next中半模态框中使用列表组件,如何实现下滑滚动到顶部后,继续下滑可以下滑关闭模态框的实战教程也可以访问 https://www.itying.com/category-93-b0.html
设置嵌套滚动模式,当列表滚动到顶部继续下滑时,手势事件会传递给父级半模态组件
List({ space: 20, initialIndex: 0 }) {
// LazyForEach内容
}
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
配置滚动条显示
.scrollBar(BarState.On) // 已设置
.edgeEffect(EdgeEffect.None) // 禁用默认边缘效果
代码优化:
@Builder
myBuilder() {
Column() {
List({ space: 20, initialIndex: 0 }) {
LazyForEach(this.arr, (item: number) => {
ListItem() {
Text('' + item)
.width('100%').height(100).fontSize(16)
.textAlign(TextAlign.Center).borderRadius(10).backgroundColor(Color.Blue)
}
}, (item: number) => item.toString())
}
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST, // 上滑父容器优先
scrollBackward: NestedScrollMode.SELF_FIRST // 下滑列表优先
})
.scrollBar(BarState.On)
.edgeEffect(EdgeEffect.None)
.height('100%')
}
.width('100%')
.height('100%') // 确保父容器高度充满
}
更多关于HarmonyOS鸿蒙Next中半模态框中使用列表组件,如何实现下滑滚动到顶部后,继续下滑可以下滑关闭模态框的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在List中加入
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
.edgeEffect(EdgeEffect.None) // 禁用边缘效果(避免与半模态冲突)
完整代码
@Entry
@Component
struct bindSheetExample {
@State isShow: boolean = false;
private arr: ListDataSource = new ListDataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
@Builder
myBuilder() {
Column() {
List({ space: 20, initialIndex: 0 }) {
LazyForEach(this.arr, (item: number) => {
ListItem() {
Text('' + item)
.width('100%').height(100).fontSize(16)
.textAlign(TextAlign.Center).borderRadius(10).backgroundColor(Color.Blue)
}
}, (item: number) => item.toString())
}
.scrollBar(BarState.On)
///加入如下代码
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
.edgeEffect(EdgeEffect.None) // 禁用边缘效果(避免与半模态冲突)
///结束
// .layoutWeight(1)
// .width('100%')
.height('100%')
}
}
}
就是你想要的那种效果
【背景知识】
【解决方案】 可以使用onTouch来监听:
class ListDataSource implements IDataSource {
private list: number[] = [];
private listeners: DataChangeListener[] = [];
constructor(list: number[]) {
this.list = list;
}
totalCount(): number {
return this.list.length;
}
getData(index: number): number {
return this.list[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
}
@Entry
@Component
struct bindSheetExample {
@State isShow: boolean = false;
private arr: ListDataSource = new ListDataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
@State isStart: boolean = false;
private touchStartY: number = 0; // 触摸起始位置
@Builder
myBuilder() {
Column() {
List({ space: 20, initialIndex: 0 }) {
LazyForEach(this.arr, (item: number) => {
ListItem() {
Text('' + item)
.width('100%').height(100).fontSize(16)
.textAlign(TextAlign.Center).borderRadius(10).backgroundColor(Color.Blue)
}
}, (item: number) => item.toString())
}
.edgeEffect(EdgeEffect.None)
.onReachStart(()=>{
this.isStart = true
})
.onTouch((event: TouchEvent)=>{
const currentY = event.touches[0].y;
switch (event.type) {
case TouchType.Down:
this.touchStartY = currentY;
break;
case TouchType.Move:
// 记录移动的方向,正值为下滑,负值为上滑
const deltaY = currentY - this.touchStartY;
if(this.isStart&&deltaY>0){
this.isShow = false
}else{
this.isStart = false
}
break;
case TouchType.Up:
break;
}
})
.scrollBar(BarState.On)
.height('100%')
}
}
build() {
Column() {
Button("xxxxx")
.onClick(() => {
this.isShow = true;
this.isStart = true;
})
.fontSize(20)
.margin(10)
.bindSheet($$this.isShow, this.myBuilder(), {
height: SheetSize.MEDIUM,
blurStyle: BlurStyle.Thick,
showClose: true,
title: { title: "xxxxx"},
preferType: SheetType.CENTER,
keyboardAvoidMode: SheetKeyboardAvoidMode.RESIZE_ONLY,
})
}
}
}
【背景知识】
- Scroll可滚动的容器组件,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。
- currentOffset获取Scroll当前的偏移量。
- isAtEnd查询Scroll是否滚动到底部。
- PanGesture可以为组件绑定滑动手势事件,其中手势回调的event.offsetY在从上向下滑动为正,反之为负。
【解决方案】
class ListDataSource implements IDataSource {
private list: number[] = [];
private listeners: DataChangeListener[] = [];
constructor(list: number[]) {
this.list = list;
}
totalCount(): number {
return this.list.length;
}
getData(index: number): number {
return this.list[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
}
@Entry
@Component
struct bindSheetExample {
@State isShow: boolean = false;
private arr: ListDataSource = new ListDataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Vertical })
private scrollerForScroll: Scroller = new Scroller()
@Builder
myBuilder() {
Column() {
Scroll(this.scrollerForScroll){
List({ space: 20, initialIndex: 0 }) {
LazyForEach(this.arr, (item: number) => {
ListItem() {
Text('' + item)
.width('100%').height(100).fontSize(16)
.textAlign(TextAlign.Center).borderRadius(10).backgroundColor(Color.Blue)
}
}, (item: number) => item.toString())
}
.scrollBar(BarState.On)
}
.edgeEffect(EdgeEffect.Spring)
.height('100%')
.parallelGesture(
PanGesture(this.panOption)
.onActionStart((event: GestureEvent) => {
console.info('Pan start')
})
.onActionUpdate((event: GestureEvent) => {
// 滑动过程中会持续触发此方法
})
.onActionEnd((event: GestureEvent) => {
// 滑动到底部并且继续滑动
if (this.scrollerForScroll.isAtEnd() && event.offsetY < 0) {
this.isShow=false
}
// 滑动到顶部并且继续滑动
const offsetRes = this.scrollerForScroll.currentOffset() // 获取Scroll现在的位置
if (offsetRes.yOffset <= 0 && event.offsetY > 0) {
console.info("执行自定义操作2")
}
}), GestureMask.Normal)
}
}
build() {
Column() {
Button("xxxxx")
.onClick(() => {
this.isShow = true;
})
.fontSize(20)
.margin(10)
.bindSheet($$this.isShow, this.myBuilder(), {
height: SheetSize.MEDIUM,
blurStyle: BlurStyle.Thick,
showClose: true,
title: { title: "xxxxx"},
preferType: SheetType.CENTER,
keyboardAvoidMode: SheetKeyboardAvoidMode.RESIZE_ONLY,
})
}
}
}
这个只能通过平行手势去自己适配么?这样实现会比较复杂且会有问题,offsetRes.yOffset小于0是会在滚动页面中下拉出空白,会导致这个模态框的下滑关闭不是跟手的。
想实现的效果是滚动内容,手势下滑,页面滚动顶部后,在继续下滑,整个模态框是跟手改变大小关闭。
就和模态框中没有实现滚动组件时,下滑跟手关闭的效果一样,
楼主你好,已在最新楼层答复,麻烦看下最新楼层回帖内容,
在HarmonyOS Next中,半模态框内使用列表组件时,要实现下滑滚动到顶部后继续下滑关闭模态框,可以通过监听列表的滚动事件实现。当列表滚动到顶部时,将滚动事件传递给半模态框的关闭手势。具体可使用Scroll组件的onScroll事件,结合scrollEdge属性判断是否到达顶部。到达顶部后,触发模态框的onDismiss或相关关闭交互。
在HarmonyOS Next中,要实现半模态框内列表滚动到顶部后继续下滑关闭模态框的效果,可以通过监听列表的滚动事件来实现。以下是关键实现步骤:
-
监听列表滚动状态: 使用
List组件的onScroll事件监听滚动位置。当滚动到顶部(scrollOffset为0)且继续下滑时,触发模态框关闭。 -
控制模态框关闭: 通过
[@State](/user/State)变量控制模态框显示状态,结合手势下滑距离动态调整模态框位置或直接关闭。 -
示例代码修改: 在
List组件中添加onScroll事件,判断滚动到顶部后的下滑操作,并触发isShow状态改变以关闭模态框。[@State](/user/State) scrollOffset: number = 0; [@State](/user/State) isAtTop: boolean = false; List({ space: 20, initialIndex: 0 }) { // LazyForEach内容 } .onScroll((scrollOffset: number, scrollState: ScrollState) => { this.scrollOffset = scrollOffset; // 判断是否滚动到顶部 if (scrollOffset <= 0) { this.isAtTop = true; } else { this.isAtTop = false; } }) .onScrollEnd((scrollOffset: number) => { // 滚动到顶部后继续下滑,关闭模态框 if (this.isAtTop && scrollOffset < 0) { this.isShow = false; } }) -
手势联动优化: 若需更流畅的拖动关闭效果,可结合
PanGesture手势事件,在列表顶部时将下滑手势传递到模态框,但需注意手势冲突处理。
注意:直接通过onScrollEnd判断可能不够精确,可考虑使用Scroll组件的嵌套或自定义手势管理来实现更细腻的控制。列表滚动条通过.scrollBar(BarState.On)已满足要求。

