HarmonyOS鸿蒙Next中同一个window.WindowStage下createSubWindow多个子窗口,当destroyWindow()一个子窗口时,侧滑返回失效

HarmonyOS鸿蒙Next中同一个window.WindowStage下createSubWindow多个子窗口,当destroyWindow()一个子窗口时,侧滑返回失效 同一个window.WindowStage下createSubWindow多个子窗口,当destroyWindow()一个子窗口后,侧滑返回失效,原因好像是子窗口destroyWindow()焦点没有默认转移到主窗口,而是转移到其它子窗口了。但destroyWindow()后又无法通过shiftAppWindowFocus转移焦点到主窗口(不知道其它子窗口ID的情况下)。怎么才能避免这个问题呢,让多子窗口情况下destroyWindow()一个子窗口后焦点自动到主窗口

// 子窗口所需参数
export class SubWindowParam {
  public subWindowIcon:string = "";
  public subWindowJumpUrl:string = "";
  public jumpBundleName:string = "";
  public jumpAbilityName:string = "";
  public jumpToastName:string = "";
  public subWindowName:string = "";
}
// 创建子窗口并展示
public static showSubWindow(subWindowParam:SubWindowParam) {
  let windowStage: window.WindowStage = AppStorage.get('windowStage') as window.WindowStage;
  let storage: LocalStorage = new LocalStorage();
  let windowName = subWindowParam.subWindowName;
  windowStage.createSubWindow(windowName, (err, windowClass) => {
    if (err.code > 0) {
      Logger.error(TAG, `failed to create subWindow Cause: ${err.message}`);
      return;
    }
    try {
      storage.setOrCreate('storageSimpleProp', subWindowParam);
      storage.setOrCreate('subWindowClass', windowClass);
      // 设置子窗口加载页
      windowClass.loadContent('pages/subWindow',storage, () => {
        windowClass.setWindowBackgroundColor(SubWindowConst.WINDOW_BACKGROUND_COLOR);
      });
      // 设置子窗口左上角坐标
      windowClass.moveWindowTo(SubWindowConst.INIT_SUBWINDOW_X, SubWindowConst.INIT_SUBWINDOW_Y);
      // 设置子窗口大小
      let uiContext: UIContext = (AppStorage.get('WiseSupportWindow') as window.Window).getUIContext();
      windowClass.resize(uiContext.vp2px(SubWindowConst.INIT_SUBWINDOW_WEIGHT), uiContext.vp2px(SubWindowConst.INIT_SUBWINDOW_HEIGHT));
      // 展示子窗口
      windowClass.showWindow();
    } catch (err) {
      Logger.error(TAG, `failed to create subWindow Cause:${err}`);
    }
  })
}

更多关于HarmonyOS鸿蒙Next中同一个window.WindowStage下createSubWindow多个子窗口,当destroyWindow()一个子窗口时,侧滑返回失效的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

核心问题:

  • 在同一个 WindowStage 下创建了多个子窗口
  • 当销毁(destroyWindow())其中一个子窗口后,侧滑返回失效
  • 原因:焦点没有转移到主窗口,而是转移到了其他子窗口
  • 无法通过 shiftAppWindowFocus 转移焦点(因为不知道其他子窗口 ID)

方案一:在销毁子窗口前主动转移焦点到主窗口(推荐)

// 销毁子窗口并转移焦点到主窗口

 public static destroySubWindowWithFocus(windowClass: window.Window) {

   try {

     // 获取主窗口

     let mainWindow: window.Window = AppStorage.get('WiseSupportWindow') as window.Window;

     // 先将焦点转移到主窗口

     if (mainWindow) {

       mainWindow.setWindowFocusable(true);

       mainWindow.requestFocus((err) => {

         if (err.code) {

           Logger.error(TAG, `Failed to request focus: ${err.message}`);

         }

         // 焦点转移成功后再销毁子窗口

         windowClass.destroyWindow((err) => {

           if (err.code) {

             Logger.error(TAG, `Failed to destroy window: ${err.message}`);

           } else {

             Logger.info(TAG, 'Subwindow destroyed successfully');

           }

         });

       });

     }

   } catch (err) {

     Logger.error(TAG, `Failed to destroy window with focus: ${err}`);

   }

 }

