【HarmonyOS Bug踩坑】主窗口调用的接口,UI在子窗口异常显示

作者:GeorgeGcs日期:2025/10/15

【HarmonyOS Bug踩坑】主窗口调用的UI表现在子窗口异常显示

一、问题现象:

这个问题的标题略显抽象,毕竟涉及到的异常表现形式太多,标题是临时拟定的。

说白了,这个问题是鸿蒙里经典的上下文指定问题

异常的业务场景是,在主窗口之上,添加一个子窗口。当在主窗口里调用某些UI表现,例如:气泡,弹窗,模态窗口,自定义安全键盘,自定义loading等,你会发现,有时候都异常加载到子窗口中了,并没有在主窗口显示。如下图所示:

在这里插入图片描述

1import { window } from '@kit.ArkUI';
2import { BusinessError } from '@kit.BasicServicesKit';
3
4@Entry
5@Component
6struct SubWinPage {
7  private TAG: string = "SubWinPage";
8  private sub_windowClass: window.Window | null = null;
9
10   aboutToAppear() {
11    this.showSubWindow("xxxx", 900, 300)
12     setTimeout(()=>{
13       try {
14         this.destroySubWindow();
15         // window.getLastWindow(getContext()).then((win)=>{
16         //   console.error(this.TAG, 'win:' + JSON.stringify(win));
17         //   let height = win.getWindowDecorHeight();
18         //   console.error(this.TAG, 'height:' + height);
19         // })
20
21         let windowStage_:  window.WindowStage = globalThis.mWindowStage;
22         let win = windowStage_.getMainWindowSync();
23         let height = win.getWindowDecorHeight();
24       }catch (e){
25         console.error(this.TAG, 'e:' + JSON.stringify(e));
26       }
27     },1000)
28  }
29
30  private showSubWindow(name: string, num: number, x: number) {
31    console.log(this.TAG, 'showSubWindow start');
32    let windowStage_:  window.WindowStage = globalThis.mWindowStage;
33    // 1.创建应用子窗口。
34    if (windowStage_ == null) {
35      console.error(this.TAG, 'Failed to create the subwindow. Cause: windowStage_ is null');
36    }
37    else {
38      windowStage_.createSubWindow(name, (err: BusinessError, data) => {
39        let errCode: number = err.code;
40        if (errCode) {
41          console.error(this.TAG, 'Failed to create the subwindow. Cause: ' + JSON.stringify(err));
42          return;
43        }
44        this.sub_windowClass = data;
45        console.info(this.TAG, 'Succeeded in creating the subwindow. Data: ' + JSON.stringify(data));
46        // 2.子窗口创建成功后,设置子窗口的位置、大小及相关属性等。
47        this.sub_windowClass.moveWindowTo(x, 300, (err: BusinessError) => {
48          let errCode: number = err.code;
49          if (errCode) {
50            console.error(this.TAG, 'Failed to move the window. Cause:' + JSON.stringify(err));
51            return;
52          }
53          console.info(this.TAG, 'Succeeded in moving the window.');
54        });
55        this.sub_windowClass.resize(num, num, (err: BusinessError) => {
56          let errCode: number = err.code;
57          if (errCode) {
58            console.error(this.TAG, 'Failed to change the window size. Cause:' + JSON.stringify(err));
59            return;
60          }
61          console.info(this.TAG, 'Succeeded in changing the window size.');
62        });
63        // 3.为子窗口加载对应的目标页面。
64        this.sub_windowClass.setUIContent("pages/SubWinLoadPage", (err: BusinessError) => {
65          let errCode: number = err.code;
66          if (errCode) {
67            console.error(this.TAG, 'Failed to load the content. Cause:' + JSON.stringify(err));
68            return;
69          }
70          this.sub_windowClass?.setWindowTouchable(true)
71          console.info(this.TAG, 'Succeeded in loading the content.');
72          // 3.显示子窗口。 (this.sub_windowClass as window.Window)
73          this.sub_windowClass?.showWindow((err: BusinessError) => {
74            let errCode: number = err.code;
75            if (errCode) {
76              console.error(this.TAG, 'Failed to show the window. Cause: ' + JSON.stringify(err));
77              return;
78            }
79            console.info(this.TAG, 'Succeeded in showing the window.');
80          });
81        });
82      })
83    }
84    console.log(this.TAG, 'showSubWindow end');
85  }
86
87  destroySubWindow() {
88    // 4.销毁子窗口。当不再需要子窗口时,可根据具体实现逻辑,使用destroy对其进行销毁。
89    (this.sub_windowClass as window.Window).destroyWindow((err: BusinessError) => {
90      let errCode: number = err.code;
91      if (errCode) {
92        console.error(this.TAG, 'Failed to destroy the window. Cause: ' + JSON.stringify(err));
93        return;
94      }
95      console.info(this.TAG, 'Succeeded in destroying the window.');
96    });
97  }
98
99  build() {
100    Column() {
101      Text("点击创建子窗口")
102        .id('SubWinPageHelloWorld')
103        .fontSize(50)
104        .fontWeight(FontWeight.Bold)
105        .onClick(()=>{
106          this.showSubWindow("ooooo", 500, 300);
107        })
108
109      Text("点击创建子窗口2")
110        .id('SubWinPageHelloWorld')
111        .fontSize(50)
112        .fontWeight(FontWeight.Bold)
113        .onClick(()=>{
114          this.showSubWindow("ooooo2", 800, 600);
115        })
116
117      Text("移动窗口2")
118        .id('SubWinPageHelloWorld')
119        .fontSize(50)
120        .fontWeight(FontWeight.Bold)
121        .onClick(()=>{
122
123          this.sub_windowClass?.moveWindowTo(700, 300, (err: BusinessError) => {
124            let errCode: number = err.code;
125            if (errCode) {
126              console.error(this.TAG, 'Failed to move the window. Cause:' + JSON.stringify(err));
127              return;
128            }
129            console.info(this.TAG, 'Succeeded in moving the window.');
130          });
131        })
132
133      Text("点击销毁子窗口")
134        .id('SubWinPageHelloWorld')
135        .fontSize(50)
136        .fontWeight(FontWeight.Bold)
137        .onClick(()=>{
138          this.destroySubWindow();
139        })
140
141      Text("显示气泡")
142        .id('SubWinPageHelloWorld')
143        .fontSize(50)
144        .fontWeight(FontWeight.Bold)
145        .onClick(async ()=>{
146             let win: window.Window = await window.getLastWindow(getContext());
147              win.getUIContext().getPromptAction().showToast({
148                message: "我是气泡,测试显示问题",
149                duration: 5000
150              });
151
152          win.getUIContext().px2vp(200)
153        })
154    }
155    .height('100%')
156    .width('100%')
157    .justifyContent(FlexAlign.Center)
158  }
159}
160

