水印遮罩导致TextInput无法获取粘贴权限
=======================

#### 问题现象

用户长按复制文本后，先在TextInput组件上添加水印遮罩，随后通过长按输入框唤起菜单，但点击“粘贴”时不生效。

问题效果预览:

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/30/v3/2rznFASaQ16OBBDxjmyRMw/zh-cn_image_0000002658800465.png?HW-CC-KV=V1&HW-CC-Date=20260709T133925Z&HW-CC-Expire=86400&HW-CC-Sign=74162611F0B7E8124A42780A607E078C7290246E6A685BD92C62BA34BEFC8F50 "点击放大")

#### 效果预览

| 
申请剪贴板权限

 | 

配置setMenuOptions

 |
| :-- | :-- |
| 

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/38/v3/V7SL4q8pRleU13iPtrrmzw/zh-cn_image_0000002628561112.png?HW-CC-KV=V1&HW-CC-Date=20260709T133925Z&HW-CC-Expire=86400&HW-CC-Sign=74E0C27BBE71D0F7D1B4E62B2E0B17C0A6C01736C294174A6EED5114CAF4DD18 "点击放大")

 | 

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/6d/v3/3YP4ntMxSz2epy68WEWPAg/zh-cn_image_0000002658920429.png?HW-CC-KV=V1&HW-CC-Date=20260709T133925Z&HW-CC-Expire=86400&HW-CC-Sign=8EEF3A29E779531B90CAE217DB1F6F187D06B9C1636B785FD8141F9835811CD1 "点击放大")

 |

#### 背景知识

*   [TextInput](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-textinput)：单行文本输入框组件，用于接收并展示输入内容。
*   [Window](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-window)：窗口提供管理窗口的一些基础能力，包括对当前窗口的创建、销毁、各属性设置，以及对各窗口间的管理调度。用于生成子窗口形成水印遮罩。
*   [约束与限制](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/security-component-overview#约束与限制)：安全控件因其自动授权的特性，为了保障用户的隐私不被恶意应用获取，针对安全控件做了很多的限制。其中安全控件被其他组件或窗口遮挡会导致授权失败。
*   [ohos.permission.READ\_PASTEBOARD](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/restricted-permissions#ohospermissionread_pasteboard)：允许应用读取剪贴板。申请权限后，应用会有剪贴板权限，不需要获取临时权限。

#### 问题定位

*   剪贴板是否存在粘贴内容。
*   是否拦截点击事件。
*   是否获取剪贴板权限。

#### 分析结论

长按输入框进行粘贴动作需要校验剪贴板权限。因为输入框上存在遮罩的水印子窗口，导致TextInput组件无法获取到剪贴板内容的权限，因此粘贴内容为空。

#### 修改建议

*   **方案一**：若要实现有水印时可粘贴，建议申请剪贴板权限。在使用长按粘贴功能前获取权限。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/60/v3/M0mIM7v0Sf-EB1cpg5UauQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T133925Z&HW-CC-Expire=86400&HW-CC-Sign=982603E1751BCD13EA907BAD8BB94E410BF4999A2037009170BB4CF04C3C02D8)
    
    ohos.permission.READ\_PASTEBOARD是受限开放权限。若使用自动签名，需要在权限配置后重新生成自动签名。若使用手动签名，需要申请对应权限。
    
    *   权限配置：
        
        "requestPermissions": \[
          {
            "name": "ohos.permission.READ\_PASTEBOARD",
            "reason": "$string:app\_name",
            "usedScene": {
              "abilities": \["EntryAbility"\],
              "when": "inuse"
            }
          }
        \],
        
    *   主页面（Index）：
        
        _// 剪贴板权限方式_
        import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
        import { window } from '@kit.ArkUI';
        import { BusinessError } from '@kit.BasicServicesKit';
        
        const permissions: Array<Permissions> = \['ohos.permission.READ\_PASTEBOARD'\];
        
        _// 申请权限_
        function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {
          let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
         _ // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗_
          atManager.requestPermissionsFromUser(context, permissions).then((data) => {
            let grantStatus: Array<number> = data.authResults;
            let length: number = grantStatus.length;
            for (let i = 0; i < length; i++) {
              if (grantStatus\[i\] === 0) {
             _   // 用户授权，可以继续访问目标操作_
              } else {
             _   // 用户拒绝授权，提示用户必须授权才能访问当前页面的功能，并引导用户到系统设置中打开相应的权限_
                return;
              }
            }
          _  // 授权成功_
          }).catch((err: BusinessError) => {
            console.error(\`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}\`);
          });
        }
        
        @Entry
        @Component
        struct Scene1 {
          private context = this.getUIContext().getHostContext() as common.UIAbilityContext; _// 获取Context_
        
          build() {
            Row() {
              Column() {
                TextInput({ placeholder: '输入文本后可复制，也可以粘贴到此处' })
                  .width('90%')
                  .margin(5)
        
                Button('弹出水印子窗口')
                  .width('90%')
                  .margin(5)
                  .onClick(() => {
                    try {
                      this.context.windowStage.createSubWindow('subWindowTest')
                        .then((win: window.Window) => {
                          win.setUIContent('pages/Watermark');
                          win.setWindowFocusable(false);
                          win.setWindowTouchable(false);
                          win.showWindow().then(() => {
                            win.setWindowBackgroundColor('#00000000');
                          });
                        })
                        .catch((err: BusinessError) => {
                          console.info(\`Failed to create the subwindow. Cause code: ${err.code}, message: ${err.message}\`);
                        });
                    } catch (exception) {
                      console.info(\`Failed to create the subwindow. Cause code: ${exception.code}, message: ${exception.message}\`);
                    }
                  })
        
                Button('获取剪贴板权限')
                  .onClick(() => {
                    reqPermissionsFromUser(permissions, this.context);
                  })
              }
              .width('100%')
            }
            .height('100%')
          }
        }
        
    *   水印页面（Watermark）：
        
        _// 水印页面_
        @Entry
        @Component
        struct Watermark {
          opacityValue: number = 0.1;
          watermark: string = 'watermark';
          canvas: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true));
        
          build() {
            Column() {
              Column() {
                Canvas(this.canvas)
                  .width('100%')
                  .height('100%')
                  .hitTestBehavior(HitTestMode.Transparent)
                  .onReady(() => {
                    this.canvas.fillStyle = '#ff000000';
                    this.canvas.font = '16vp';
                    this.canvas.textAlign = 'center';
                    this.canvas.textBaseline = 'middle';
                 _   // 在这里绘制文字水印，也可以是图片水印_
                    for (let i = 0; i < this.canvas.width / 120; i++) {
                      this.canvas.translate(120, 0);
                      let j = 0;
                      for (; j < this.canvas.height / 120; j++) {
                        this.canvas.rotate(-Math.PI / 180 \* 30);
                   _     // 此处水印数据是写死的，具体请替换为自己的水印_
                        this.canvas.fillText('test', -60, -60);
                        this.canvas.rotate(Math.PI / 180 \* 30);
                        this.canvas.translate(0, 120);
                      }
                      this.canvas.translate(0, -120 \* j);
                    }
                  })
              }
              .backgroundColor(Color.White)
              .borderRadius(20)
              .width('100%')
              .height('100%')
              .justifyContent(FlexAlign.Center)
            }
            .opacity(this.opacityValue)
            .justifyContent(FlexAlign.Center)
            .height('100%')
            .width('100%')
            .backgroundColor('#60000000')
            .transition(TransitionEffect.OPACITY.animation({ duration: 300 }))
          }
        }
        
*   **方案二**：可以通过配置[setMenuOptions](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-uicontext-textmenucontroller#setmenuoptions16)来实现显示子窗水印后TextInput实现粘贴，当TextInput、TextArea可支持拉起[enableAutoFill](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-textinput#enableautofill11)时，不支持将其对应的文本选择菜单显示在独立窗口中。因为TextInput配置了placeholder触发了自动填充，所以需要将自动填充关闭.enableAutoFill(false)。
    
    _// 控制Text菜单显示方式_
    import { common } from '@kit.AbilityKit';
    import { window } from '@kit.ArkUI';
    import { BusinessError } from '@kit.BasicServicesKit';
    
    @Entry
    @Component
    struct Scene2 {
      private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    
      aboutToAppear(): void {
       _ // 优先显示在独立窗口中_
        this.getUIContext()
          .getTextMenuController()
          .setMenuOptions(
            {
              showMode: TextMenuShowMode.PREFER\_WINDOW
            }
          );
      }
    
      build() {
        Row() {
          Column() {
            TextInput({ placeholder: '输入文本后可复制，也可以粘贴到此处' })
              .width('90%')
              .margin(5)
              .enableAutoFill(false)
    
            Button('弹出水印子窗口')
              .width('90%')
              .margin(5)
              .onClick(() => {
                try {
                  this.context.windowStage.createSubWindow('subWindowTest')
                    .then((win: window.Window) => {
                      win.setUIContent('pages/Watermark');
                      win.setWindowFocusable(false);
                      win.setWindowTouchable(false);
                      win.showWindow().then(() => {
                        win.setWindowBackgroundColor('#00000000');
                      });
                    })
                    .catch((err: BusinessError) => {
                      console.info(\`Failed to create the subwindow. Cause code: ${err.code}, message: ${err.message}\`);
                    });
                } catch (exception) {
                  console.info(\`Failed to create the subwindow. Cause code: ${exception.code}, message: ${exception.message}\`);
                }
              })
          }
          .width('100%')
        }
        .height('100%')
      }
    }
    

#### 常见FAQ

Q：为何长按输入框粘贴后，再生成水印，此时长按输入框可以粘贴内容？

A：先长按输入框粘贴，因为没有遮挡，此时可以正常获取剪贴板临时权限，后续再进行粘贴的时候已经获得了剪贴板的权限，所以可以进行粘贴。

Q：如何改变水印子窗口位置大小？

A：可以通过[resize](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-window#resize9-1)来设置子窗口宽高，[moveWindowTo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-window#movewindowto9-1)设置子窗口起始位置，以改变子窗口大小和位置。