引言

屏幕是用户与数字世界交互的唯一窗口。无论你的应用是短视频播放器、文档阅读器,还是 3D 渲染引擎,它最终都要呈现在一块真实的物理屏幕上。但大多数开发者对"屏幕"的认知止步于 vp2px 和媒体查询——实际上,HarmonyOS NEXT 通过 @ohos.display 模块暴露了一套完整的显示子系统诊断 API,让开发者能够:

  • 精确感知屏幕物理属性:分辨率、刷新率、像素密度(DPI / densityPixels / scaledDensity)、方向(含四方向枚举)、屏幕形态(矩形/圆形)
  • 深度理解折叠屏行为:设备是否可折叠?当前是展开还是折叠?副屏是否激活?屏幕折痕区域在哪?折叠角度是多少度?
  • 检测屏幕开孔与曲面:前置摄像头挖孔/刘海的精确边界矩形、瀑布屏的四边曲面区域
  • 识别显示源模式:当前屏幕是主显示器、镜像模式还是扩展模式?(2in1/平板外接显示器场景)
  • 监听屏幕录屏/投屏isCaptured() 检测是否正在被录屏,captureStatusChange 实时监听状态变化
  • 多显示器管理:枚举所有连接的显示器,每个都有独立的 Display 对象和属性

本文构建一个显示信息诊断中心 Demo,通过同步和事件监听两种方式,全面展示当前设备的显示系统状态,并实时追踪屏幕变化、折叠屏状态变化和录屏状态变化。

读完本文,你将掌握:

  • Display 对象:15+ 属性覆盖屏幕的所有物理参数
  • 折叠屏 APIisFoldable() / getFoldStatus() / getFoldDisplayMode() / getCurrentFoldCreaseRegion()
  • 屏幕开孔检测getCutoutInfo() 返回 CutoutInfo(含 boundingRects + waterfall 曲面区域)
  • 显示事件监听display.on('change') / on('foldStatusChange') / on('captureStatusChange') / on('foldAngleChange')
  • 多显示器枚举getAllDisplays() 返回所有连接的 Display 对象
  • API 级别意识:不同 API 的最低版本要求(API 10-23),以及如何做版本兼容处理

环境与权限

@ohos.display 自 API 7 开始提供,属于 @kit.ArkUI。导入方式:

import display from '@ohos.display';

零权限需求:所有读操作(获取 Display 属性、检测折叠状态、监听事件)均不需要任何权限声明。仅 createVirtualScreen() / destroyVirtualScreen() 等写操作需要 ohos.permission.ACCESS_VIRTUAL_SCREEN(系统级权限)。

一、核心数据结构

1.1 Display — 显示器对象

Display@ohos.display 最核心的数据结构,代表一个物理或虚拟显示器的完整属性集合。通过 getDefaultDisplaySync() 同步获取:

interface Display {
  id: number;                    // 显示器唯一 ID
  name: string;                  // 显示器名称
  alive: boolean;                // 是否存活(未销毁)
  state: DisplayState;           // 屏幕状态(ON/OFF/DOZE等 7 种)
  refreshRate: number;           // 当前刷新率(Hz)
  rotation: number;              // 旋转值(0/1/2/3 对应 0°/90°/180°/270°)
  width: number;                 // 逻辑宽度(px)
  height: number;                // 逻辑高度(px)
  availableWidth: number;        // 可用宽度(排除系统栏)(API 12+)
  availableHeight: number;       // 可用高度(排除系统栏)(API 12+)
  densityDPI: number;            // 屏幕 DPI
  densityPixels: number;         // 像素密度缩放系数(1.0 = 低分辨率基准)
  scaledDensity: number;         // 文本缩放密度
  xDPI: number;                  // X 轴物理 DPI(API 20+ crossplatform)
  yDPI: number;                  // Y 轴物理 DPI(API 20+ crossplatform)
  orientation: Orientation;      // 显示方向(四向枚举)
  screenShape?: ScreenShape;     // 屏幕形态(矩形/圆形)(API 18+)
  colorSpaces: ColorSpace[];     // 支持的所有色彩空间(API 11+)
  hdrFormats: HDRFormat[];       // 支持的所有 HDR 格式(API 11+)
  sourceMode?: DisplaySourceMode; // 显示源模式(主/镜像/扩展)(API 19+)
  supportedRefreshRates?: number[]; // 支持的所有刷新率(API 20+)
  x?: number;                    // 相对于主屏幕的 X 偏移(API 19+)
  y?: number;                    // 相对于主屏幕的 Y 偏移(API 19+)

