HarmonyOS鸿蒙Next中单次拖动无法既实现List组件滚动又使得进度条变化的问题如何处理

HarmonyOS鸿蒙Next中单次拖动无法既实现List组件滚动又使得进度条变化的问题如何处理

【问题现象】

当触摸进度条所在位置时,手指不抬起进行拖动操作,若拖动操作起始为上下,那么后续的拖动只会影响List列表的滚动(无论手指后续拖动方向)。相反,若拖动操作起始为左右,那么后续拖动只会影响进度条的变化,无法影响列表滚动。效果持续至手指抬起。

【背景知识】

目前,有大量页面需要在单次手势操作下,进行多方向的移动,涉及多个组件,目前版本对于点击(按下+抬起)、拖拽等非触摸(Touch)互动事件,仅有nestedScroll支持在单方向(垂直或者水平)拖动事件的透传,其他情况下均不支持透传,若组件识别该即消费。且滑动组件(Slider)不支持nestedScroll,因此需要介入拖动手势(PanGesture)。

【定位思路】

由背景知识可知,使用拖动手势可以根据手指在每个方向上的移动距离来影响各个方向。后续仅需解决手势绑定在哪个组件上,以及如何实现手势内部参数。

  1. 手势绑定的组件

    只有当手指在Slider上拖动时,才会涉及到影响两个方向的不同组件,因此,将手势绑定在Slider上。此时方便拖动对影响哪一个Slider做判断以及不影响其他组件的点击拖动,降低耦合度,以及系统性能消耗。

  2. 内部实现手势参数

    通过scrollBy影响外部滑动组件。

    通过value影响内部进度条组件。

具体实现以及效果见解决方案。

【解决方案】

即在进度条上绑定拖动手势,注意此时需要将Slider通过额外的容器进行包裹且Slider本身通过enabled或者hitTestBehavior.None取消掉本身组件对于手势的消费,手势绑定在容器上。手势需额外绑定的原因为当组件消费手势,组件额外绑定的手势将不生效,因此需要将手势透传到父组件进行处理,通过定位思路中的内部手势参数即可获得对应的调控接口,实现代码如下:

@Entry
@Component
struct Index {
  dataArr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 171, 172, 173, 174];
  @State eventType: string = ''
  @State sliderNum: number[] = []
  @State offsetX: number = 0
  @State offsetY: number = 0
  scroller: Scroller = new Scroller()

  aboutToAppear(): void {
    // 初始化进度条进度参数
    for (let i = 0; i < this.dataArr.length; i++) {
      this.sliderNum[i] = 0
    }
  }

  build() {
    Scroll() {
      WaterFlow({ scroller: this.scroller }) {
        ForEach(this.dataArr, (item: number, index: number) => {
          FlowItem() {
            Column() {
              Slider({
                value: this.sliderNum[index],
                min: 0,
                max: 100,
                style: SliderStyle.OutSet
              })
                .enabled(false) // 取消进度条交互,防止消费拖拽事件
            }
            .hitTestBehavior(HitTestMode.Block)
            .gesture(
              PanGesture()
                .onActionStart((event: GestureEvent) => {
                  this.offsetX = 0
                  this.offsetY = 0
                })
                .onActionUpdate((event: GestureEvent) => { // 见解决方案注意第二条
                  if (event) {
                    if (event.offsetY != 0) {
                      this.scroller.scrollBy(0, -px2vp(event.offsetY - this.offsetY))
                      this.offsetY = event.offsetY
                    }
                    if (event.offsetX != 0) {
                      this.sliderNum[index] += px2vp(event.offsetX - this.offsetX)
                      this.offsetX = event.offsetX
                    }
                  }
                })
                .onActionEnd((event: GestureEvent) => {
                  console.info('Pan end')
                })
            )
          }
          .height(100)
        })
      }
      .columnsTemplate("1fr 1fr")
      .columnsGap(10)
      .rowsGap(5)
      .backgroundColor(0xFAEEE0)
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .width('100%')
  }
}

注意

  1. 手势不要绑定在List上或者List父组件上,会增加对控制哪一个进度条的判断的问题。
  2. 拖动为实时拖动,因此每次拖动的参数需要用当前拖动距离与上次拖动距离做减法。
  3. 建议将拖动距离进行进制转换。

【总结】

对于非触摸的交互事件,在第一次被组件消费掉之后就不会再继续往下透传,因此若想单次拖动影响多个组件,需使用手势接口,识别手势后再影响组件。


更多关于HarmonyOS鸿蒙Next中单次拖动无法既实现List组件滚动又使得进度条变化的问题如何处理的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙Next中单次拖动无法既实现List组件滚动又使得进度条变化的问题如何处理的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,若需实现单次拖动既滚动List组件又改变进度条,可通过以下步骤解决:

  1. 手势绑定:将PanGesture手势绑定在Slider的父容器上,而非Slider本身。通过enabled(false)hitTestBehavior.None取消Slider对手势的消费,确保手势能透传到父容器。

  2. 手势处理:在onActionUpdate回调中,根据手势的offsetXoffsetY分别更新SlidervalueList的滚动位置。使用scrollBy控制List滚动,value控制Slider进度。

  3. 实时更新:每次拖动时,将当前拖动距离与上次拖动距离做差,确保实时更新。

通过上述方式,可实现在单次拖动中同时影响List滚动和Slider进度条变化。

回到顶部