电子说
窗口子系统位于 fundationwindowmanager 目录下,提供对窗口与 Display 管理的基础能力
每个 Ability 在创建时都会创建一个主窗口,并且为该窗口设置 ACE 中的 UIContent 用于加载展示 UI 界面。基本上所有的 UI 视图都是在窗口中展示的,比如弹窗、toast、系统状态栏导航栏、应用等。因此窗口子系统是系统图形界面显示所需的基础子系统。
WindowFlag
flag 指定窗口的部分测量规则:
WINDOW_FLAG_NEED_AVOID 是否避开区域,默认避开,比如状态栏导航栏区域
WINDOW_FLAG_PARENT_LIMIT 是否受到父窗口的限制,默认不限制,如果限制,则宽高不能超过父窗口,需与 WINDOW_MODE_FLOATING 配合使用
WindowMode
mode 指定窗口的布局规则:
WINDOW_MODE_UNDEFINED 默认模式,默认宽高为 display 宽高减去状态栏导航栏等的宽高
WINDOW_MODE_FULLSCREEN 全屏模式,但需要与 * * * WINDOW_FLAG_NEED_AVOID 一起使用,默认宽高为 display 宽高
WINDOW_MODE_SPLIT_PRIMARY 分屏主窗口模式,如果是横屏则位于左侧,竖屏位于上方
WINDOW_MODE_SPLIT_SECONDARY 分屏副窗口模式,如果是横屏则位于右侧,竖屏位于下方
WINDOW_MODE_FLOATING 悬浮模式,悬浮窗口可以通过窗口边缘改变窗口大小,默认宽高为 display 宽高的 3/4
应用主窗口可以通过启动 ability 时的参数 Want::PARAM_RESV_WINDOW_MODE(ohos.aafwk.param.windowMode)来侧面指定 WindowMode 的值
priority
窗口优先级决定了窗口的层级,priority 越大窗口越靠近顶部。该属性位于 WindowNode 内,且只能由 WindowType 决定。
WindowType
窗口类型的改变会引起 flag、mode、priority 或其他属性的改变,从而达到改变窗口的测量、排列与层级的目的。如:
property_- >SetWindowMode(WindowMode::WINDOW_MODE_FLOATING);
property_- >SetFocusable(false);
RemoveWindowFlag(WindowFlag::WINDOW_FLAG_NEED_AVOID);
SetWindowMode(WindowMode::WINDOW_MODE_FULLSCREEN);
层级则是由 WindowType 的 Priority 值与类型共同决定,同类型取值越大层级越高,WindowType 的 Priority 定义位于 foundationwindowmanagerwmserverincludewindow_zorder_policy.h
中,如:
WINDOW_TYPE_WALLPAPER = 0
WINDOW_TYPE_DESKTOP = 1
WINDOW_TYPE_APP_MAIN_WINDOW = 0
WINDOW_TYPE_APP_SUB_WINDOW = 1
WINDOW_TYPE_STATUS_BAR = 110
WINDOW_TYPE_KEYGUARD = 114
WINDOW_TYPE_BOOT_ANIMATION = 117
WindowType 的类型则有三种:
BelowApp 位于底层,如桌面、壁纸等
App 位于中间,如应用主窗口、子窗口
AboveApp 位于上方,如锁屏、状态栏等
WindowType 是在这几个属性中,开发者目前唯一能直接修改的窗口属性:
window.setWindowType(type: WindowType): Promise< void >
Window、Display、Screen 的关系
Screen 是物理屏幕,Display 是逻辑屏幕,Window 则依附于 Display。Screen 与 Display 之间是多对多的关系,Display 与 Window 也是多对多的关系。在普通的单屏场景下,Screen 与 Display 是一对一,Display 与 Window 则是一对多。
WindowManagerService
WMS 主要负责 Window 的管理,比如创建、销毁、布局、层级的管理,并提供窗口布局、焦点、事件分发的能力,但不负责绘制。主要职责如下:
DMS 提供 Display 信息、Display 事件通知以及管理 Display 与 Screen 映射关系,其他能力主要通过 RenderService 实现。主要职责如下:
窗口的创建从 Ability 的 OnStart 声明周期函数中触发。
AbilityWindow 是 Ability 持有用来在生命周期函数中生成或调用窗口生命周期的类,操作窗口的类则是 WindowScene。WindowScene 由 WindowManager client 端提供,用于屏蔽元能力与窗口管理之间强耦合,方便后续无屏幕的小型设备裁剪显示系统。
WindowImpl 是 IWindow 的实现,是提供给上层操作窗口的接口。WindowNode 与 WindowImpl 一一对应,是 WMS 中操作窗口的实体,其通过 WindowRoot 管理。* WindowNode 内部维护了一个 windowToken_对象,该对象的指向就是 WindowImpl。
顾名思义,WindowRoot 管理着所有的窗口。其内部维护着 WindowNode 与 WindowId 的 map,提供了对 WindowNode 的增删改查操作,并且提供了最小化所有窗口、最大化窗口、设置布局策略等能力。
主窗口的 WindowImpl 由 WindowScene 持有,子窗口则由主窗口自己管理维护。在 Ability 销毁时,会通知 WindowScene 销毁主窗口,主窗口则会销毁所有的子窗口,并通知 WMS 中的 WindowRoot 销毁相应的 WindowNode。
创建的流程仅仅是创建了 WindowImpl 与 WindowNode,并未涉及布局与渲染,那么窗口是如何显示的呢?
WindowNodeContainer 与 Display 一一对应,其管理了单个 Display 中的所有窗口,WindowRoot 则管理了所有的窗口与 WindowNodeContainer。WindowNodeContainer 提供了布局策略的决策与设置、窗口焦点设置、窗口排列、避开区域管理、窗口分屏显示等能力。
OH 目前支持两种策略,CASCADE(层叠)与 TILE(平铺)。默认的布局策略是 CASCADE,分屏显示也会将策略切换至 CASCADE。布局策略的主要能力就是决定窗口的排列布局方式、位置与大小。两种策略的区别如下:
设置全屏可以通过 ets 调用 window.setFullScreen(true),window 会占满全屏,并且状态栏与导航栏会消失。接下来来看看底层是如何实现的。
setFullScreen 会走到 WindowImpl 中,其中主要做了 3 件事
SetWindowMode
代码会调用到 WindowController::SetWindowMode 内,其中会对 mode 做一些判断。针对 FULLSCREEN 的情况,会最小化其他 app 的 window
接着调用 WindowNodeContainer::UpdateWindowNode,其中会调用布局策略来更新窗口的布局
RemoveWindowFlag
为窗口的 property 这是 flag 后,同样会走到 WindowNodeContainer::UpdateWindowNode 中
与窗口显示流程一样,其判断为全屏窗口后,不会避开状态栏与导航栏区域
SetSystemBarProperty
SetSystemBarProperty 同样会在 WindowNodeContainer 中更新窗口
迭代所有窗口,遇到全屏窗口,就将窗口内的 SystemBarProperty 与默认的对比,有变化(enable 值不同)就通知订阅了 systemBarTintChange 事件的组件
即 ets 中:window.on(‘systemBarTintChange’)
systemui 订阅了该事件,在收到事件后,根据 enable 的值,去调用 statusBar/navigationBar 窗口的 hide 方法,来达到隐藏状态栏导航栏的目的
只需要在调用 window 的 setFullScreen 函数后,在调用其 setSystemBarEnable 即可:
window.setSystemBarEnable(['status', 'navigation']).then(() = > {})
在 Stage 模式中,我们通过 WindowStage 的 setUIContent 来加载页面,这个过程是如何实现的?WindowStage 是 WMS 提供给前端的一套 api,其通过调用 WindowImpl 的 setUIContent 来实现:
uiContent_ = Ace::UIContent::Create(context_.get(), engine)
uiContent_- >Initialize(this, contentInfo, storage)
WindowImpl 会在合适的时机,调用 UIContent 内的回调:
触摸事件由多模输入模块传递到窗口,经过处理后,传递给 ACE 中的 UIContent 中。
通过 InputManager 注册为窗口输入事件消费者
触摸事件会回调至 WindowInputChannel::HandlePointerEvent 中
如果调用了窗口的 AddInputEventListener 设置触摸监听,转发事件至监听内,并且只将 POINTER_ACTION_DOWN 与 POINTER_ACTION_BUTTON_DOWN 传递给窗口。
如果是 POINTER_ACTION_MOVE 事件,在下一帧将事件传递给窗口。如果是其他事件,立即传递给窗口。
在窗口内,如果是悬浮窗口:
DMS 在启动时的主要工作就是从 RenderService 获取屏幕信息,并创建 ScreenGroup 与 Display
通过 RSInterface 注册屏幕连接回调,在屏幕连接后,创建 AbsScreen
再通过 RSInterface 获取屏幕支持的分辨率、刷新率等信息,设置到 AbsScreen 中
创建 ScreenGroup,将 AbsScreen 添加到 group 中
添加后会为 AbstractScreen 初始化 RSDisplayNode,并向 RenderService 提交一条 RSDisplayNode 创建的事务
ScreenGroup 与 AbsScreen 初始化完毕后,会为 * *
AbsScreen 创建一个 AbstractDisplay
AbstractDisplay 保存了 AbsScreen 中的分辨率刷新率等信息,并且根据屏幕宽与高,决定虚拟像素比。
通知感兴趣的部件,Display 已经创建好
WMS 也会通过 DMS 监听 Display 的变化,比如大小变化、横竖屏变化,WMS 会通知到 WindowNodeContainer 去更新 Display 的状态并重新布局所有窗口
如果遇到 BEFORE_SUSPEND 事件,比如进入锁屏状态,WMS 会通过 WindowNodeContainer 通知每个窗口(WindowImpl)更新其状态为 STATE_FROZEN,并告知 AMS,让持有窗口的 Ability 进入后台状态
ScreenGroup 顾名思义是屏幕组,屏幕组中定义了多个屏幕的连接方式,如扩展或镜像。每个物理屏幕在连接后都会加入到默认的屏幕组中。屏幕组也可以包含虚拟屏幕。ScreenGroup 与 AbsScreen 都由 AbstractScreenController 管理。
UpdateRSTree 会在窗口节点显示或隐藏时调用,其作用就是为 AbsScreen 中的 RSDisplayNode 添加或删除窗口的 RSSurfaceNode,并向向 RenderService 提交增加或删除子节点的事务。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !