屏幕信息全解析:HarmonyOS的分辨率、刷新率、折叠状态一个API搞定

引言

你的App需要知道屏幕的宽高来做自适应布局?要适配折叠屏的展开/折叠状态?要根据刷新率调整动画帧率?HarmonyOS 的 @ohos.display 模块就是干这个的。

本文涵盖屏幕信息获取、折叠状态监听、刷新率读取等核心能力。


一、基础信息获取

typescript 复制代码
import display from '@ohos.display';

1.1 获取默认显示设备

typescript 复制代码
const defaultDisplay = display.getDefaultDisplaySync();
console.log({
  width: defaultDisplay.width,            // 逻辑分辨率宽
  height: defaultDisplay.height,          // 逻辑分辨率高
  densityPixels: defaultDisplay.densityPixels,  // 像素密度
  densityDPI: defaultDisplay.densityDPI,        // DPI
  refreshRate: defaultDisplay.refreshRate,      // 刷新率(Hz)
  rotation: defaultDisplay.rotation,            // 旋转角度
  orientation: defaultDisplay.orientation,      // 横竖屏状态
});

1.2 实际像素 vs 逻辑像素

typescript 复制代码
const d = display.getDefaultDisplaySync();
// 逻辑像素 → 物理像素
const physicalWidth = px2vp(d.width); // 反之用 vp2px
const physicalHeight = px2vp(d.height);

// 直接使用 vp 单位
console.log(`屏幕: ${d.width}x${d.height} vp, ${d.densityPixels}x 密度`);

二、折叠屏适配

2.1 判断折叠状态

typescript 复制代码
// 获取折叠信息
const foldInfo = display.getDefaultDisplaySync().foldStatus;
console.log('折叠状态:', foldStatus);

// 可能的状态值
enum FoldStatus {
  UNKNOWN = 0,       // 未知
  EXPANDED = 1,      // 展开
  FOLDED = 2,        // 折叠
  HALF_FOLDED = 3    // 半折叠(悬停模式)
}

2.2 监听折叠状态变化

typescript 复制代码
import { BusinessError } from '@kit.BasicServicesKit';

// 注册折叠状态变化监听
display.on('foldStatusChange', (status: display.FoldStatus) => {
  switch (status) {
    case display.FoldStatus.EXPANDED:
      console.log('设备已展开 → 切换为大屏布局');
      break;
    case display.FoldStatus.FOLDED:
      console.log('设备已折叠 → 切换为小屏布局');
      break;
    case display.FoldStatus.HALF_FOLDED:
      console.log('设备半折叠 → 进入悬停模式');
      break;
  }
});

// 组件销毁时移除
aboutToDisappear() {
  display.off('foldStatusChange');
}

三、多屏与扩展显示

3.1 获取所有显示设备

typescript 复制代码
const allDisplays = display.getAllDisplays();
console.log('共有', allDisplays.length, '个屏幕');

// 每个屏幕信息
allDisplays.forEach((d, i) => {
  console.log(`屏幕${i + 1}: ${d.width}x${d.height} @${d.refreshRate}Hz`);
});

3.2 监听屏幕变化

typescript 复制代码
// 屏幕连接/断开(外接显示器)
display.on('add', (d: display.Display) => {
  console.log('新屏幕接入:', d.id);
});
display.on('remove', (d: display.Display) => {
  console.log('屏幕断开:', d.id);
});

// 屏幕属性变化(分辨率/刷新率变更)
display.on('change', (d: display.Display) => {
  console.log('屏幕属性变化:', d.id, d.width, d.height);
});

四、实战:自适应布局工具

typescript 复制代码
class ScreenAdaptor {
  private display: display.Display;

  constructor() {
    this.display = display.getDefaultDisplaySync();
    this.initListeners();
  }

  // 根据屏幕宽度判断设备类型
  getDeviceType(): 'phone' | 'tablet' | 'foldable' {
    const w = this.display.width;
    if (w >= 840) return 'tablet';
    if (w >= 600) return 'foldable';
    return 'phone';
  }

