HarmonyOS鸿蒙Next中关于onBlur执行顺序导致inputMethod.InputMethodController全局对象处理冲突

HarmonyOS鸿蒙Next中关于onBlur执行顺序导致inputMethod.InputMethodController全局对象处理冲突 我定义了一个自定义组件,该组件做了如下操作

点击事件(onClilck)时绑定输入法,代码如下(代码与文档中的一致):

// 绑定和设置监听
  async attachAndListener() {
    await this.inputController.attach(true, {
      inputAttribute: {
        textInputType: inputMethod.TextInputType.TEXT,
        enterKeyType: inputMethod.EnterKeyType.NEWLINE
      }
    });

    this.inputController.on('insertText', (text) => {
      logger.info(`插入文本 = ` + text);
      //if(/^\d+$/.test(text)){return;}   //不处理数字
      this.insertText(text);
    })
    this.inputController.on('deleteLeft', (length) => {
      logger.info(`删除文本长度 = ` + length);
      this.deleteText(length);
    })
  }

  .onClick((event?: ClickEvent) => {
    attachAndListener()
  }

失去焦点事件(onBlur)时解除绑定,代码如下:

.onBlur(() => {
  logger.info(`失去焦点ID = ` + this.inputId)

  this.inputController.off('insertText');
  this.inputController.off('deleteLeft');
  this.inputController.detach();
}

当一个页面有两个这个自定义组件(分别是A和B),当我来回切换这两个组件的焦点时onBlur执行顺序会导致

inputMethod.InputMethodController对象处理冲突,执行顺序如下:

A组件点击 -> 执行A组件onClick事件绑定输入法

B组件点击 -> 执行B组件onClick事件绑定输入法

A组件onBlur事件执行,执行解除绑定(在这里因为inputMethod.InputMethodController对象是全局的,导致监听被关闭,B组件无法监听到输入信息

我想问问题是

  1. A组件的onBlur事件执行顺序低于B组件的onClick事件,这是一个问题吗?

  2. 是否有什么好的方法来解决这个问题?


更多关于HarmonyOS鸿蒙Next中关于onBlur执行顺序导致inputMethod.InputMethodController全局对象处理冲突的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复
// 输入法绑定管理器
class SafeInputMethodBinding {
  private static instance: SafeInputMethodBinding;
  private activeComponent: string | null = null;
  private controller: inputMethod.InputMethodController;
  private listeners: Map<string, Function> = new Map();
  
  private constructor() {
    this.controller = inputMethod.getController();
  }
  
  static getInstance(): SafeInputMethodBinding {
    if (!SafeInputMethodBinding.instance) {
      SafeInputMethodBinding.instance = new SafeInputMethodBinding();
    }
    return SafeInputMethodBinding.instance;
  }
  
  // 安全绑定
  async bind(componentId: string, config: any): Promise<boolean> {
    if (this.activeComponent === componentId) {
      return true;
    }
    
    // 先清理之前的绑定
    await this.unbind();
    
    try {
      await this.controller.attach(true, config);
      this.activeComponent = componentId;
      
      // 设置监听器(确保只设置一次)
      if (!this.listeners.has('insertText')) {
        const insertHandler = (text: string) => this.handleInsertText(text);
        this.controller.on('insertText', insertHandler);
        this.listeners.set('insertText', insertHandler);
      }
      
      if (!this.listeners.has('deleteLeft')) {
        const deleteHandler = (length: number) => this.handleDeleteLeft(length);
        this.controller.on('deleteLeft', deleteHandler);
        this.listeners.set('deleteLeft', deleteHandler);
      }
      
      return true;
    } catch (error) {
      logger.error('绑定失败:', error);
      return false;
    }
  }
  
  // 安全解除绑定
  async unbind(): Promise<void> {
    if (!this.activeComponent) {
      return;
    }
    
    try {
      this.activeComponent = null;
      // 注意:不要在这里off监听器,因为其他组件可能还需要
      await this.controller.detach();
    } catch (error) {
      logger.error('解除绑定失败:', error);
    }
  }
  
  private handleInsertText(text: string): void {
    // 分发到当前激活组件
    // 可以通过事件总线或回调机制实现
  }
  
  private handleDeleteLeft(length: number): void {
    // 分发到当前激活组件
  }
  
  isBoundTo(componentId: string): boolean {
    return this.activeComponent === componentId;
  }
}

更多关于HarmonyOS鸿蒙Next中关于onBlur执行顺序导致inputMethod.InputMethodController全局对象处理冲突的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,onBlur事件执行顺序可能导致InputMethodController全局对象处理冲突。该问题源于焦点切换时,onBlur回调与输入法控制器状态更新存在时序竞争。当多个输入组件快速切换焦点时,InputMethodController可能尚未完成前一个组件的清理操作,就接收到新组件的绑定请求,造成状态紊乱。

这是一个典型的由全局 InputMethodController 实例和事件执行顺序引发的焦点管理问题。

1. 关于执行顺序: A组件的onBlur在B组件的onClick之后执行,这不是一个框架问题,而是预期的行为。焦点切换的典型流程是:新组件(B)获得焦点(触发onClick) -> 旧组件(A)失去焦点(触发onBlur)。因此,B先绑定输入法,A随后解绑,这导致了A的操作影响了B的监听。

2. 解决方案: 核心思路是避免全局InputMethodController实例的监听管理互相干扰。推荐以下两种方法:

  • 方案一:使用独立的Controller实例(推荐) 为每个自定义组件创建并管理独立的InputMethodController实例,而非共享全局对象。这样每个组件的绑定和解绑操作完全隔离。

    // 在组件内部
    private inputController: inputMethod.InputMethodController = inputMethod.getController();
    
    .onClick((event?: ClickEvent) => {
      // 使用 this.inputController 进行 attach 和监听
      this.attachAndListener();
    })
    
    .onBlur(() => {
      // 仅解绑和 detach 自己的 controller
      this.inputController.off('insertText');
      this.inputController.off('deleteLeft');
      this.inputController.detach();
    })
    
  • 方案二:增加焦点状态校验onBlur的解绑逻辑前,增加对当前组件是否仍处于焦点状态的判断。可以通过比较全局当前焦点组件ID和自身ID来实现,仅当确实失去焦点且不是新焦点组件时才执行解绑。但这需要自行维护焦点状态,复杂度较高。

第一种方案更清晰,能从根本上解决冲突,是更优的选择。

回到顶部