HarmonyOS鸿蒙Next中创建悬浮窗之后,设置setWindowTouchable(false)之后,悬浮窗上的按钮也不响应

HarmonyOS鸿蒙Next中创建悬浮窗之后,设置setWindowTouchable(false)之后,悬浮窗上的按钮也不响应 代码如下:

创建悬浮窗代码

onCreateShareWind() {
  // 1.创建悬浮窗。
  let windowClass: window.Window | null = null;
  let config: window.Configuration = {
    name: 'shareWindow', windowType: window.WindowType.TYPE_FLOAT, ctx: this.getUIContext().getHostContext()
  };
  window.createWindow(config, (err, data) => {
    let errCode: number = err.code;
    if (errCode) {
      Logger.error(tag, `Failed to create the shareWindow. Cause: ${JSON.stringify(err)}`);
      return;
    }
    windowClass = data;
    // 2.悬浮窗窗口创建成功后,设置悬浮窗的位置、大小及相关属性等。
    try {
      let displayClass = display.getDefaultDisplaySync();
      windowClass.moveWindowTo(0, 0).catch(() => {
        // TODO: Implement error handling.
      });
      windowClass.resize(displayClass.width, displayClass.height).catch(() => {
        // TODO: Implement error handling.
      });
    } catch (error) {
      // TODO: Implement error handling.
    }

    windowClass.setWindowTouchable(false).catch(() => {
      // TODO: Implement error handling.
    });
    windowClass.setWindowFocusable(false).catch(() => {
      // TODO: Implement error handling.
    }); // 禁止获焦


    // 3.为悬浮窗加载对应的目标页面。
    windowClass.setUIContent('pages/SharePage', (err1) => {
      let errCode: number = err1.code;
      if (errCode) {
        Logger.error(tag, `Failed to load the content. Cause: ${JSON.stringify(err1)}`);
        return;
      }

      try {
        (windowClass as window.Window).setWindowBackgroundColor('#00000000');
      } catch (error) {
        // TODO: Implement error handling.
      }

      ConfService.closeContext?.hideAbility().catch(() => {
        // TODO: Implement error handling.
      });

      // 3.显示悬浮窗。
      (windowClass as window.Window).showWindow().catch(() => {
      });
    });
  });
}

页面代码

build() {
  Stack({ alignContent: Alignment.Top }) {
    Row() {
      Text('正在共享屏幕')
      Button($r('app.string.StopSharing'))
        .onClick(() => {
          Logger.info(tag, `StopSharing`)
        })
    }
    .backgroundColor(Color.White)
  }
  .height('100%')
  .width('100%')
  .borderWidth( 5)
  .enabled(this.isEnabled)
  .borderColor(Color.Red)
}

我想实现的是全屏悬浮窗,窗口上按钮可以响应事件,其他区域不响应,能透传到其他应用窗口。

目前问题:

其他应用窗口可以响应,但按钮点击没有任何反应


更多关于HarmonyOS鸿蒙Next中创建悬浮窗之后,设置setWindowTouchable(false)之后,悬浮窗上的按钮也不响应的实战教程也可以访问 https://www.itying.com/category-93-b0.html

11 回复

开发者你好,

设置了setWindowTouchable及setWindowFocusable之后,就代表窗口不可获焦不可触摸,不会响应任何事件。窗口要么可触摸要么不可触摸,如果设置了窗口不可触摸不可获焦,则不响应任何事件是正常情况。可使用两个窗口的实现对应效果。

更多关于HarmonyOS鸿蒙Next中创建悬浮窗之后,设置setWindowTouchable(false)之后,悬浮窗上的按钮也不响应的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


希望后续能够改进,能够真正解决我的诉求

尊敬的开发者,您好!麻烦您提供以下需求信息:

请问您是在什么样的业务场景中使用该能力,交互流程是怎样的,在哪一个环节遇到了问题?另外请您说明能力不满足可能带来的影响:什么时间用到?是否高频?有无三方库可以做到?若提供该能力,是否会造成大工作量返工?请您注意提供的内容不要包含您或第三方的非公开信息,如给您带来不便,敬请谅解。

核心结论

setWindowTouchable(false) = 整个悬浮窗完全不接收触摸 → 按钮必然失效你要的效果是:悬浮窗全屏 + 背景穿透 + 只有按钮能点击

正确方案只有一种:

不要用 setWindowTouchable (false),改用 UI 属性:hitTestBehavior: HitTestMode.HIT_TEST_NONE


