引言

屏幕是应用与用户交互的唯一窗口。应用的布局适配、字体缩放、图片资源选择,都依赖对屏幕参数的准确感知。HarmonyOS NEXT 通过 @ohos.display 模块将屏幕元信息统一暴露为同步可读的属性和事件回调——分辨率、刷新率、像素密度、旋转角度、折叠状态、录屏检测等,全部无需权限即可获取。

@ohos.display 属于 @kit.ArkUI,是 UI 框架层的核心模块。与 Android 的 DisplayMetrics / WindowManager 和 iOS 的 UIScreen 不同,@ohos.display 不仅提供基础的分辨率和密度,还内置了对折叠设备(Foldable)的完整支持——isFoldable() 判断、getFoldStatus() 查询、on('foldStatusChange') 实时监听,以及 isCaptured() 录屏检测。这些在 Android 和 iOS 中需要分散在多个 API 中的能力,在鸿蒙中被整合到了一个模块中。

本文将深入讲解 @ohos.display 的屏幕属性读取、密度体系、旋转识别和折叠状态管理四大核心能力,并构建一个"屏幕信息实验室"Demo,在一个页面中完整展示屏幕全部参数。

一、API 架构:同步属性 + 事件监听

1.1 核心设计理念

@ohos.display 的 API 分为两层:同步读取层提供屏幕属性的即时获取,全部以 Sync 后缀或直接属性访问的形式提供;事件监听层通过 on/off 模式提供折叠状态、录屏状态等实时变化的回调通知。

import display from '@ohos.display';

// 同步:瞬间返回完整 Display 对象
const d = display.getDefaultDisplaySync();

// 直接读取属性——全部同步
console.log(d.width + ' × ' + d.height);       // 分辨率
console.log(d.refreshRate.toFixed(0) + ' Hz'); // 刷新率
console.log(d.densityDPI.toFixed(0) + ' DPI'); // 像素密度

// 事件监听:折叠状态变化
display.on('foldStatusChange', (fs: display.FoldStatus) => {
  if (fs === display.FoldStatus.FOLD_STATUS_EXPANDED) {
    // 切换到展开布局
  }
});

这种"同步读取 + 事件回调"的分层设计非常实用——屏幕基础属性在应用生命周期内通常不变(分辨率、DPI),同步读取零开销;折叠状态和录屏状态是动态变化的,事件监听模型及时且精确。

1.2 getDefaultDisplaySync —— 默认屏幕对象

getDefaultDisplaySync() 是屏幕信息的核心入口。它同步返回一个 Display 对象,包含当前默认屏幕的全部参数。这个方法无参数、无权限要求,可以在任何地方调用。

const d = display.getDefaultDisplaySync();

返回的 Display 对象是一个属性只读的快照——屏幕参数不会自动更新。如果需要获取最新值(例如屏幕旋转后),需要重新调用 getDefaultDisplaySync()

Display 接口完整属性:

属性 类型 说明
id number 屏幕唯一标识符
name string 屏幕名称(如 “built-in”)
alive boolean 屏幕是否活跃
state DisplayState 屏幕状态(亮屏/息屏/待机/VR)
width number 屏幕宽度(像素)
height number 屏幕高度(像素)
refreshRate number 屏幕刷新率(Hz)
rotation number 屏幕旋转角度(0/90/180/270)
densityDPI number 屏幕物理像素密度(DPI)
densityPixels number 逻辑像素密度(缩放系数)
scaledDensity number 字体缩放密度

1.3 DisplayState —— 屏幕状态枚举

DisplayState 是屏幕当前的工作状态,有四种取值:

枚举值 说明 典型场景
STATE_ON 亮屏 正常使用中
STATE_OFF 息屏 锁屏后、按电源键关闭
STATE_DOZE 待机(常亮显示) AOD 息屏显示模式
STATE_VR VR 模式 连接 VR 设备时
private stateLabel(state: display.DisplayState): string {
  if (state === display.DisplayState.STATE_ON) return '亮屏';
  if (state === display.DisplayState.STATE_OFF) return '息屏';
  if (state === display.DisplayState.STATE_DOZE) return '待机';
  if (state === display.DisplayState.STATE_VR) return 'VR 模式';
  return '未知';
}

在实际开发中,state 可用于判断是否需要在息屏或待机状态下暂停动画、降低刷新率以节省电量。

1.4 分辨率与刷新率

widthheight 返回屏幕的物理像素分辨率。与 Android 的 DisplayMetrics.widthPixels 类似,这两个值代表屏幕的实际像素数。

refreshRate 返回屏幕当前的刷新率(Hz)。大多数手机屏幕为 60Hz,高刷屏为 90Hz 或 120Hz。开发者可以根据刷新率调整动画帧率:

const rr = display.getDefaultDisplaySync().refreshRate;
if (rr >= 90) {
  // 高刷屏:使用 90fps 流畅动画
} else {
  // 标准屏:使用 60fps 动画即可
}

1.5 rotation —— 屏幕旋转角度

rotation 返回当前屏幕的旋转角度(0°、90°、180°、270°)。这个值反映的是屏幕物理旋转状态,不是应用窗口的朝向。配合 @ohos.windowsetPreferredOrientation() 使用时,rotation 可以帮助判断用户当前的持握方向。

private rotationLabel(rot: number): string {
  if (rot === 0) return '0°(竖屏)';
  if (rot === 90) return '90°(横屏)';
  if (rot === 180) return '180°(倒置)';
  if (rot === 270) return '270°(反向横屏)';
  return rot.toString() + '°';
}

二、密度体系:DPI、像素密度与缩放密度

2.1 三层密度模型

@ohos.display 提供了三层密度概念,这是理解鸿蒙 UI 尺寸体系的关键:

属性 说明 典型值(手机) 用途
densityDPI 物理像素密度(每英寸像素数) 420-560 判断屏幕精细度等级
densityPixels 逻辑像素缩放系数 2.75-3.5 物理像素与逻辑像素的换算
scaledDensity 字体缩放密度 与 densityPixels 相近 字体大小的自动适配

densityDPI 是屏幕的物理属性,反映了硬件的像素密度。值越高,屏幕越精细。开发者通常不需要直接使用这个值做布局计算,但可以用它来判断设备的屏幕等级(低清/高清/超清)。

densityPixels 是物理像素与逻辑像素(vp)之间的换算系数。在 ArkUI 中,所有尺寸单位(vp、fp)都是逻辑像素,框架会自动根据 densityPixels 换算为物理像素。只有在需要精确控制物理像素(如 Canvas 绘制、图片解码)时才需要读取这个值。

scaledDensity 是在 densityPixels 基础上叠加用户字体缩放设置后的系数。当用户在系统设置中调整字体大小时,这个值会相应变化。

// 三个密度的关系
const d = display.getDefaultDisplaySync();
const physicalPx = 100 * d.densityPixels; // 100vp → 物理像素
const fontSize = 16 * d.scaledDensity;    // 16fp → 物理像素

2.2 密度与资源适配

在 HarmonyOS 的资源管理中,系统会根据 densityDPI 自动选择最合适的资源文件(如 resources/base/media vs resources/xxhdpi/media)。开发者通常不需要手动判断 DPI 等级,但了解当前设备的 DPI 值有助于调试资源加载问题。

三、折叠设备支持

3.1 isFoldable —— 判断设备是否可折叠

isFoldable() 同步返回 boolean,判断当前设备是否为折叠设备(折叠屏手机、平板等)。

const foldable = display.isFoldable();
if (foldable) {
  // 初始化折叠适配逻辑
}

这是折叠适配的第一步——先判断设备类型,再决定是否启用折叠相关的 UI 逻辑。

3.2 getFoldStatus —— 当前折叠状态

getFoldStatus() 同步返回当前折叠状态,值来自 FoldStatus 枚举:

枚举值 说明 布局策略
FOLD_STATUS_EXPANDED 展开状态 大屏布局,充分利用空间
FOLD_STATUS_FOLDED 折叠状态 小屏布局,优化单手操作
FOLD_STATUS_HALF_FOLDED 半开状态 中间态,通常保持前一个状态布局
private foldLabel(status: display.FoldStatus): string {
  if (status === display.FoldStatus.FOLD_STATUS_EXPANDED) return '展开';
  if (status === display.FoldStatus.FOLD_STATUS_FOLDED) return '折叠';
  if (status === display.FoldStatus.FOLD_STATUS_HALF_FOLDED) return '半开';
  return '未知';
}

在半开状态下(如 Flex 模式悬停),应用可以选择显示特殊的分屏 UI——例如上半屏播放视频、下半屏显示控制面板。

3.3 折叠状态实时监听

on('foldStatusChange', callback)off('foldStatusChange', callback) 提供折叠状态的实时监听。当用户折叠或展开设备时,回调会被立即触发。

private startFoldListen(): void {
  if (!display.isFoldable()) {
    return;
  }
  display.on('foldStatusChange', (fs: display.FoldStatus) => {
    this.foldStatus = this.foldLabel(fs);
    // 根据新状态调整布局
  });
}

// 离开页面时取消监听
private stopFoldListen(): void {
  display.off('foldStatusChange');
}

需要注意的是,off() 必须在组件销毁(aboutToDisappear)时调用,否则会造成内存泄漏。Demo 中我们将监听开关绑定到 Toggle 按钮,用户可以手动开启/关闭折叠状态监听。
在这里插入图片描述
在这里插入图片描述

四、实战 Demo:屏幕信息实验室

4.1 页面设计

"屏幕信息实验室"页面分为六个功能区域:

  1. 屏幕身份卡片:展示屏幕名称(大字标题)、活跃状态(活跃/未活跃带颜色标签)、屏幕 ID。下方三栏展示屏幕 ID、活跃状态和录屏检测结果。

  2. 分辨率与刷新率面板:双栏展示物理分辨率(width × height)和刷新率(Hz),右侧展示当前旋转角度(0°竖屏/90°横屏/180°倒置/270°反向横屏)。

  3. 显示密度面板:三栏展示三层密度——DPI(物理像素密度)、像素密度(逻辑缩放系数)、缩放密度(字体缩放系数)。

  4. 折叠状态面板:展示是否折叠设备、当前折叠状态(展开/折叠/半开)。提供两个按钮——"刷新折叠状态"重新读取最新值,"监听折叠"Toggle 按钮控制折叠状态监听的开启与关闭。

  5. API 能力说明:以灰色文字展示核心 API 的方法签名和功能说明。

  6. 操作日志:记录所有操作,按时间倒序排列,不同类别(success / error / system)以不同颜色标记。

4.2 核心实现

数据模型:

@State displayName: string = '--';
@State displayId: string = '--';
@State displayAlive: string = '--';
@State displayState: string = '--';
@State resolution: string = '--';
@State refreshRate: string = '--';
@State densityDPI: string = '--';
@State densityPixels: string = '--';
@State scaledDensity: string = '--';
@State rotation: string = '--';
@State isFoldable: boolean = false;
@State foldStatus: string = '--';
@State foldListening: boolean = false;
@State isCaptured: string = '--';
@State logs: LogEntry[] = [];

每个屏幕属性独立一个 @State 变量,确保 UI 能够单独更新每一项。

一次性读取全部属性:

private refreshAll(): void {
  try {
    const d = display.getDefaultDisplaySync();
    this.displayName = d.name;
    this.displayId = d.id.toString();
    this.displayAlive = d.alive ? '活跃' : '未活跃';
    this.displayState = this.stateLabel(d.state);
    this.resolution = d.width.toString() + ' × ' + d.height.toString();
    this.refreshRate = d.refreshRate.toFixed(0) + ' Hz';
    this.densityDPI = d.densityDPI.toFixed(0) + ' DPI';
    this.densityPixels = d.densityPixels.toFixed(3);
    this.scaledDensity = d.scaledDensity.toFixed(3);
    this.rotation = this.rotationLabel(d.rotation);
  } catch (e) {
    this.addLog('获取屏幕信息失败', 'error');
  }

  try {
    this.isFoldable = display.isFoldable();
    if (this.isFoldable) {
      const fs = display.getFoldStatus();
      this.foldStatus = this.foldLabel(fs);
    } else {
      this.foldStatus = '非折叠设备';
    }
  } catch (e) {
    this.isFoldable = false;
  }

  try {
    this.isCaptured = display.isCaptured() ? '录屏中' : '正常';
  } catch (e) {
    this.isCaptured = '--';
  }
}

每个独立功能块都使用 try/catch 包裹,确保单个模块的异常不影响其他模块的数据读取。

折叠状态监听 Toggle:

private toggleFoldListen(): void {
  if (this.foldListening) {
    this.stopFoldListen();
  } else {
    this.startFoldListen();
  }
}

private startFoldListen(): void {
  if (!display.isFoldable()) {
    this.addLog('当前设备不支持折叠', 'system');
    return;
  }
  display.on('foldStatusChange', (fs: display.FoldStatus) => {
    this.foldStatus = this.foldLabel(fs);
    this.addLog('折叠状态变化: ' + this.foldLabel(fs), 'system');
  });
  this.foldListening = true;
  this.addLog('已开启折叠状态监听', 'success');
}

private stopFoldListen(): void {
  try {
    display.off('foldStatusChange');
    this.foldListening = false;
    this.addLog('已关闭折叠状态监听', 'system');
  } catch (e) {
    this.addLog('关闭折叠监听失败', 'error');
  }
}

4.3 交互方式

Demo 提供四个核心交互点:

  1. 刷新全部信息:屏幕身份卡片中的隐式刷新——每次进入页面时自动调用 refreshAll() 读取所有属性。Demo 中不设显式刷新按钮,因为屏幕基础属性在应用生命周期内通常不变,但折叠状态面板有独立刷新按钮。

  2. 刷新折叠状态:单击"刷新折叠状态"按钮重新调用 isFoldable() + getFoldStatus(),更新折叠设备信息和当前折叠状态。

  3. 折叠状态监听 Toggle:单击"监听折叠"按钮开启监听(按钮变为红色"停止监听"),再次单击关闭监听(按钮恢复青色"监听折叠")。开启后折叠设备的状态变化会实时反映在 UI 和操作日志中。

  4. 操作日志:每次 API 调用和状态变化都以时间戳 + 消息的形式记录到日志区域,方便追踪操作时序。

五、实际应用场景

5.1 折叠屏布局适配

class FoldableLayoutManager {
  private isExpanded: boolean = false;

  constructor() {
    if (display.isFoldable()) {
      this.isExpanded = display.getFoldStatus() ===
        display.FoldStatus.FOLD_STATUS_EXPANDED;
      display.on('foldStatusChange', (fs: display.FoldStatus) => {
        this.isExpanded = fs === display.FoldStatus.FOLD_STATUS_EXPANDED;
        this.onLayoutChanged();
      });
    }
  }

  getLayoutMode(): 'single' | 'dual' {
    return this.isExpanded ? 'dual' : 'single';
  }

  private onLayoutChanged(): void {
    // 通知 UI 层重新布局
  }
}

5.2 根据屏幕密度选择图片资源

function getImageQuality(): 'low' | 'medium' | 'high' {
  const dpi = display.getDefaultDisplaySync().densityDPI;
  if (dpi >= 480) return 'high';
  if (dpi >= 320) return 'medium';
  return 'low';
}

虽然 HarmonyOS 的资源系统会自动匹配 DPI 等级,但在使用网络图片时,可以根据此值请求不同分辨率的图片以优化流量和加载速度。