方案二:维护子窗口管理器,统一管理所有子窗口

 // 子窗口管理器

 export class SubWindowManager {

   private static subWindows: Map<string, window.Window> = new Map();

   private static mainWindow: window.Window | null = null;

   // 初始化主窗口引用

   public static init(mainWindow: window.Window) {

     this.mainWindow = mainWindow;

   }

   // 创建并注册子窗口

   public static showSubWindow(subWindowParam: SubWindowParam) {

     let windowStage: window.WindowStage = AppStorage.get('windowStage') as window.WindowStage;

     let storage: LocalStorage = new LocalStorage();

     let windowName = subWindowParam.subWindowName;

     windowStage.createSubWindow(windowName, (err, windowClass) => {

       if (err.code > 0) {

         Logger.error(TAG, `failed to create subWindow Cause: ${err.message}`);

         return;

       }

       try {

         // 注册子窗口到管理器

         this.subWindows.set(windowName, windowClass);

         storage.setOrCreate('storageSimpleProp', subWindowParam);

         storage.setOrCreate('subWindowClass', windowClass);

         windowClass.loadContent('pages/subWindow', storage, () => {

           windowClass.setWindowBackgroundColor(SubWindowConst.WINDOW_BACKGROUND_COLOR);

         });

         windowClass.moveWindowTo(SubWindowConst.INIT_SUBWINDOW_X, SubWindowConst.INIT_SUBWINDOW_Y);

         let uiContext: UIContext = (AppStorage.get('WiseSupportWindow') as window.Window).getUIContext();

         windowClass.resize(

           uiContext.vp2px(SubWindowConst.INIT_SUBWINDOW_WEIGHT),

           uiContext.vp2px(SubWindowConst.INIT_SUBWINDOW_HEIGHT)

         );

         windowClass.showWindow();

       } catch (err) {

         Logger.error(TAG, `failed to create subWindow Cause:${err}`);

       }

     });

   }

   // 销毁子窗口并自动转移焦点

   public static destroySubWindow(windowName: string) {

     let windowClass = this.subWindows.get(windowName);

     if (!windowClass) {

       Logger.error(TAG, `SubWindow ${windowName} not found`);

       return;

     }

     try {

       // 移除注册

       this.subWindows.delete(windowName);

       // 如果还有其他子窗口,先销毁当前窗口

       // 然后转移焦点到主窗口

       if (this.mainWindow) {

         windowClass.destroyWindow((err) => {

           if (err.code) {

             Logger.error(TAG, `Failed to destroy window: ${err.message}`);

             return;

           }

           // 销毁成功后,将焦点转移到主窗口

           this.mainWindow?.requestFocus((err) => {

             if (err.code) {

               Logger.error(TAG, `Failed to request focus to main window: ${err.message}`);

             } else {

               Logger.info(TAG, 'Focus transferred to main window');

             }

           });

         });

       }

     } catch (err) {

       Logger.error(TAG, `Failed to destroy subWindow: ${err}`);

     }

   }

   // 获取所有子窗口列表

   public static getAllSubWindows(): string[] {

     return Array.from(this.subWindows.keys());

   }

   // 检查是否还有子窗口

   public static hasSubWindows(): boolean {

     return this.subWindows.size > 0;

   }

 }

 方案三:使用窗口生命周期监听

 // 在创建子窗口时添加监听

 windowClass.on('windowEvent', (event) => {

   if (event === window.WindowEventType.WINDOW_INACTIVE) {

     // 窗口失活时,如果是因为销毁,转移焦点到主窗口

     let mainWindow: window.Window = AppStorage.get('WiseSupportWindow') as window.Window;

     mainWindow?.requestFocus();

   }

 });

// 销毁前记得移除监听

windowClass.off(‘windowEvent’);

windowClass.destroyWindow();

使用建议

推荐使用方案二(子窗口管理器),原因如下:

  1. ✅ 统一管理所有子窗口,便于追踪和控制

  2. ✅ 自动处理焦点转移逻辑

  3. ✅ 可以随时知道还有哪些子窗口存在

  4. ✅ 便于扩展其他功能(如批量关闭、窗口切换等)

初始化示例

// 在应用启动时初始化