  // 实例方法
  getCutoutInfo(): Promise<CutoutInfo>;          // 获取挖孔/刘海信息
  getAvailableArea(): Promise<Rect>;             // 获取可用区域(API 12+)
  getLiveCreaseRegion(): FoldCreaseRegion;        // 获取当前折痕区域(API 20+)
  getRoundedCorner(): RoundedCorner[];            // 获取圆角信息(API 23+)
  getDisplayCapability(): string;                // 获取显示能力摘要(API 18+)
}

Display 的设计兼顾了通用性和可扩展性——基础属性自 API 7 就存在,而较新的特性(如 supportedRefreshRatesscreenShapesourceMode)通过可选字段添加,保证向后兼容。

1.2 DisplayState — 屏幕状态(7 种)

enum DisplayState {
  STATE_UNKNOWN = 0,       // 未知
  STATE_OFF = 1,           // 息屏
  STATE_ON = 2,            // 亮屏(正常显示)
  STATE_DOZE = 3,          // 低功耗常显(AOD)
  STATE_DOZE_SUSPEND = 4,  // 低功耗暂停更新
  STATE_VR = 5,            // VR 模式
  STATE_ON_SUSPEND = 6     // 亮屏但暂停更新
}

DisplayState 是理解设备屏幕电源管理的关键。STATE_DOZE 对应 Android 的 Ambient Mode(熄屏显示),而 STATE_VR 是 VR 头显的特殊状态。

1.3 Orientation — 四方向枚举

enum Orientation {
  PORTRAIT = 0,            // 竖屏 0°
  LANDSCAPE = 1,           // 横屏 90°
  PORTRAIT_INVERTED = 2,   // 倒竖屏 180°
  LANDSCAPE_INVERTED = 3   // 倒横屏 270°
}

Orientation 是跨平台枚举(crossplatform, API 10+),比 rotation 数值更语义化。两者的关系:Orientation 描述的是显示内容的渲染方向,而 rotation 是屏幕硬件旋转的原始值。

1.4 FoldStatus — 折叠状态(11 种,含双折叠)

enum FoldStatus {
  FOLD_STATUS_UNKNOWN = 0,                           // 未知
  FOLD_STATUS_EXPANDED = 1,                          // 展开(单折叠)
  FOLD_STATUS_FOLDED = 2,                            // 折叠(单折叠)
  FOLD_STATUS_HALF_FOLDED = 3,                       // 半折(悬停态,单折叠)
  FOLD_STATUS_EXPANDED_WITH_SECOND_EXPANDED = 11,    // 双展开(API 15+)
  FOLD_STATUS_EXPANDED_WITH_SECOND_HALF_FOLDED = 21, // 第一轴展开第二轴半折
  FOLD_STATUS_FOLDED_WITH_SECOND_EXPANDED = 12,      // 第一轴折叠第二轴展开
  FOLD_STATUS_FOLDED_WITH_SECOND_HALF_FOLDED = 22,   // 第一轴折叠第二轴半折
  FOLD_STATUS_HALF_FOLDED_WITH_SECOND_EXPANDED = 13, // 第一轴半折第二轴展开
  FOLD_STATUS_HALF_FOLDED_WITH_SECOND_HALF_FOLDED = 23 // 双半折
}

HarmonyOS 的 FoldStatus 枚举覆盖了从单折叠手机到双折叠轴设备的全部状态组合。枚举值的编码设计很有技巧:十位数表示第一轴状态(0=未知, 1=展开, 2=折叠, 3=半折),个位数表示第二轴状态。这种编码方式让状态比较可以直接通过数学运算进行。

1.5 FoldDisplayMode — 折叠显示模式