5.3 高刷屏动画优化

function getAnimationFPS(): number {
  const rr = display.getDefaultDisplaySync().refreshRate;
  if (rr >= 120) return 120;
  if (rr >= 90) return 90;
  return 60;
}

5.4 录屏检测

function onSensitiveContent(): void {
  if (display.isCaptured()) {
    // 检测到录屏:隐藏敏感信息、添加水印
    showWatermark();
  }
}

isCaptured() 返回当前屏幕是否正在被录制(录屏或投屏),这在金融、社交等涉及隐私保护的场景中非常实用。

六、ArkTS 使用注意事项

6.1 Display 对象是快照

getDefaultDisplaySync() 返回的 Display 对象是调用时刻的快照,不会自动更新。如果屏幕发生了旋转或插入了外接显示器,需要重新调用来获取最新值。这与 Android 的 DisplayMetrics 行为一致。

6.2 事件监听的正确清理

display.on('foldStatusChange', callback) 注册的监听器必须在组件生命周期结束时通过 display.off('foldStatusChange', callback) 移除。在 aboutToDisappear() 中执行取消操作是最佳实践。

6.3 非折叠设备的兼容处理

在非折叠设备上,isFoldable() 返回 falsegetFoldStatus() 的行为是未定义的。代码中必须先判断 isFoldable() 的返回值,再决定是否调用折叠相关 API。

6.4 densityPixels 与 scaledDensity 的区别

densityPixels 是物理像素与逻辑像素(vp)的换算系数,scaledDensity 在此基础上叠加了用户的字体缩放设置。在布局计算中使用 densityPixels,在字号计算中使用 scaledDensity

七、总结

@ohos.display 是 HarmonyOS NEXT 中获取屏幕参数和监听显示状态变化的核心模块。通过本文的学习,你应该已经掌握:

  1. 同步读取模型getDefaultDisplaySync() 返回 Display 对象,包含 id/name/alive/state/refreshRate/rotation/width/height/densityDPI/densityPixels/scaledDensity——全部同步可读
  2. 三层密度体系:densityDPI(物理硬件密度)、densityPixels(逻辑像素缩放系数)、scaledDensity(字体缩放密度),分别服务于资源匹配、布局换算和字体适配
  3. 屏幕状态识别DisplayState 枚举(亮屏/息屏/待机/VR)和 rotation 角度值(0°/90°/180°/270°),是节电策略和方向适配的依据
  4. 折叠设备支持isFoldable() 判断 + getFoldStatus() 查询 + on/off('foldStatusChange') 实时监听,构成完整的折叠屏适配方案
  5. 录屏检测isCaptured() 同步返回当前是否处于录屏/投屏状态,是隐私保护的重要检测点

@ohos.display 的最佳使用模式可以总结为:

应用启动时全量读取 Display 对象建立屏幕画像,折叠设备注册 foldStatusChange 监听驱动布局切换,敏感场景调用 isCaptured 检测录屏状态。所有 API 同步为主、事件为辅——零权限、零延迟。

屏幕信息是 UI 适配的元数据。虽然 @ohos.display 的 API 数量不多,但它覆盖了从基础分辨率到折叠状态的完整屏幕模型。在 HarmonyOS NEXT 的 @kit.ArkUI 体系下,@ohos.display@ohos.window 共同构成 UI 框架的双基石——display 负责"屏幕是什么样的",window 负责"窗口怎么做"。

@ohos.display 属于 @kit.ArkUI,是 HarmonyOS NEXT 屏幕信息查询与显示状态监听的统一入口。它的 API 设计体现了"同步优先、事件补充"的核心理念——基础属性零开销读取,动态变化通过 on/off 回调精确感知。与 Android 的 WindowManager + DisplayMetrics + DisplayListener 碎片化 API 相比,@ohos.display 的一站式设计更加简洁和直观。

Logo

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

更多推荐