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

8 回复

设置嵌套滚动模式,当列表滚动到顶部继续下滑时,手势事件会传递给父级半模态组件

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%')

    }
  }
}

就是你想要的那种效果

【背景知识】

  • List:List是用来显示列表的组件,包含一系列相同宽度的列表项,适合连续、多行地呈现同类数据。
  • onTouch:手指触摸动作触发该回调。鼠标左键按下时对应的事件也会转化成触摸事件并触发该回调。

【解决方案】 可以使用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可滚动的容器组件,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。
  • 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中,要实现半模态框内列表滚动到顶部后继续下滑关闭模态框的效果,可以通过监听列表的滚动事件来实现。以下是关键实现步骤:

  1. 监听列表滚动状态: 使用List组件的onScroll事件监听滚动位置。当滚动到顶部(scrollOffset为0)且继续下滑时,触发模态框关闭。

  2. 控制模态框关闭: 通过[@State](/user/State)变量控制模态框显示状态,结合手势下滑距离动态调整模态框位置或直接关闭。

  3. 示例代码修改: 在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;
      }
    })
    
  4. 手势联动优化: 若需更流畅的拖动关闭效果,可结合PanGesture手势事件,在列表顶部时将下滑手势传递到模态框,但需注意手势冲突处理。

注意:直接通过onScrollEnd判断可能不够精确,可考虑使用Scroll组件的嵌套或自定义手势管理来实现更细腻的控制。列表滚动条通过.scrollBar(BarState.On)已满足要求。

回到顶部