enum FoldDisplayMode {
  FOLD_DISPLAY_MODE_UNKNOWN = 0,      // 未知
  FOLD_DISPLAY_MODE_FULL = 1,         // 全屏(完全展开时)
  FOLD_DISPLAY_MODE_MAIN = 2,         // 主屏(折叠时用户面对的屏幕)
  FOLD_DISPLAY_MODE_SUB = 3,          // 副屏(折叠时背面的屏幕)
  FOLD_DISPLAY_MODE_COORDINATION = 4  // 协同模式(双屏联动)
}

FoldStatus 描述的是硬件物理状态,而 FoldDisplayMode 描述的是软件层面的显示策略——在折叠状态下,App 的内容渲染在哪个屏幕上。

1.6 CutoutInfo — 屏幕开孔与曲面

interface CutoutInfo {
  readonly boundingRects: Rect[];                        // 挖孔/刘海边界矩形数组
  readonly waterfallDisplayAreaRects: WaterfallDisplayAreaRects; // 瀑布屏曲面区域
}

interface WaterfallDisplayAreaRects {
  readonly left: Rect;    // 左侧曲面区域
  readonly right: Rect;   // 右侧曲面区域
  readonly top: Rect;     // 顶部曲面区域
  readonly bottom: Rect;  // 底部曲面区域
}

interface Rect {
  left: number;
  top: number;
  width: number;
  height: number;
}

getCutoutInfo() 是异步方法,返回 Promise<CutoutInfo>boundingRects 数组中的每个 Rect 描述了一个屏幕开孔(前置摄像头挖孔或刘海)的像素级边界。waterfallDisplayAreaRects 描述曲面屏的弯曲边缘区域——这对于需要在曲面区域放置可交互 UI 元素的应用至关重要。

二、同步 API — 获取显示属性

2.1 getDefaultDisplaySync()

let d: display.Display = display.getDefaultDisplaySync();

同步返回默认的 Display 对象。所有 Display 属性都是快照值(不会自动更新),如果屏幕属性发生了变化(如旋转、折叠状态切换),需要重新调用该方法获取最新值,或通过事件监听器接收通知。

2.2 读取 Display 属性

let width = d.width;           // 逻辑宽度
let height = d.height;         // 逻辑高度
let density = d.densityDPI;    // 屏幕 DPI
let scale = d.densityPixels;   // 像素密度系数
let rate = d.refreshRate;      // 当前刷新率
let orient = d.orientation;    // 方向枚举

// 版本相关属性 — 需要判空
if (d.supportedRefreshRates) {
  let rates = d.supportedRefreshRates; // API 20+
}
if (d.screenShape !== undefined) {
  let shape = d.screenShape;  // API 18+
}

关键注意事项:较高 API 版本的属性(screenShapesourceModesupportedRefreshRatesxy)被定义为可选字段(?),在读取前必须做空值检查,否则在低版本设备上会导致运行时异常。

2.3 getAllDisplays() — 多显示器枚举

display.getAllDisplays().then((displays: display.Display[]) => {
  for (let d of displays) {
    console.log(`Display #${d.id}: ${d.name} ${d.width}x${d.height}`);
  }
});

在 2in1 设备、平板连接外部显示器时,这个方法会返回多个 Display 对象。每个 Display 都有独立的 idname、分辨率和方向。sourceMode 属性(API 19+)可以区分主显示器(MAIN)、镜像模式(MIRROR)、扩展模式(EXTEND)和独立模式(ALONE)。
在这里插入图片描述
在这里插入图片描述

三、折叠屏 API — 从检测到监听

HarmonyOS 对折叠屏的支持是全方位的——从"设备是否可折叠"到"当前折叠角度",从"折痕像素区域"到"双显示器模式切换"。这些 API 全部零权限、同步返回。

3.1 isFoldable() — 判断是否折叠设备

let foldable = display.isFoldable();  // true on foldable phones/tablets

这是任何折叠屏适配逻辑的入口。在非折叠设备上,所有后续的折叠相关 API 要么返回默认值,要么抛出异常,因此 isFoldable() 的结果应当作为条件分支的守卫

3.2 getFoldStatus() — 获取折叠状态

let status = display.getFoldStatus();
switch (status) {
  case display.FoldStatus.FOLD_STATUS_EXPANDED:
    // 屏幕完全展开 — 全屏体验
    break;
  case display.FoldStatus.FOLD_STATUS_FOLDED:
    // 屏幕完全折叠 — 适配小屏或切换到副屏
    break;
  case display.FoldStatus.FOLD_STATUS_HALF_FOLDED:
    // 半折悬停态 — 可以通过 split-screen 或特殊的悬停 UI
    break;
}

折叠状态变化会自然触发应用的界面适配。例如,视频播放器在 FOLDED 状态切换到副屏(FOLD_DISPLAY_MODE_SUB)播放,而主屏显示控制面板。

3.3 getFoldDisplayMode() — 当前显示模式

let mode = display.getFoldDisplayMode();
// FULL: 完全展开,使用整块屏幕
// MAIN: 折叠时用户面对的主屏幕
// SUB:  折叠时背面的副屏幕(如 Galaxy Z Flip 的外屏)
// COORDINATION: 双屏协同模式

getFoldDisplayMode() 回答的是"我的 App 当前渲染在哪个屏幕上"。在折叠状态下,App 可能被系统自动切换到副屏(通常是小尺寸外屏),这时需要对 UI 做大幅度的简化。

3.4 getCurrentFoldCreaseRegion() — 折痕区域

let crease = display.getCurrentFoldCreaseRegion();
// crease.displayId — 折痕所在的屏幕 ID
// crease.creaseRects — 折痕矩形数组
for (let rect of crease.creaseRects) {
  console.log(`Crease: ${rect.left},${rect.top} ${rect.width}x${rect.height}`);
}

折痕区域信息让开发者可以主动避开在折痕位置放置关键交互元素(如按钮、滑块),避免用户触摸体验不佳。这在设计折叠屏优化布局时是"最后 1px 的精细调整"。

3.5 折叠角度事件 (API 12+)

display.on('foldAngleChange', (angles: Array<number>) => {
  // angles[0] = 第一折叠轴的角度(0° = 完全折叠,180° = 完全展开)
  // angles[1] = 第二折叠轴的角度(双折叠设备)
  console.log('Fold angle: ' + angles[0] + '°');
});

foldAngleChange 是折叠屏 API 中最细粒度的事件——它不是离散的状态变化通知,而是连续的物理角度值。这让开发者可以实现实时响应用户折叠动作的动画效果(如折叠一半时 UI 布局的渐进式变化)。

四、屏幕开孔与曲面检测

4.1 getCutoutInfo() — 挖孔/刘海/曲面

let d = display.getDefaultDisplaySync();
d.getCutoutInfo().then((info: display.CutoutInfo) => {
  // 挖孔/刘海区域
  for (let rect of info.boundingRects) {
    console.log(`Notch: ${rect.left},${rect.top} ${rect.width}x${rect.height}`);
  }

  // 瀑布屏曲面区域
  let wf = info.waterfallDisplayAreaRects;
  if (wf.left) { /* 左侧曲面 */ }
  if (wf.right) { /* 右侧曲面 */ }
  if (wf.top) { /* 顶部曲面 */ }
  if (wf.bottom) { /* 底部曲面 */ }
});

getCutoutInfo() 是异步方法(返回 Promise),需要注意:在调用前确保 Display 对象仍然 alive,否则可能抛出异常。

waterfallDisplayAreaRects 的四个方向曲面是独立检测的——有的设备只有左右曲面(曲面屏手机),有的设备四边都是曲面(环形瀑布屏)。四个字段都是 Rect | null,需要逐个判空。

4.2 getAvailableArea() — 可用区域(API 12+)

d.getAvailableArea().then((rect: display.Rect) => {
  // rect 描述了排除挖孔、刘海、系统栏后的可用渲染区域
  console.log(`Safe area: ${rect.left},${rect.top} ${rect.width}x${rect.height}`);
});

availableWidth/availableHeight 相比,getAvailableArea() 返回的是完整的 Rect(包含起始坐标),适合处理不规则可用区域(如有曲面边缘的设备)。

五、录屏与投屏检测

5.1 isCaptured() — 检测录屏状态

let captured = display.isCaptured();
if (captured) {
  // 设备正在被录屏或投屏
  // 可以隐藏敏感信息、启用防截屏保护
}

isCaptured() 是 API 12 新增的方法,同步返回当前屏幕的捕获状态。检测范围包括:

  • 系统录屏功能
  • 第三方录屏应用
  • 无线投屏(屏幕镜像)
  • 有线视频输出(HDMI/DP 外接显示器)

5.2 captureStatusChange — 监听状态变化

display.on('captureStatusChange', (captured: boolean) => {
  if (captured) {
    // 开始录屏/投屏 — 启用水印或隐藏敏感数据
  } else {
    // 录屏结束 — 恢复正常显示
  }
});

captureStatusChange 事件让应用可以在录屏开始时即时响应,而不是周期性地轮询 isCaptured()。对于金融类应用、隐私敏感应用,这是一个非常实用的安全防护点。

六、事件监听体系

@ohos.display 提供了完整的事件监听机制,覆盖了所有重要的显示状态变化:

事件类型 回调参数 触发时机 最低 API
change Callback<number> 显示器增加/移除/属性变更 API 7
foldStatusChange Callback<FoldStatus> 折叠状态变化 API 10
foldDisplayModeChange Callback<FoldDisplayMode> 折叠显示模式变化 API 10
foldAngleChange Callback<Array<number>> 折叠角度变化(连续值) API 12
captureStatusChange Callback<boolean> 录屏/投屏状态变化 API 12
availableAreaChange Callback<Rect> 可用区域变化(Display 实例方法) API 12
brightnessInfoChange BrightnessCallback<number, BrightnessInfo> 亮度信息变化(API 22) API 22

6.1 注册与注销

// 注册监听
let changeCallback = (id: number) => { /* ... */ };
display.on('change', changeCallback);

// 注销监听(必须传递相同的 callback 引用)
display.off('change', changeCallback);

// 注销该事件类型的所有回调(API 20+)
display.off('change');

关键陷阱off()callback 参数在 API 7-19 是必填的,必须传递与 on() 相同的函数引用才能成功注销。API 20 起 callback 变为可选——不传则移除该事件类型的所有回调。

6.2 onChangeWithAttribute — 精确属性过滤(API 23)

display.onChangeWithAttribute(['refreshRate', 'orientation'], (id: number) => {
  // 仅在刷新率或方向变化时触发,避免噪音通知
});

这个 API 让事件监听更精准——只关注你关心的属性变化,减少不必要的事件处理和 UI 刷新。

七、实战 Demo:显示信息诊断中心

页面结构

显示信息诊断中心
├── 标题栏 — "显示信息诊断中心" + @ohos.display
├── 摘要栏 — 分辨率 + 刷新率 + DPI + 刷新按钮
├── 基础显示属性
│   ├── ID / 名称 / 存活状态
│   ├── 分辨率 + 可用区域
│   ├── 刷新率 + 支持的刷新率列表
│   ├── 密度(DPI / densityPixels / scaledDensity / X/Y DPI)
│   ├── 方向 + 旋转值
│   └── 屏幕形态(矩形/圆形)
├── 显示状态
│   ├── 屏幕状态(ON/OFF/DOZE/VR 等)
│   ├── 显示源模式(MAIN/MIRROR/EXTEND/ALONE)
│   ├── 色彩空间数量 + HDR 格式数量
│   └── 录屏状态
├── 折叠屏检测
│   ├── 是否折叠设备
│   ├── 折叠状态 + 折叠显示模式
│   ├── 折痕区域
│   └── 折叠角度(实时更新)
├── 屏幕开孔与曲面
│   ├── 挖孔/刘海区域(边界矩形坐标)
│   └── 曲面屏区域(L/R/T/B)
├── 多显示器(异步加载)
│   └── 每个显示器的 ID/名称/分辨率/方向/状态
├── 实时事件监听(开关控制)
│   └── change / foldStatusChange / foldDisplayModeChange / captureStatusChange / foldAngleChange
├── 四类显示事件对比卡片
└── 核心 API 参考 — 18 个关键 API + 枚举

4 个交互点

  1. 一键刷新全部属性 — 点击刷新按钮,同步读取 Display 对象的所有属性并更新 UI
  2. 实时事件监听 — 拨动开关开启所有事件监听器(change / foldStatus / foldDisplayMode / capture / foldAngle),每个事件触发时实时更新 UI 并追加事件日志
  3. 折叠屏专项诊断 — 独立读取和展示折叠状态、显示模式、折痕区域和折叠角度
  4. 多显示器浏览 — 异步加载所有连接的显示器,以卡片列表展示每个显示器的独立属性

核心代码实现

Display 对象属性读取
import display from '@ohos.display';

refreshAll(): void {
  try {
    let d = display.getDefaultDisplaySync();

    this.displayId = d.id;
    this.displayName = d.name;
    this.isAlive = d.alive;
    this.scrWidth = d.width;
    this.scrHeight = d.height;
    this.availWidth = d.availableWidth;
    this.availHeight = d.availableHeight;
    this.refreshRate = d.refreshRate;
    this.densityDPI = d.densityDPI;
    this.densityPixels = d.densityPixels;
    this.scaledDensity = d.scaledDensity;
    this.xDPI = d.xDPI;
    this.yDPI = d.yDPI;
    this.orientation = orientationLabel(d.orientation);
    this.rotation = d.rotation;
    this.displayState = stateLabel(d.state);

    // 版本相关属性 — 判空
    if (d.supportedRefreshRates !== undefined && d.supportedRefreshRates !== null) {
      this.supportedRates = d.supportedRefreshRates.map(r => r + 'Hz').join(', ');
    }
    if (d.screenShape !== undefined && d.screenShape !== null) {
      this.screenShape = d.screenShape === 0 ? '矩形' : '圆形';
    }
    if (d.sourceMode !== undefined && d.sourceMode !== null) {
      this.sourceMode = ['NONE', 'MAIN', 'MIRROR', 'EXTEND', 'ALONE'][d.sourceMode];
    }
  } catch (e) {
    // 异常处理
  }
}
折叠屏专项检测
refreshFoldable(): void {
  try {
    this.isFoldable = display.isFoldable();
  } catch (e) {
    this.isFoldable = false;
  }

  if (this.isFoldable) {
    this.foldStatus = this.foldStatusLabel(display.getFoldStatus());
    this.foldDisplayMode = this.foldDisplayModeLabel(display.getFoldDisplayMode());
    let crease = display.getCurrentFoldCreaseRegion();
    this.creaseInfo = 'displayId: ' + crease.displayId
      + ', 折痕区域: ' + crease.creaseRects.length + ' 个';
  }
}
事件监听全套注册
startListeners(): void {
  // 显示变化
  this.displayCallback = (id: number) => {
    this.addLog('显示变化 (id:' + id + ')', '#3B82F6');
    this.refreshAll();
  };
  display.on('change', this.displayCallback);

  // 折叠状态变化
  this.foldCallback = (status: display.FoldStatus) => {
    this.addLog('折叠状态: ' + this.foldStatusLabel(status), '#8B5CF6');
    this.foldStatus = this.foldStatusLabel(status);
  };
  display.on('foldStatusChange', this.foldCallback);

  // 录屏状态变化
  this.captureCallback = (captured: boolean) => {
    this.isCaptured = captured;
    this.addLog(captured ? '开始录屏/投屏' : '录屏/投屏已停止',
      captured ? '#EF4444' : '#10B981');
  };
  display.on('captureStatusChange', this.captureCallback);

  // 折叠角度变化
  this.foldAngleCallback = (angles: Array<number>) => {
    this.foldAngle = angles.map(a => a + '°').join(', ');
    this.addLog('折叠角度: ' + this.foldAngle, '#8B5CF6');
  };
  display.on('foldAngleChange', this.foldAngleCallback);
}

注销时必须传递相同的 callback 引用

stopListeners(): void {
  display.off('change', this.displayCallback);
  display.off('foldStatusChange', this.foldCallback);
  display.off('captureStatusChange', this.captureCallback);
  display.off('foldAngleChange', this.foldAngleCallback);
}

预览效果预期

  • 摘要栏:显示类似 “1260×2720 @120Hz 450DPI” 的实时屏幕摘要
  • 基础属性区:15 行属性值,包括分辨率、密度(4 种)、刷新率、方向等
  • 折叠屏区:在折叠设备上显示详细的折叠状态和折痕区域;在普通手机上显示"非折叠设备"
  • 挖孔区:显示类似 “1 个: [100,24 120x30]” 或 “无挖孔/刘海”;曲面区显示 “曲面: L,R” 或 “非曲面屏”
  • 事件日志:开启监听后,折叠/展开设备、插拔外接显示器时实时收到事件通知