export default class EntryAbility extends UIAbility {

onWindowStageCreate(windowStage: window.WindowStage) {

 windowStage.loadContent('pages/Index', (err) => {

   if (err.code) {

     return;

   }

   // 获取主窗口并初始化管理器

   windowStage.getMainWindow((err, mainWindow) => {

     if (err.code) {

       return;

     }

     AppStorage.setOrCreate('WiseSupportWindow', mainWindow);

     SubWindowManager.init(mainWindow);

   });

   AppStorage.setOrCreate('windowStage', windowStage);

 });

}

}

更多关于HarmonyOS鸿蒙Next中同一个window.WindowStage下createSubWindow多个子窗口,当destroyWindow()一个子窗口时,侧滑返回失效的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


您描述的问题是由于销毁子窗口后焦点未正确转移回主窗口导致的。

解决方案

在销毁子窗口之前,显式调用window.shiftAppWindowFocus将焦点从待销毁的子窗口转移回主窗口。步骤如下:

  1. 获取主窗口ID:主窗口ID应在创建时存储(例如在AppStorage中)。
  2. 获取待销毁子窗口的ID:在创建子窗口时保存其ID。
  3. 转移焦点:在销毁前调用shiftAppWindowFocus(sourceWindowId: 子窗口ID, targetWindowId: 主窗口ID)
  4. 销毁子窗口:焦点转移成功后,再调用destroyWindow()

代码修改示例

// 1. 在创建子窗口时保存子窗口ID(修改showSubWindow方法)
public static showSubWindow(subWindowParam: SubWindowParam) {
  let windowStage: window.WindowStage = AppStorage.get('windowStage') as window.WindowStage;
  let storage: LocalStorage = new LocalStorage();
  let windowName = subWindowParam.subWindowName;
  windowStage.createSubWindow(windowName, (err, windowClass) => {
    if (err.code > 0) {
      Logger.error(TAG, `Failed to create subWindow. Cause: ${err.message}`);
      return;
    }
    try {
      // ... 其他代码(loadContent、moveWindowTo等) ...
      
      // 保存子窗口对象和ID到AppStorage,以便后续销毁时使用
      let subWindowId = windowClass.getWindowProperties().id; // 获取子窗口ID
      AppStorage.setOrCreate(`subWindowClass_${windowName}`, windowClass);
      AppStorage.setOrCreate(`subWindowId_${windowName}`, subWindowId);
      
      windowClass.showWindow();
    } catch (err) {
      Logger.error(TAG, `Failed to create subWindow. Cause: ${err}`);
    }
  });
}

// 2. 新增销毁子窗口方法,包含焦点转移
public static destroySubWindow(subWindowName: string) {
  let windowStage: window.WindowStage = AppStorage.get('windowStage') as window.WindowStage;
  // 从AppStorage获取子窗口对象和ID
  let subWindowClass: window.Window = AppStorage.get(`subWindowClass_${subWindowName}`);
  let subWindowId: number = AppStorage.get(`subWindowId_${subWindowName}`);
  let mainWindowId: number = AppStorage.get('mainWindowId'); // 主窗口ID应提前存储

  if (!subWindowClass) {
    Logger.error(TAG, `SubWindow ${subWindowName} not found.`);
    return;
  }

  // 先转移焦点到主窗口
  window.shiftAppWindowFocus(subWindowId, mainWindowId).then(() => {
    Logger.info(TAG, 'Focus shifted to main window successfully.');
    // 焦点转移成功后销毁子窗口
    subWindowClass.destroyWindow((err) => {
      if (err.code) {
        Logger.error(TAG, `Failed to destroy subWindow. Cause: ${err.message}`);
        return;
      }
      Logger.info(TAG, 'SubWindow destroyed successfully.');
      // 清理AppStorage中的子窗口数据
      AppStorage.delete(`subWindowClass_${subWindowName}`);
      AppStorage.delete(`subWindowId_${subWindowName}`);
    });
  }).catch((err) => {
    Logger.error(TAG, `Failed to shift focus. Cause: ${err.message}`);
    // 即使转移失败也尝试销毁子窗口,避免残留
    subWindowClass.destroyWindow((destroyErr) => {
      if (destroyErr.code) {
        Logger.error(TAG, `Failed to destroy subWindow. Cause: ${destroyErr.message}`);
      }
    });
  });
}