甚至还有使用老路由router跳转,在主窗口跳转也有可能会加载到子窗口中。或者一些使用逻辑处理的业务也可能异常。

该问题多出现在较大的工程里,使用了Har包,Hsp包。或者使用MVVM架构,纯逻辑层处理业务等。

二、问题原由:

综上所述,说了这么多,其实问题的原因在开头已经提到了,罪魁祸首就是上下文。Context。

如果接触鸿蒙开发,时间比较长的同学,其实对上下文还是很熟悉的,你会发现在日常开发中,经常要用到上下文去调用某些接口。特别是从api7最早开始接触鸿蒙,到如今api20了。这样的老同学体会更深,会发现很多之前不需要使用上下文调用的接口,现在也推荐或者强制让使用上下文引入接口了。

例如气泡,px2vp等等。

1getUIContext().getPromptAction().showToast({
2                message: "我是气泡,测试显示问题",
3                duration: 5000
4              });
5              
6getUIContext().px2vp(200)
7

其实该问题就是因为上下文依赖,因为鸿蒙特殊的堆叠渲染树,需要通过上下文作为挂靠节点的标志位。有了上下文作为导航,就知道当前要渲染的UI控件,现在挂载到哪个节点下了。

最早的时候,上下文是沉入到系统底层,上层开发感知比较少,很少需要调用到上下文去引出接口。但是随着接入鸿蒙的app越来越多。很多复杂的项目和业务场景迁移到鸿蒙中,发现这样的设计方式有问题。很多UI挂载的预期很离谱。