  // 根据刷新率决定动画帧率
  getRecommendedFrameRate(): number {
    const rate = this.display.refreshRate;
    // 120Hz 屏幕用 60fps 动画也流畅,省电
    return Math.min(rate, 60);
  }

  // 计算安全区域
  getSafeArea(): { top: number; bottom: number } {
    // 折叠屏展开时有一些区域被遮挡
    return {
      top: this.display.height * 0.02,
      bottom: this.display.height * 0.02
    };
  }

  private initListeners() {
    display.on('change', () => {
      this.display = display.getDefaultDisplaySync();
    });
  }
}

// 使用
const adaptor = new ScreenAdaptor();
console.log('设备类型:', adaptor.getDeviceType());

五、关键属性速查

属性 类型 说明
width number 显示区域宽度(vp)
height number 显示区域高度(vp)
densityPixels number 屏幕密度
densityDPI number DPI
refreshRate number 屏幕刷新率(Hz)
rotation number 旋转角度(0/90/180/270)
orientation Orientation 横竖屏状态
foldStatus FoldStatus 折叠状态
isHdr boolean 是否支持HDR
colorDepth number 色深(bit)

六、最佳实践

✅ 正确用法

typescript 复制代码
// 1. 不要在 build 方法里反复 getDefaultDisplaySync
// ✅ 在 aboutToAppear 获取一次,监听变化更新
aboutToAppear() {
  this.displayInfo = display.getDefaultDisplaySync();
  display.on('change', () => {
    this.displayInfo = display.getDefaultDisplaySync();
  });
}

// 2. 折叠屏适配用 @State + foldStatusChange
@State isExpanded: boolean = true;
aboutToAppear() {
  display.on('foldStatusChange', (status) => {
    this.isExpanded = status === display.FoldStatus.EXPANDED;
  });
}

⚠️ 注意事项

  • getDefaultDisplaySync 只能在 UI 线程调用
  • 不要在循环中频繁获取:信息不会频繁变,缓存即可
  • 折叠状态变化后布局可能重新测量@State 驱动重新渲染
  • 多屏场景下默认屏幕不一定是内屏

总结

