HarmonyOS鸿蒙Next中如何判断当前是全屏还是分屏,根据屏幕不同展示不同的UI
HarmonyOS鸿蒙Next中如何判断当前是全屏还是分屏,根据屏幕不同展示不同的UI
如何监听窗口大小的变化,判断当前是全屏还是分屏,根据屏幕不同展示不同的UI
一、监听窗口大小变化
通过注册 windowSizeChange 事件监听窗口尺寸变化,获取实时窗口尺寸(单位:px):
// 在UIAbility的onWindowStageCreate中设置监听
import { window } from '@kit.ArkUI';
import { UIAbility } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {
private windowObj: window.Window | undefined;
onWindowStageCreate(windowStage: window.WindowStage) {
// 获取主窗口
windowStage.getMainWindow((err, data) => {
if (err) {
console.error('Failed to get main window. Cause: ' + JSON.stringify(err));
return;
}
this.windowObj = data;
// 注册窗口尺寸变化监听
this.windowObj.on('windowSizeChange', (size: window.Size) => {
console.info('Window size changed: ' + JSON.stringify(size));
// 这里可触发UI更新逻辑
this.updateUIByWindowSize(size);
});
});
}
// 窗口销毁时取消监听
onWindowStageDestroy() {
if (this.windowObj) {
this.windowObj.off('windowSizeChange');
}
}
}
二、判断全屏/分屏状态
通过 windowStatusChange 事件监听窗口模式变化,直接获取当前状态(全屏、分屏、最大化等):
// 注册窗口状态监听
this.windowObj.on('windowStatusChange', (status: window.WindowStatusType) => {
console.info('Window status changed: ' + JSON.stringify(status));
// 判断全屏/分屏(需根据API版本适配)
if (status === window.WindowStatusType.FULL_SCREEN) {
console.info('当前为全屏模式');
this.handleFullScreenUI();
} else if (status === window.WindowStatusType.SPLIT_SCREEN) {
console.info('当前为分屏模式');
this.handleSplitScreenUI();
} else if (status === window.WindowStatusType.MAXIMIZE) {
// API version 14+:最大化模式(2合1设备)
console.info('当前为最大化模式(非全屏)');
this.handleMaximizeUI();
}
});
三、根据屏幕差异展示不同UI
方案1:基于断点(Breakpoint)的响应式布局
根据窗口宽度(转换为vp单位)划分断点,动态调整UI:
import { display } from '@kit.ArkUI';
// 定义断点(单位:vp)
private updateBreakpoint(windowWidthPx: number) {
const density = display.getDefaultDisplaySync().densityPixels;
const windowWidthVp = windowWidthPx / density; // 转换为vp
let breakpoint: string;
if (windowWidthVp < 320) {
breakpoint = 'xs'; // 智能穿戴等小屏设备
} else if (windowWidthVp < 600) {
breakpoint = 'sm'; // 手机等中等屏幕
} else if (windowWidthVp < 840) {
breakpoint = 'md'; // 平板等大屏
} else {
breakpoint = 'lg'; // 大屏设备
}
// 存储断点信息供UI使用(例如通过AppStorage)
AppStorage.setOrCreate('currentBreakpoint', breakpoint);
}
// 在windowSizeChange回调中调用
private updateUIByWindowSize(size: window.Size) {
this.updateBreakpoint(size.width);
// 同时可结合窗口状态综合判断
this.windowObj.getWindowStatus().then((status) => {
this.applyUILayout(status, size);
});
}
方案2:直接结合窗口状态和尺寸
private applyUILayout(status: window.WindowStatusType, size: window.Size) {
if (status === window.WindowStatusType.FULL_SCREEN) {
// 全屏模式下使用紧凑布局
this.loadLayout('fullscreen_layout');
} else if (status === window.WindowStatusType.SPLIT_SCREEN) {
// 分屏模式下使用双栏布局
this.loadLayout('split_screen_layout');
} else {
// 根据宽度动态选择布局
if (size.width < 600) { // 窄屏布局
this.loadLayout('compact_layout');
} else { // 宽屏布局
this.loadLayout('expanded_layout');
}
}
}
四、完整示例代码(UIAbility中)
import { UIAbility } from '@kit.AbilityKit';
import { window, display } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility {
private windowObj: window.Window | undefined;
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.getMainWindow((err: BusinessError, data) => {
if (err) {
console.error('Failed to get main window. Cause: ' + JSON.stringify(err));
return;
}
this.windowObj = data;
// 1. 监听窗口尺寸变化
this.windowObj.on('windowSizeChange', (size: window.Size) => {
this.updateBreakpoint(size.width);
});
// 2. 监听窗口状态变化
this.windowObj.on('windowStatusChange', (status: window.WindowStatusType) => {
this.handleWindowStatusChange(status);
});
// 初始获取一次状态
this.windowObj.getWindowStatus().then((status) => {
this.handleWindowStatusChange(status);
});
});
}
private updateBreakpoint(windowWidthPx: number) {
// ... 断点逻辑同上文
}
private handleWindowStatusChange(status: window.WindowStatusType) {
// ... 状态处理逻辑同上文
}
onWindowStageDestroy() {
if (this.windowObj) {
this.windowObj.off('windowSizeChange');
this.windowObj.off('windowStatusChange');
}
}
}
【问题背景】:如何区分全屏状态还是分屏状态,做UI适配
【解决思路】:主要可以通过窗口的on(‘windowSizeChange’)方法实现对窗口尺寸大小变化的监听。再根据窗口的尺寸变化,更新调整自身应用布局以实现适配。
主要步骤和示例如下:
- 在onWindowStageCreate方法中,获取Window对象。
- 通过getWindowProperties方法返回值中的windowRect获取窗口尺寸,写入AppStorage中用于UI侧窗口尺寸的首次初始化赋值。
- 使用on(‘windowSizeChange’)注册窗口尺寸变化时的监听,并写入AppStorage中供UI侧布局使用。
- UI侧通过@StorageLink绑定窗口尺寸后,AppStorage中属性key值对应的数据一旦改变,UI侧会同步修改。
- @StorageLink装饰的数据本身是状态变量,所以窗口尺寸发生变化时,会引起组件的重新渲染,开发者可以根据最新的窗口尺寸动态调整应用布局。
// EntryAbility.ets
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
console.info('Ability onWindowStageCreate.');
windowStage.getMainWindow().then((windowClass) => {
// 获取窗口尺寸,存入AppStorage
AppStorage.setOrCreate('winWidth', windowClass.getWindowProperties().windowRect.width);
AppStorage.setOrCreate('winHeight', windowClass.getWindowProperties().windowRect.height);
// 监听窗口尺寸变化
windowClass.on('windowSizeChange', (windowSize) => {
AppStorage.setOrCreate('winWidth', windowSize.width);
AppStorage.setOrCreate('winHeight', windowSize.height);
});
});
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error('Failed to load the content. Cause: ' + JSON.stringify(err));
return;
}
console.info('Succeeded in loading the content.');
});
}
}
页面入口UI文件
// Index.ets
@Entry
@Component
struct Index {
// 初始化参数,这里会初始化为AppStorage中存储的值
[@StorageLink](/user/StorageLink)('winWidth') winWidth: number = 1260;
[@StorageLink](/user/StorageLink)('winHeight') winHeight: number = 2224;
aboutToAppear() {
console.info('Current window size. width: ' + this.winWidth + ', height: ' + this.winHeight);
}
build() {
Row() {
// 根据winWidth、winHeight动态调整应用布局
// ...
}
.size({
width: this.getUIContext().px2vp(this.winWidth),
height: this.getUIContext().px2vp(this.winHeight)
})
}
}
具体可以参考以下的官方文档说明
1.在EntryAbility的onWindowStageCreate里增加监听屏幕大小变化,并用AppStorage.setOrCreate缓存起来,使用emitter.emit发送消息推送:
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.getMainWindowSync().on('windowSizeChange', (windowSize) => {
//缓存最新的屏幕宽高
AppStorage.setOrCreate('screenWidth', windowSize.width);
AppStorage.setOrCreate('screenHeight', windowSize.height);
//发送消息推送
emitter.emit({
eventId: 100,
priority: emitter.EventPriority.IMMEDIATE
});
}
}
2.在需要实时接收屏幕大小变化的Page的aboutToAppear里使用emitter.on绑定消息回调,回调中通过AppStorage.get获取最新缓存起来的屏幕大小值,然后更新当前page组件绑定的@State数据:
@Entry
@Component
struct MyPage {
[@State](/user/State) buttonWidth: number = 200;
aboutToAppear(): void {
//绑定消息回调
emitter.on({eventId: 100}, (event) => {
//从AppStorage读取最新的屏幕宽高
let screenWidth: number = AppStorage.get('screenWidth') as number;
let screenHeight: number = AppStorage.get('screenHeight') as number;
//更新组件对应的数据
if(screenWidth < screenHeight) {
this.buttonWidth = 200;
} else {
this.buttonWidth = 400;
}
}
}
build() {
Column() {
Button('按钮')
.width(this.buttonWidth)
}
.width('100%')
.height('100%')
}
}
望采纳!!!
监听窗口大小变化:
window.WindowStage.getMainWindow().then((windowClass) => { // 获取窗口尺寸 const width = px2vp(windowClass.getWindowProperties().windowRect.width); const height = px2vp(windowClass.getWindowProperties().windowRect.height); // 监听窗口尺寸变化 windowClass.on(‘windowSizeChange’, (windowSize) => { const width = px2vp(windowSize.width); const height = px2vp(windowSize.height); }); });
【问题现象】
如何监听当前窗口模式,例如全屏状态、分屏模式,针对不同模式进行适配。
【解决方案】
在EntryAbility的onWindowStageCreate方法中使用on(‘windowStatusChange’)监听窗口模式,示例代码如下:
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
let windowClass : window.Window | undefined = undefined;
AppStorage.setOrCreate('windowStatus', window.WindowStatusType.FULL_SCREEN)
windowStage.getMainWindow((err, data) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to obtain the main window. Cause: %{public}s', `error: ${err.message}`);
return;
}
windowClass = data;
windowClass.on("windowStatusChange", (windowStatusType) => {
AppStorage.setOrCreate('windowStatus', windowStatusType)
})
})
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', `error: ${err.message}`);
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
在Index.ets中根据不同的窗口模式显示不同的文字,示例代码如下:
import { window } from '@kit.ArkUI';
@Entry
@Component
struct Index {
@State message: string = '';
@StorageProp('windowStatus') windowStatus:number = window.WindowStatusType.FULL_SCREEN
build() {
RelativeContainer() {
if (this.windowStatus === window.WindowStatusType.FULL_SCREEN) {
Text('FULL SCREEN')
.id('HelloWorld')
.fontSize($r('app.float.page_text_font_size'))
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
} else if (this.windowStatus === window.WindowStatusType.SPLIT_SCREEN) {
Text('SPLIT SCREEN')
.id('HelloWorld')
.fontSize($r('app.float.page_text_font_size'))
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
} else {
Text('OTHER STATUS')
.id('HelloWorld')
.fontSize($r('app.float.page_text_font_size'))
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
}
}
.height('100%')
.width('100%')
}
}
注意:在自由窗口状态下,应用的targetAPIVersion设置小于14时,在窗口最大化状态(窗口铺满整个屏幕,2in1设备会有dock栏和状态栏,Tablet设备会有状态栏)时返回值对应为WindowStatusType::FULL_SCREEN。
应用的targetAPIVersion设置大于等于14时,在窗口最大化状态(窗口铺满整个屏幕,2in1设备会有dock栏和状态栏,Tablet设备会有状态栏)时返回值对应为WindowStatusType::MAXIMIZE。
Q:PC窗口最小化不触发onBackground,如何获取最小化的事件?
A:使用窗口实例的on(‘windowStatusChange’)方法开启窗口模式变化的监听。
在HarmonyOS Next中,可通过windowMode属性判断窗口模式。使用Window类的getWindowMode()方法获取当前状态,返回值为全屏(WINDOW_MODE_FULLSCREEN)或分屏(WINDOW_MODE_SPLIT_PRIMARY、WINDOW_MODE_SPLIT_SECONDARY)。根据返回值动态调整UI布局与组件可见性,实现不同屏幕模式下的界面适配。
在HarmonyOS Next中,可以通过Window类的on('windowSizeChange')事件监听窗口大小变化,结合windowSizeClass判断当前显示模式(全屏/分屏),动态调整UI布局。
具体实现步骤:
- 获取窗口实例并监听尺寸变化:
import { window } from '@kit.ArkUI';
// 获取当前窗口
let currentWindow = window.getLastWindow(this.context);
// 注册窗口大小变化监听
currentWindow.on('windowSizeChange', (windowSize) => {
// 根据窗口尺寸更新UI
this.updateLayout(windowSize);
});
- 根据窗口尺寸分类判断显示模式:
updateLayout(windowSize: window.Size) {
// 使用窗口尺寸分类标准
if (windowSize.width >= 840) { // 大屏设备全屏模式
this.showFullScreenUI();
} else if (windowSize.width >= 600) { // 分屏模式
this.showSplitScreenUI();
} else { // 小屏模式
this.showCompactUI();
}
}
- 在UI中使用状态变量控制布局:
@State currentLayout: string = 'compact';
// 根据不同布局条件渲染UI
build() {
if (this.currentLayout === 'fullScreen') {
// 全屏布局组件
} else if (this.currentLayout === 'splitScreen') {
// 分屏布局组件
} else {
// 紧凑布局组件
}
}
通过这种方式,应用可以实时响应窗口尺寸变化,在不同显示模式下提供最优的用户界面体验。记得在组件销毁时取消事件监听,避免内存泄漏。