所以随着API的升级,上下文慢慢开放到应用层,让开发者来灵活的使用,来掌控挂载的预期效果。

该问题大多出现在api12或者之前的老项目中,因为封装的逻辑层,需要用到上下文。多是通过getLastWindow的形式,获取窗口,再从窗口中拿上下文。来做UI的引用操作。但是当有子窗口显示时,getLastWindow其实拿就不是主窗口,而是子窗口,这也导致后续获取的上下文也是子窗口的。

1    let win: window.Window = await window.getLastWindow(getContext());
2              win.getUIContext().getPromptAction().showToast({
3                message: "我是气泡,测试显示问题",
4                duration: 5000
5              });
6

像Loading,弹窗,甚至是悬浮活动按钮,都喜欢用子窗口来做,都会导致该问题。虽然子窗口可以高于主窗口显示,方便在顶层做一些UI效果。但是后续的上下文调用处理很麻烦。

三、解决方案:

1、不更换子窗口的情况,UI调用处的上下文获取需要进行修改,可以将上下文获取的逻辑进行删除,通过外部调用方传入上下文的形式,来获取上下文。

该方案的优点是封装者不需要考虑上下文的获取,UI显示也会符合预期。 缺点就是暴露的参数多了一个,并且有时候调用方获取上下文可能也不方便,如果是多层逻辑调用,那就要穿透式新增上下文参数了,改动也比较大。

2、更换子窗口,使用其他容器方案来实现展示在主窗口层级之上的效果。例如浮层OverlayManager。


【HarmonyOS Bug踩坑】主窗口调用的接口,UI在子窗口异常显示》 是转载文章,点击查看原文


相关推荐


k8s-pod的启动
Code Rhythm2025/10/13

k8s-pod的启动 一、命令行启动nginx的pod创建deployment访问节点中的nginx查看部署控制器和副本控制器模拟高可用,将k8s-3关机手动触发重建删除rs会重新启新的rs删除deploy,所管理的rs也会被删除 二、yaml文件启podkubectl apply 启动podkubectl apply 使用部署控制器启动pod 三、pod的启动流程四、pod的终止过程 官方文档:https://kubernetes.io/zh-cn/docs/concept


敏捷开发流程-精简版
暖阳_2025/10/12

敏捷开发流程 - 精简版(实战版) 版本: v1.0 更新日期: 2025-10-11 适用场景: 中小型团队快速迭代开发 🎯 流程全景图 📋 5大阶段详解 阶段1:需求收集 → 业务需求文档 负责人: 项目经理 输入: 用户原始需求(口头/邮件/会议记录) 核心工作: 收集用户需求 整理为层级结构(一级-二级-三级) 组织需求评审会①(内部简单评审) 产出物: ✅ 业务需求文档(层级结构) 评审点:需求评审会① 参与人:项目经理、产品经理 目的:快速确认需求方向和范围 输


门诊场景评测深度分析报告:医生-病人-测量代理交互对诊断影响机制研究(上)
Allen_Lyb2025/10/10

引言 医疗人工智能(AI)的发展正从静态问答系统向动态交互式决策助手演进,大型语言模型(LLM)在医学领域测评中展现出显著进步,如美国医学执照考试正确率从 2021 年 9 月的 38.1% 提升至 2023 年 11 月的 90.2%,超越人类专家平均水平(87%)[1][2]。然而,临床决策的复杂、顺序性本质与多模态数据收集需求,使得依赖静态问答的传统评估方法难以准确描绘 AI 系统的真实临床能力——研究表明,动态决策环境下诊断准确率可降至静态问答的 1/10 以下[3][4]。与此同时,


Android Studio 新功能 Journey Test:借助 AI 实现基于自然语言的 UI 测试用例编写
fundroid2025/10/9

在 Android 应用开发中,大家经常使用单元测试框架进行 UI 测试。随着技术演进,Android Studio 推出的 Journey Test 功能,依托 Gemini AI,为 UI 测试带来了全新的范式转变。 核心能力:自然语言与 AI 驱动的测试 Journey Test 最大的亮点在于结合了自然语言和 Gemini AI 的能力。开发者无需再埋头于复杂的代码编写,只需用日常的自然语言描述测试步骤,比如 “在邮箱输入框中输入 [email protected]”“验证是否显示‘邮箱为


【SpringCloud(2)】微服务注册中心:Eureka、Zookeeper;CAP分析;服务注册与服务发现;单机/集群部署Eureka;连接注册中心
凉凉心.2025/10/7

1. 什么是服务治理? SpringCloud封装了Netfix开发的Eureka模块来实现服务治理 在传统pc的远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册 2. 什么是服务注册与发现 Eureka采用了CS的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心,而系统中的其他微服务使用Eureka的客户端连接到Eureka


Java分布式缓存的负载均衡与扩展性:构建高可用系统的隐形支柱
墨夶2025/10/6

Java与分布式缓存的“生死博弈” 在某个电商平台的双十一大促凌晨,当流量洪峰冲破百万QPS时,Java工程师们发现:那些精心设计的Redis集群、Ehcache本地缓存、以及看似简单的负载均衡策略,正在以肉眼可见的速度瓦解。这个残酷的现实揭示了一个真相:Java在分布式缓存中的角色,远不止是调用API那么简单。它需要像外科医生一样精准控制资源分配,像建筑大师一样设计可扩展的架构。 一、Java分布式缓存的“三体法则”:负载均衡的本质 1.1 负载均衡:分布式系统的“心脏起搏器” /


Python高校社团选择报名系统 微信小程序设计与实现
qq_31666783672025/10/4

目录 已开发项目效果实现截图技术路线核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式! 已开发项目效果实现截图 同行可拿货,招校园代理 本系统还支持springboot/laravel/express/nodejs/thinkphp/flask/django/ssm/springcloud 微服务分布式等框架 技术路线 开发语言:Python 框架:flask/django 开发软件:PyCharm/vscode 数据库


[创业之路-655]:人类可以直接使用的自然资源
文火冰糖的硅基工坊2025/10/3

人类可以直接使用的自然资源,是指那些无需复杂加工或仅需简单处理即可被利用的自然物质与能量。这些资源是人类生存和发展的基础,广泛应用于生活、生产、能源、交通等领域。 下面从资源类型、使用方式、分布特点与可持续性四个维度,系统梳理人类可以直接使用的自然资源。 一、按资源性质分类:六大类可直接使用的自然资源 类别是否可再生可直接使用的形式使用场景1. 水资源可再生河流、湖泊、雨水、地下水饮用、灌溉、洗涤、发电2. 太阳能可再生阳光(热能与光能)取暖、晾晒、太阳能灶、光伏发电(初级转换)3. 风


DeepSeek Java 单例模式详解
稚辉君.清华大学马士兵P9Java2025/10/2

Java 单例模式详解 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。## 单例模式的实现方式### 1. 饿汉式(Eager Initialization)javapublic class EagerSingleton { // 类加载时就创建实例 private static final EagerSingleton instance = new EagerSingleton(); // 私有构造函数,防止外部实例化 private EagerSingleton


maven install依赖后 另一个项目 maven reload找不到包
zzxxlty2025/10/2

如果是build报错,找不到包,则在idea设置-maven-runner里,选择delegate IDE build actions to Maven, 让maven接管build

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0