#mermaid-svg-dNnlhgsMKjBgL42S{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dNnlhgsMKjBgL42S .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dNnlhgsMKjBgL42S .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dNnlhgsMKjBgL42S .error-icon{fill:#552222;}#mermaid-svg-dNnlhgsMKjBgL42S .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dNnlhgsMKjBgL42S .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dNnlhgsMKjBgL42S .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dNnlhgsMKjBgL42S .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dNnlhgsMKjBgL42S .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dNnlhgsMKjBgL42S .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dNnlhgsMKjBgL42S .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dNnlhgsMKjBgL42S .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dNnlhgsMKjBgL42S .marker.cross{stroke:#333333;}#mermaid-svg-dNnlhgsMKjBgL42S svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dNnlhgsMKjBgL42S p{margin:0;}#mermaid-svg-dNnlhgsMKjBgL42S .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dNnlhgsMKjBgL42S .cluster-label text{fill:#333;}#mermaid-svg-dNnlhgsMKjBgL42S .cluster-label span{color:#333;}#mermaid-svg-dNnlhgsMKjBgL42S .cluster-label span p{background-color:transparent;}#mermaid-svg-dNnlhgsMKjBgL42S .label text,#mermaid-svg-dNnlhgsMKjBgL42S span{fill:#333;color:#333;}#mermaid-svg-dNnlhgsMKjBgL42S .node rect,#mermaid-svg-dNnlhgsMKjBgL42S .node circle,#mermaid-svg-dNnlhgsMKjBgL42S .node ellipse,#mermaid-svg-dNnlhgsMKjBgL42S .node polygon,#mermaid-svg-dNnlhgsMKjBgL42S .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dNnlhgsMKjBgL42S .rough-node .label text,#mermaid-svg-dNnlhgsMKjBgL42S .node .label text,#mermaid-svg-dNnlhgsMKjBgL42S .image-shape .label,#mermaid-svg-dNnlhgsMKjBgL42S .icon-shape .label{text-anchor:middle;}#mermaid-svg-dNnlhgsMKjBgL42S .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dNnlhgsMKjBgL42S .rough-node .label,#mermaid-svg-dNnlhgsMKjBgL42S .node .label,#mermaid-svg-dNnlhgsMKjBgL42S .image-shape .label,#mermaid-svg-dNnlhgsMKjBgL42S .icon-shape .label{text-align:center;}#mermaid-svg-dNnlhgsMKjBgL42S .node.clickable{cursor:pointer;}#mermaid-svg-dNnlhgsMKjBgL42S .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dNnlhgsMKjBgL42S .arrowheadPath{fill:#333333;}#mermaid-svg-dNnlhgsMKjBgL42S .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dNnlhgsMKjBgL42S .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dNnlhgsMKjBgL42S .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dNnlhgsMKjBgL42S .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dNnlhgsMKjBgL42S .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dNnlhgsMKjBgL42S .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dNnlhgsMKjBgL42S .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dNnlhgsMKjBgL42S .cluster text{fill:#333;}#mermaid-svg-dNnlhgsMKjBgL42S .cluster span{color:#333;}#mermaid-svg-dNnlhgsMKjBgL42S div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dNnlhgsMKjBgL42S .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dNnlhgsMKjBgL42S rect.text{fill:none;stroke-width:0;}#mermaid-svg-dNnlhgsMKjBgL42S .icon-shape,#mermaid-svg-dNnlhgsMKjBgL42S .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dNnlhgsMKjBgL42S .icon-shape p,#mermaid-svg-dNnlhgsMKjBgL42S .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dNnlhgsMKjBgL42S .icon-shape .label rect,#mermaid-svg-dNnlhgsMKjBgL42S .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dNnlhgsMKjBgL42S .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dNnlhgsMKjBgL42S .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dNnlhgsMKjBgL42S :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 自适应布局
折叠适配
动画优化
多屏场景
getDefaultDisplaySync
用途
width/height/densityPixels
foldStatus + foldStatusChange
refreshRate
getAllDisplays + add/remove监听

@ohos.display 是你连接鸿蒙设备的"眼睛"------知道屏幕是什么样子,才能做出适配的体验。

相关推荐
黑臂麒麟18 分钟前
HarmonyOS 网络连接诊断实战:检测网络状态、WiFi 切换、弱网监测一网打尽
网络·华为·arkts·鸿蒙
贾伟康1 小时前
【天体运行模拟|08】HarmonyOS ArkTS 单位换算实战:处理天文尺度、科学计数与精度
harmonyos·arkts·arkui·数值精度·单位换算
2501_919749032 小时前
华为鸿蒙免费反诈APP—小羊反诈
华为·harmonyos·鸿蒙
2501_919749032 小时前
华为鸿蒙免费视频播放器—小羊免费播放器
华为·harmonyos·鸿蒙
贾伟康3 小时前
【天体运行模拟|15】HarmonyOS ArkTS 本地状态持久化实战:让保存、删除和页面返回后的数据即时一致
harmonyos·arkts·数据持久化·状态管理·preferences
黑臂麒麟3 小时前
Harmony鸿蒙实战应用10:随手账本——发布检查与最终验收
华为·app·arkts·鸿蒙
贾伟康4 小时前
【天体运行模拟|16】HarmonyOS ArkTS 多设备布局实战:适配手机、平板和 PC/2in1 的窗口变化
harmonyos·arkts·arkui·响应式布局·多设备适配
黑臂麒麟4 小时前
HarmonyOS鸿蒙实战应用6:随手账本——Preferences本地持久化
数据库·华为·app·鸿蒙
大雷神4 小时前
HarmonyOS AR Engine高精几何重建实战——扫描纸盒并测量体积
华为·ar·harmonyos