HarmonyOS鸿蒙Next中悬浮窗拖动与悬浮窗内的Web组件事件冲突如何解决

HarmonyOS鸿蒙Next中悬浮窗拖动与悬浮窗内的Web组件事件冲突如何解决

【问题现象】

加载HarmonyOS文档的悬浮窗可以拖动,但是不能点击Web组件里的按钮。

下文将以Row组件和Web组件嵌套举例:给Row组件添加自定义拖动手势,Web组件不添加自定义手势,但是有触摸事件

预期效果: 既可以拖动Row组件,又能点击Web组件中的按钮。

实际效果: 可以拖动窗口,但是点击Web组件中的按钮没反应。

点击放大

问题代码如下:

build() {
    Row() {
        // 定义Web
        Web({ src: 'https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-web',
          controller: this.controller
        })
          .onPageEnd(()=>{
            this.controller.setScrollable(false)
          })
          .width(CommonConstants.DEFAULT_WINDOW_SIZE1).height(CommonConstants.DEFAULT_WINDOW_SIZE1)
    }
    .backgroundColor(Color.Transparent)

     // 自身响应触摸测试
    .hitTestBehavior(HitTestMode.Block)

     // 移动手势
    .gesture(
      PanGesture(this.panOption)
        .onActionStart((event: GestureEvent) => {})
        .onActionUpdate((event: GestureEvent) => {
          this.windowPosition.x += event.offsetX;
          this.windowPosition.y += event.offsetY;
          let top = CommonConstants.DEFAULT_HEIGHT;
          let bottom = display.getDefaultDisplaySync().height - this.subWindow.getWindowProperties().windowRect.height - top;
          if (this.windowPosition.y < top) {
            this.windowPosition.y = top;
          } else if (this.windowPosition.y > bottom) {
            this.windowPosition.y = bottom;
          }
          this.subWindow.moveWindowTo(this.windowPosition.x, this.windowPosition.y);
        })
        .onActionEnd((event: GestureEvent) => {
          if (this.windowPosition.x >= display.getDefaultDisplaySync().width / 2) {
            this.windowPosition.x = display.getDefaultDisplaySync().width -
              this.subWindow.getWindowProperties().windowRect.width;
          } else if (event.offsetX < display.getDefaultDisplaySync().width / 2) {
            this.windowPosition.x = 0;
          }
          let top = CommonConstants.DEFAULT_HEIGHT;
          let bottom = display.getDefaultDisplaySync().height - this.subWindow.getWindowProperties().windowRect.height
            - top;
          if (this.windowPosition.y < top) {
            this.windowPosition.y = top;
          } else if (this.windowPosition.y > bottom) {
            this.windowPosition.y = bottom;
          }
          this.subWindow.moveWindowTo(this.windowPosition.x, this.windowPosition.y);
        }),
    )
 }

【背景知识】

在HarmonyOS开发中,触摸事件(onTouch事件)是用户与设备交互的基础,也是所有手势事件组成的基础,手势均由触摸事件组成。当组件被父布局(Stack、Row等任何容器组件)封装住时,对于父、子组件设置的响应手势极容易发生冲突。

【定位思路】

当前手势绑定在了Row组件上,而Web组件位于Row组件的内部,整体结构如下图所示:

点击放大

不难看出是最外层组件,B是A的子组件,两个组件叠加在一起,很容易出现手势冲突问题。

因此需排查以下几点内容:

(1)父组件手势优先级高于子组件

父组件具有自定义手势,方法为gesture,子组件未设置自定义手势,但是具有系统级的触摸事件。从手势优先级上来说,一定是子组件高于父组件,正常响应情况应该为:Web组件能响应触控事件,而Row组件失去了拖动响应。而非错误现象表现出的仅有父组件有触控响应,因此不是优先级设置问题。

(2)阻塞触摸事件

根据问题代码可知,Row组件为了提高自己的优先级绑定了方法hitTestBehavior(HitTestMode.Block),该方法会阻塞子节点进行触摸测试,因此内层Web组件不会响应onTouch事件。

【解决方案】

根据上述思路,方法hitTestBehavior(HitTestMode.Block)会使子组件的onTouch事件被阻塞,因此不能使用该方法。

代码示例如下:

Row() {
  Web({
    ...
  })
    ...
}
// .hitTestBehavior(HitTestMode.Transparent)
.gesture(
  ...
)

GestureMask.IgnoreInternal则会屏蔽子组件的手势,而非触碰事件。如子组件为List组件时,内置的滑动手势同样会被屏蔽。 若父子组件区域存在部分重叠,则只会屏蔽父子组件重叠的部分。

因此当前场景下应该使用GestureMask.IgnoreInternal方法。

代码示例如下:

.gesture(
  PanGesture(this.panOption)
  // ...
  GestureMask.IgnoreInternal
)

处理效果:

点击放大

【总结】

在复杂的应用界面中,多个组件嵌套时同时绑定手势事件,或者同一个组件同时绑定多个手势,都有可能导致手势事件产生冲突,达不到用户的预期效果,因此要分析好事件响应链,排布好手势响应的优先级。

手势分为系统手势自定义手势两类,一般情况下,除非显式声明允许多个手势同时成功,否则同一时间只会有一个手势响应。

常见的手势冲突问题还有:滚动容器嵌套滚动容器事件冲突系统手势和自定义手势之间冲突使用组合手势同时绑定多个同类型手势冲突等等。

下面将举例和Web相关的另一个常见冲突问题:ListScroller等滚动容器嵌套Web组件导致滑动事件冲突。

解决方案:

使用nestedScroll属性设置Web组件的嵌套滚动方式,NestedScrollMode设置成SELF_FIRST时,Web组件滚动到页面边缘后,父组件继续滚动。NestedScrollMode设置为PARENT_FIRST时,父组件先滚动,滚动至边缘后通知Web组件继续滚动。

Web(...)
.nestedScroll({
  scrollForward: NestedScrollMode.PARENT_FIRST,
  scrollBackward: NestedScrollMode.SELF_FIRST
})

Web组件嵌套滚动:Web组件嵌套滚动的典型应用场景为,在一个页面中,有多个独立的区域需要进行滚动,当用户滚动Web区域内容时,可带动其他滚动区域进行滚动,以达到上下滑动页面的用户体验。

更多常见手势冲突问题请见链接内容。


更多关于HarmonyOS鸿蒙Next中悬浮窗拖动与悬浮窗内的Web组件事件冲突如何解决的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙Next中悬浮窗拖动与悬浮窗内的Web组件事件冲突如何解决的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,悬浮窗拖动与悬浮窗内的Web组件事件冲突,可以通过以下方式解决:

  1. 移除hitTestBehavior(HitTestMode.Block):该方法会阻塞子组件的触摸事件,导致Web组件无法响应点击事件。

  2. 使用GestureMask.IgnoreInternal:在Row组件的gesture方法中添加GestureMask.IgnoreInternal,以屏蔽子组件的手势,但不影响子组件的触摸事件。

代码示例:

.gesture(
  PanGesture(this.panOption)
  // ...
  GestureMask.IgnoreInternal
)
  1. 设置Web组件的嵌套滚动方式:如果Web组件嵌套在滚动容器中,使用nestedScroll属性设置滚动方式,避免滑动事件冲突。

代码示例:

Web(...)
.nestedScroll({
  scrollForward: NestedScrollMode.PARENT_FIRST,
  scrollBackward: NestedScrollMode.SELF_FIRST
})

通过以上调整,可以实现悬浮窗的拖动功能,同时确保Web组件内的按钮能够正常响应点击事件。

回到顶部