针对鸿蒙开发中多子窗口场景下销毁子窗口导致侧滑返回失效的问题,核心原因是焦点未正确转移到主窗口。

在销毁子窗口前,通过shiftAppWindowFocus方法强制将焦点转移到主窗口:

// 获取主窗口ID(在Ability初始化时保存)
private mainWindowId: number = windowStage.getMainWindowSync().getWindowProperties().id;

// 销毁子窗口时转移焦点
public static destroySubWindow(windowClass: window.Window) {
  // 先转移焦点到主窗口
  window.shiftAppWindowFocus(windowClass.getWindowProperties().id, this.mainWindowId, (err) => {
    if (err.code) {
      Logger.error(TAG, 'Failed to shift focus. Cause:' + JSON.stringify(err));
    }
    // 再销毁子窗口
    windowClass.destroyWindow((destroyErr) => {
      if (destroyErr.code) {
        Logger.error(TAG, 'Failed to destroy subWindow. Cause:' + JSON.stringify(destroyErr));
      }
    });
  });
}

HarmonyOS的分布式技术让我实现了跨设备的无缝协作,工作效率翻倍。

【问题分析】

destroyWindow()后可以通过shiftAppWindowFocus转移焦点到主窗口,销毁后窗口状态会发生变化,可以通过窗口监听来处理

【解决方案】

增加窗口监听的方法来处理焦点变更,目标窗口需确保具有获得焦点的能力(可通过setWindowFocusable()设置),并确保调用showWindow()成功且执行完毕。

在调用shiftAppWindowFocus()前,建议确保目标窗口已调用loadContent()setUIContent()并生效,否则可能会导致不可见窗口获取焦点,造成功能异常或影响用户体验。

// 监听Window状态,确保已经就绪
subWindow.on("windowEvent", (windowEvent) => {
  if (windowEvent == window.WindowEventType.WINDOW_ACTIVE) {
    // 切换焦点
    window.shiftAppWindowFocus(subWindowId, mainWindowId).then(() => {
      console.info('Succeeded in shifting app window focus');
    }).catch((err: BusinessError) => {
      console.error(`Failed to shift app window focus. Cause code: ${err.code}, message: ${err.message}`);
    });
  }
});

【参考文档】

Functions-@ohos.window (窗口)-窗口管理-ArkTS API-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者

在HarmonyOS Next中,同一个WindowStage下创建多个子窗口时,调用destroyWindow()销毁其中一个子窗口可能导致侧滑返回手势失效。这是因为窗口栈管理机制在销毁子窗口后未正确恢复导航状态。需检查窗口生命周期回调,确保onBackPressed事件未被错误拦截。可通过重写onBackPressed方法或调整窗口焦点顺序来修复。

这是一个典型的窗口焦点管理问题。在HarmonyOS Next中,当多个子窗口共存时,系统默认会将焦点转移到下一个可用的子窗口,而不是主窗口。

解决方案是在销毁子窗口前,先将焦点强制转移到主窗口。以下是关键代码示例:

// 在销毁子窗口前转移焦点到主窗口
public static closeSubWindow(windowName: string) {
  let windowStage: window.WindowStage = AppStorage.get('windowStage') as window.WindowStage;
  
  // 1. 先获取主窗口
  let mainWindow: window.Window = windowStage.getMainWindowSync();
  
  // 2. 将焦点转移到主窗口
  try {
    window.shiftAppWindowFocus({ windowId: mainWindow.id });
  } catch (err) {
    Logger.error(TAG, `shift focus to main window failed: ${err}`);
  }
  
  // 3. 销毁子窗口
  windowStage.destroyWindow(windowName, (err) => {
    if (err.code > 0) {
      Logger.error(TAG, `destroy subWindow failed: ${err.message}`);
    }
  });
}

核心要点:

  1. 使用 windowStage.getMainWindowSync() 获取主窗口实例
  2. 通过 window.shiftAppWindowFocus() 将焦点转移到主窗口
  3. 确保在销毁子窗口前完成焦点转移

这样处理后,侧滑返回功能就能正常工作了,因为焦点已经正确回到了主窗口。

回到顶部