八、与 Android / iOS 的 API 对比

能力 Android (DisplayManager) iOS (UIScreen) HarmonyOS (display)
默认屏幕 getDefaultDisplay() UIScreen.main getDefaultDisplaySync()
所有屏幕 getDisplays() UIScreen.screens getAllDisplays()
分辨率/密度 DisplayMetrics nativeBounds / scale Display.width/height/densityPixels
刷新率 Display.Mode.getRefreshRate() UIScreen.maximumFramesPerSecond Display.refreshRate + supportedRefreshRates
折叠检测 PackageManager.hasSystemFeature() 间接判断 无标准 API isFoldable() 直接 API
折叠状态 需监听 Configuration 变化 getFoldStatus() + on('foldStatusChange')
折痕位置 无标准 API getCurrentFoldCreaseRegion()
挖孔/刘海 WindowInsets.getDisplayCutout() API 28+ safeAreaInsets getCutoutInfo()
曲面屏 waterfallDisplayAreaRects
录屏检测 MediaProjection 需权限 UIScreen.isCaptured iOS 11+ isCaptured() + captureStatusChange
权限需求 无(读操作) 无(读操作) 无(读操作)

HarmonyOS 在显示子系统 API 的设计上有一个独特的优势——它将折叠屏能力曲面屏检测作为一等公民来对待,而不是通过间接方式推断。这意味着折叠设备上的 App 可以获得 Android 生态中需要大量样板代码才能实现(甚至无法实现)的精准屏幕感知能力。

九、常见陷阱与最佳实践

9.1 Display 对象是快照,不是实时引用

let d = display.getDefaultDisplaySync();
// ... 5 秒后用户旋转屏幕 ...
// d.orientation 仍然是旧值!
// 需要重新调用 getDefaultDisplaySync() 或通过事件监听更新

Display 对象获取后不会自动更新。需要定期刷新或通过事件监听获取最新状态。

9.2 版本兼容性——可选字段判空

// 错误 — 在 API 17 设备上 screenShape 为 undefined
let shapeText = d.screenShape === 0 ? '矩形' : '圆形';  // 崩溃!

// 正确
if (d.screenShape !== undefined && d.screenShape !== null) {
  let shapeText = d.screenShape === 0 ? '矩形' : '圆形';
}

9.3 off() 的 callback 引用陷阱

// 错误 — off 传递了新的匿名函数,与 on 不是同一引用
display.on('change', (id: number) => { /* A */ });
display.off('change', (id: number) => { /* B */ }); // 不会生效!

// 正确 — 保存 callback 引用
let cb = (id: number) => { /* ... */ };
display.on('change', cb);
display.off('change', cb);

9.4 折叠 API 的条件调用

// 非折叠设备上以下调用可能抛出异常:
// getFoldStatus(), getFoldDisplayMode(), getCurrentFoldCreaseRegion()

if (display.isFoldable()) {
  // 安全地调用折叠 API
  let status = display.getFoldStatus();
}

十、总结

@ohos.display 是 HarmonyOS NEXT 显示子系统的完整程序化入口。它远不止是"获取屏幕尺寸"那么简单——核心能力包括:

  1. Display 对象:15+ 属性覆盖了屏幕的物理参数、密度体系、刷新率、方向、色彩和 HDR,以及显示源模式
  2. 折叠屏全套 API:从 isFoldable()getFoldStatus()foldAngleChange,覆盖了折叠设备的所有维度——物理状态、显示策略、折痕区域、连续角度
  3. 屏幕开孔与曲面getCutoutInfo() 返回精确的边界矩形坐标,让 UI 可以主动避开障碍区域
  4. 录屏检测isCaptured()captureStatusChange 提供了零权限的录屏/投屏感知能力
  5. 事件监听体系:6 种事件类型覆盖显示系统所有的重要状态变化
  6. 多显示器管理getAllDisplays() 枚举所有连接的屏幕,每个都有独立的完整属性集

结合 @ohos.window(窗口管理)、@ohos.screen(屏幕亮度/省电策略)和 @ohos.mediaquery(媒体查询),HarmonyOS 开发者拥有了从硬件感知到 UI 适配的完整工具链。


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