一、修复步骤(100% 解决)

1. 删除这两行(关键!)

ts

// 删掉这两行!!!
windowClass.setWindowTouchable(false)
windowClass.setWindowFocusable(false)

2. 悬浮窗保持默认:可触摸、可点击

ts

// 什么都不用写,默认就是 touchable: true

3. 在你的悬浮窗页面根节点,设置:

ts

.hitTestBehavior(HitTestMode.HIT_TEST_NONE)

作用:根布局不接收触摸,触摸直接穿透到下面

4. 给 Button 单独设置:

ts

.hitTestBehavior(HitTestMode.HIT_TEST_SELF)

作用:只有按钮能接收点击


二、最终正确页面代码

ts

@Entry
@Component
struct SharePage {
  build() {
    Stack() {
      Row() {
        Text('正在共享屏幕')
        
        Button('停止共享')
          .onClick(() => {
            console.log('✅ 按钮可点击!')
          })
          // 关键:按钮自己能响应
          .hitTestBehavior(HitTestMode.HIT_TEST_SELF)
      }
      .backgroundColor(Color.White)
    }
    .width('100%')
    .height('100%')
    // 关键:根布局穿透触摸
    .hitTestBehavior(HitTestMode.HIT_TEST_NONE)
  }
}

三、悬浮窗创建代码(保持不变,只删 touchable 即可)

ts

windowClass.setWindowTouchable(false) // ❌ 删除
windowClass.setWindowFocusable(false) // ❌ 删除

四、最终效果(你想要的)

  • 悬浮窗全屏
  • 背景穿透点击(下面 APP 可操作)
  • 悬浮窗上的按钮可以正常响应

五、官方文档

cke_210.png

为啥网站无法访问

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17,

你好,可以使用 触摸测试控制 HitTestMode

1、将悬浮窗口设置为 HitTestMode.Transparent: 实现事件穿透。

Stack()
.hitTestBehavior(HitTestMode.Transparent)

2、将窗口里的按钮设置为 HitTestMode.BLOCK_HIERARCHY: 自身和子节点响应触摸测试,阻止所有优先级较低的兄弟节点和父节点参与触摸测试。

@Entry
@Component
struct Index {
  build() {
    Stack() {
      Row()
        .height('100%')
        .width('100%')
        .onClick(() => {
          console.log('----底部响应了---')
        })
        .backgroundColor('#80154fde')

      Stack() {
        Text('Text')
          .height(100)
          .width(100)
          .backgroundColor('#f0f')
          .hitTestBehavior(HitTestMode.BLOCK_HIERARCHY)
          .gesture(
            TapGesture()
              .onAction(() => {
                console.log('悬浮窗上的按钮点击了')
              })
          )
      }
      .height('100%')
      .width('100%')
      .backgroundColor('#1aa54c4c')
      .hitTestBehavior(HitTestMode.Transparent)
    }
  }
}

setWindowTouchable(true/false)方法是设置窗口是否为可触状态。true表示可触;false表示不可触。您设置了false是关闭触摸了,设置成true试试。

设置true之后就不能透传的底层了,无法点击到窗口下层的应用

在HarmonyOS鸿蒙Next中,setWindowTouchable(false)会使整个悬浮窗不响应所有触摸事件,包括按钮。要让按钮继续响应,需设置为true,并通过其他方式(如调整窗口区域或事件拦截)实现部分区域不可交互。

设置 setWindowTouchable(false) 后,整个窗口(包括其中的按钮)都不再接收触摸事件,因此按钮无法响应。要实现“按钮可点击,其余区域穿透”的效果,应使用 setTouchableAreas 接口。该方法允许你指定窗口内的一组矩形区域,只有这些区域内的触摸事件会被窗口处理,区域外的触摸会自动穿透到下层的应用窗口,无需再将整个窗口设为不可触摸。

操作步骤如下:

  1. 保持窗口默认可触摸(不调用 setWindowTouchable(false))。
  2. 获取按钮在窗口中的位置与尺寸(可通过 onAreaChange 等事件)。
  3. 调用 setTouchableAreas 设置矩形区域(坐标相对于窗口左上角)。

示例关键代码:

// 假设已获得按钮在窗口中的左、上、宽、高
let rect: window.Rect = { left: 100, top: 50, width: 200, height: 80 };
windowClass.setTouchableAreas([rect]);

按钮以外的区域触摸会直接透传到下层,按钮可正常响应,同时 setWindowFocusable(false) 可继续使用以避免抢占焦点。

回到顶部