鸿蒙新特性:@ohos.mediaquery 响应式布局实战 — 构建自适应多列界面
引言
如果你同时使用手机和平板打开同一款应用,你可能会注意到:手机上是单列纵向滚动,平板上是双列或三列网格。这不是两套代码——这是同一套代码根据屏幕尺寸动态切换了布局模式。这种"同一套代码、多种屏幕表现"的能力,在移动端开发中称为"响应式布局"。
HarmonyOS NEXT 提供了 @ohos.mediaquery API 来实现响应式布局。它的工作原理类似于 Web 前端的 CSS Media Queries:定义一个条件(如屏幕宽度 ≥ 768vp),监听这个条件的变化(如设备旋转),当条件匹配时切换布局。
本文将通过构建一个"响应式布局适配器",深入讲解 mediaquery 的核心 API:matchMediaSync、MediaQueryListener、on('change') 事件监听、以及基于断点的自适应布局策略。
读完本文你将能够:
- 使用
mediaquery.matchMediaSync()检测屏幕方向和尺寸 - 使用
on('change', callback)监听设备状态变化 - 设计断点策略:手机竖屏 / 手机横屏 / 平板三档
- 使用
Grid.columnsTemplate动态切换列数 - 理解响应式布局与自适应布局的区别
CSS Media Queries 的 ArkUI 对应
如果你有 Web 开发经验,你一定用过 CSS Media Queries:
@media (min-width: 768px) {
.container { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 767px) {
.container { grid-template-columns: 1fr; }
}
ArkUI 的 @ohos.mediaquery 是这个概念的编程式对应。它不写在样式表中,而是写在 ArkTS 代码中——通过 JavaScript API 检测条件、注册监听、驱动状态变化。
import { mediaquery } from '@kit.ArkUI';
// 创建媒体查询监听器
let mqListener = mediaquery.matchMediaSync('(min-width: 768vp)');
// 检查当前是否匹配
if (mqListener.matches) {
// 平板布局
} else {
// 手机布局
}
// 监听后续变化(如设备旋转、折叠屏展开)
mqListener.on('change', (result: mediaquery.MediaQueryResult) => {
if (result.matches) {
// 切换到平板布局
}
});
这个编程式 API 的一个优势是:你可以在回调中执行任意的 ArkTS 逻辑——不仅仅是切换样式,还可以重新组织数据结构、切换导航模式、甚至加载不同的子组件。
核心 API 解析
matchMediaSync(condition: string): MediaQueryListener
matchMediaSync 接收一个条件字符串,返回一个 MediaQueryListener 对象。条件字符串的语法与 CSS Media Queries 基本一致,支持以下条件:
| 条件类型 | 示例 | 含义 |
|---|---|---|
| 宽度 | (min-width: 768vp) |
视口宽度 ≥ 768vp |
| 宽度 | (max-width: 599vp) |
视口宽度 ≤ 599vp |
| 宽度 | (width: 360vp) |
视口宽度 = 360vp |
| 方向 | (orientation: landscape) |
横屏 |
| 方向 | (orientation: portrait) |
竖屏 |
| 设备类型 | (device-type: tablet) |
平板设备 |
| 设备类型 | (device-type: phone) |
手机设备 |
| 深色模式 | (dark-mode: true) |
系统深色模式 |
注意单位使用 vp(virtual pixel),这是 ArkUI 的标准长度单位,与 CSS 的 px 不同。
MediaQueryListener
MediaQueryListener 继承自 MediaQueryResult,增加了一个 on 方法用于注册变化监听:
matches: boolean— 当前条件是否匹配(只读)media: string— 匹配条件的原始字符串(只读)on(type: 'change', callback: Callback<MediaQueryResult>): void— 注册变化监听off(type: 'change', callback?: Callback<MediaQueryResult>): void— 移除监听
当设备方向改变、折叠屏展开/折叠、窗口大小调整等事件发生时,on('change') 的回调会被触发,result.matches 反映了新的匹配状态。
生命周期管理
Listener 应该在 aboutToAppear() 中创建,在 aboutToDisappear() 中移除:
private mqListener: mediaquery.MediaQueryListener | null = null;
aboutToAppear(): void {
this.mqListener = mediaquery.matchMediaSync('(orientation: landscape)');
this.mqListener.on('change', this.onOrientationChange);
}
aboutToDisappear(): void {
if (this.mqListener !== null) {
this.mqListener.off('change', this.onOrientationChange);
}
}


Demo 设计:三档断点策略
本文 Demo 定义了三档断点:
| 断点 | 条件 | 列数 | 布局模式 |
|---|---|---|---|
| 手机竖屏 | width < 600vp | 1 列 | 单列纵向 |
| 手机横屏 | 600vp ≤ width < 768vp | 2 列 | 双列网格 |
| 平板 | width ≥ 768vp | 3 列 | 三列网格 |
断点值的选取参考了常见设备尺寸:
- 手机竖屏约 320-400vp
- 手机横屏约 600-700vp
- 平板约 768vp+
Demo 提供两个入口来感知断点变化:
- 真实设备检测:
aboutToAppear()中使用matchMediaSync检测实际屏幕尺寸 - 手动模拟切换:三个按钮允许用户在手机上手动切换到平板布局模式
手动模拟是必要的——因为在手机设备上,真实屏幕尺寸永远处于"手机竖屏"断点中。如果没有模拟器,用户无法在手机设备上看到平板布局的效果。
断点更新逻辑
updateBreakpoint(name: string): void {
this.currentBreakpoint = name;
if (name === '手机竖屏 (< 600vp)') {
this.columns = 1;
this.layoutMode = '单列';
} else if (name === '手机横屏 (600-768vp)') {
this.columns = 2;
this.layoutMode = '双列';
} else if (name === '平板 (> 768vp)') {
this.columns = 3;
this.layoutMode = '三列';
}
}
columns 这个 @State 变量的变化会触发 Grid 组件的重新渲染,从而改变列数。
核心实现:自适应 Grid 布局
Demo 的内容区是一个包含 6 张卡片的 Grid,列数由 columns 状态决定:
Grid() {
ForEach(this.cardData, (card: CardData) => {
GridItem() {
Column() {
Text(card.title)
.fontSize(15)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
Text(card.desc)
.fontSize(11)
.fontColor('#FFFFFFCC')
}
.width('100%')
.padding(16)
.borderRadius(10)
.backgroundColor(card.color)
}
}, (card: CardData) => card.title)
}
.columnsTemplate(this.getColumnsTemplate())
.columnsGap(10)
.rowsGap(10)
getColumnsTemplate() 根据当前 columns 值返回不同的模板字符串:
getColumnsTemplate(): string {
if (this.columns === 1) {
return '1fr';
}
if (this.columns === 2) {
return '1fr 1fr';
}
return '1fr 1fr 1fr';
}
columnsTemplate 使用 CSS Grid 的 fr 单位——每列等分可用空间。1fr 1fr 表示两列等宽,1fr 1fr 1fr 表示三列等宽。
这种基于模板字符串的动态列数是 Grid 组件最灵活的用法——你不需要为每种布局写三套 Grid,只需要改变 columnsTemplate 的参数。
交互流程
Demo 提供三个主要交互:
- 真实设备信息展示:页面加载时自动检测设备方向和平板状态,显示当前断点和布局模式
- 手动断点切换:三个按钮(手机竖屏 / 手机横屏 / 平板)让用户手动切换,Grid 列数实时响应
- 方向变化监听:如果用户旋转设备,
on('change')回调自动检测并更新断点信息
设备信息面板显示四个状态:
Row() {
this.infoBadge('当前断点', this.currentBreakpoint, '#1677FF')
this.infoBadge('布局模式', this.layoutMode, '#D53F8C')
}
Row() {
this.infoBadge('方向', this.isLandscape ? '横屏' : '竖屏', '#38A169')
this.infoBadge('列数', this.columns.toString() + ' 列', '#ED8936')
}
这些信息让用户清楚看到:当前运行在哪个断点中、Grid 是如何响应的。
响应式布局 vs 自适应布局
虽然"响应式"和"自适应"常被混用,但它们有明确的区别:
| 维度 | 响应式布局(Responsive) | 自适应布局(Adaptive) |
|---|---|---|
| 布局变化 | 连续性变化(任意宽度都有合适表现) | 离散性变化(几个固定断点) |
| 实现方式 | 百分比、flex、相对单位 | 不同断点的固定布局 |
| 设计成本 | 高(需要保证所有宽度都好看) | 中(只设计几个关键断点) |
| 典型场景 | Web 网站 | 移动端 App |
| ArkUI 方式 | Flex + 百分比 + weight | mediaquery 断点 + Grid |
对于移动端 App 开发,自适应布局(Adaptive)通常是更务实的选择——你只需要针对三到四个关键断点设计布局即可,不需要像响应式网站那样覆盖从 320px 到 2560px 的所有宽度。
本文 Demo 采取的是自适应策略:定义三个断点,每个断点对应固定的列数。这符合绝大多数 App 的多设备适配需求。
@ohos.mediaquery 的局限性
理解 mediaquery 的局限性能帮助你做出更合理的技术决策:
-
只能检测,不能改变:mediaquery 告诉你当前屏幕是什么状态,但它不能改变屏幕尺寸。真正的布局变化需要你根据检测结果手动切换。
-
断点是全局的:mediaquery 检测的是整个应用窗口的尺寸,而不是某个组件的容器尺寸。如果你需要在特定容器内做响应式布局(如侧边栏内部的宽度变化),mediaquery 不适用,需要使用组件的
onAreaChange回调。 -
不支持嵌套查询:与 CSS
@media (min-width: 768px) and (orientation: landscape)不同,ArkUI 的 mediaquery 单次调用只支持一个条件。如果需要组合条件,需要分别创建多个 listener。 -
API 18 已标记废弃:在 API 18 中,
matchMediaSync被标记为 deprecated,推荐使用ohos.arkui.UIContext.MediaQuery#matchMediaSync。不过 API 24 中旧 API 仍然可用。
完整页面结构
Column(根容器)
├── Header(深色标题栏:"响应式布局" + @ohos.mediaquery 标签)
├── Scroll
│ └── Column
│ ├── 设备信息面板(4 个 badges:断点、模式、方向、列数)
│ ├── 断点模拟器(3 个按钮:手机竖屏 / 手机横屏 / 平板)
│ ├── 自适应内容区(Grid,列数动态变化)
│ └── API 参考文字
└── 根容器结束
总结
本文通过构建一个"响应式布局适配器",深入讲解了 HarmonyOS ArkUI 中 @ohos.mediaquery 媒体查询 API 的核心用法:
- matchMediaSync:接收条件字符串,返回 MediaQueryListener,支持宽度、方向、设备类型、深色模式等条件
- MediaQueryListener:提供
matches(当前是否匹配)、on('change')(变化监听)、off('change')(移除监听) - 三档断点策略:手机竖屏 1 列 / 手机横屏 2 列 / 平板 3 列
- Grid.columnsTemplate:通过
1fr模板字符串动态切换列数 - 手动断点模拟:通过按钮切换覆盖真实设备检测结果,方便开发调试
响应式/自适应布局是"一次开发、多设备运行"的基础设施。在一个鸿蒙应用需要同时运行在手机、折叠屏、平板、车机上的时代,mediaquery 提供的设备感知能力是构建多端适配界面的第一步。
在某种意义上,mediaquery 是"设备与 UI 之间的翻译官"——它把硬件的物理属性(屏幕尺寸、方向)翻译成软件能理解的条件表达式。开发者只需要关心"匹配什么条件时用什么布局",而不需要关心"这个硬件的像素密度是多少、物理尺寸是多少"。
更多推荐



所有评论(0)