各位同学们大家好!全场景适配是鸿蒙 “分布式技术” 的核心落地场景,也是认证中占比 14% 的高频考点 —— 从布局自适应、设备特性识别到资源分级,全程贴合认证大纲,今天咱们拆解手机、平板、车机的差异化适配技巧,带完整实战代码,看完就能做出跨设备流畅运行的界面!

一、适配核心原理与认证考点(必懂)

1. 鸿蒙多设备适配核心逻辑

鸿蒙通过 “三层适配体系” 实现全场景 UI 兼容,核心原则:

  • 底层:系统自动适配屏幕分辨率、像素密度;
  • 中层:布局组件(Flex/Grid)自适应伸缩;
  • 上层:开发者针对设备特性做差异化设计。

2. 认证考点分值分布

考点类型

分值占比

核心考查内容

自适应布局

5 分

Flex/Row/Column 弹性布局、百分比尺寸

响应式组件

3 分

Grid 自适应列数、List 组件适配

设备特性识别

2 分

屏幕尺寸判断、设备类型区分

资源分级适配

2 分

多设备资源目录、差异化资源加载

车机 / 平板专属适配

2 分

车载 UI 规范、平板分屏布局

3. 前置配置(module.json5 必配)

开启多设备支持,配置屏幕适配策略(认证必考点):


// module.json5

{

"module": {

"deviceTypes": ["phone", "tablet", "car"], // 支持的设备类型

"displayOrientation": "unspecified", // 自动适配横竖屏

"configChanges": ["orientation", "screenSize"], // 监听屏幕变化

"abilities": [

{

"name": "MainAbility",

"windowMode": "windowed", // 支持分屏(平板/车机必备)

"launchType": "standard"

}

]

}

}

⚠️ 认证考点标红:configChanges必须配置orientation和screenSize,否则横竖屏切换时布局错乱,实操直接扣分!

二、基础适配技巧:布局自适应(通用所有设备)

1. 弹性布局(Flex/Row/Column):替代固定像素

核心:用flexGrow/flexShrink/百分比宽度让布局随屏幕伸缩,避免固定 px 导致的适配问题。


// 适配前(固定像素,平板/车机显示不全)

Row() {

Button('添加待办').width(120).height(40);

Button('同步').width(120).height(40);

}

// 适配后(弹性布局,自适应所有设备)

Row({ space: 15, justifyContent: FlexAlign.SpaceBetween }) {

Button('添加待办')

.width('48%') // 百分比宽度(适配不同屏幕)

.height(44) // 按钮高度≥44px(手机规范)

.flexGrow(1); // 剩余空间自动分配

Button('同步')

.width('48%')

.height(44)

.flexGrow(1);

}

.padding(15)

.width('100%'); // 占满父容器宽度

2. 响应式 Grid 布局:大屏多列 / 小屏单列(认证重点)

Grid是鸿蒙适配的核心组件,通过columnsTemplate实现 “自动列数”,小屏(手机)自动缩为 1 列,大屏(平板 / 车机)自动多列展示:


// 多设备自适应Grid布局(待办清单示例)

@Entry

@Component

struct TodoGridAdaptPage {

@State todoList: string[] = [

'上班打卡', '写代码', '开会', '健身', '阅读', '陪家人', '购物', '学习鸿蒙'

];

build() {

Column() {

Text('待办清单(Grid自适应)')

.fontSize($r('app.float.title_size')) // 资源分级字体

.fontWeight(FontWeight.Bold)

.padding(15);

// 核心:auto-fit自动适配列数,minmax设置最小宽度

Grid() {

ForEach(this.todoList, (item, index) => {

GridItem() {

Text(item)

.fontSize($r('app.float.item_size'))

.padding(20)

.backgroundColor('#007dff')

.fontColor('#fff')

.borderRadius(12)

.textAlign(TextAlign.Center);

}

})

}

.columnsTemplate('repeat(auto-fit, minmax(140px, 1fr))') // 认证考点

.columnsGap(15)

.rowsGap(15)

.padding(15)

.width('100%')

.flexGrow(1);

}

.width('100%')

.height('100%');

}

}

⚠️ 认证考点标红:repeat(auto-fit, minmax(最小宽度, 1fr))是 Grid 自适应核心,必须掌握其原理 —— 根据屏幕宽度自动计算列数,小屏 1 列、大屏多列。

3. 屏幕尺寸判断:动态调整布局逻辑

通过window模块获取屏幕宽高,判断设备类型,实现 “不同设备不同布局”:


// utils/ScreenAdaptUtil.ets(认证常用工具类)

import window from '@ohos.window';

// 设备类型枚举(认证规范)

export enum DeviceType {

PHONE = 'phone',

TABLET = 'tablet',

CAR = 'car'

}

// 获取屏幕信息与设备类型

export async function getDeviceInfo(context: Context): Promise<{

width: number,

height: number,

deviceType: DeviceType

}> {

const windowClass = await window.getLastWindow(context);

const metrics = await windowClass.getWindowProperties();

const { windowWidth: width, windowHeight: height } = metrics;

// 按屏幕宽度阈值判断设备类型(认证参考标准)

if (width >= 1000 && height 600) { // 车机:宽屏+矮高

return { width, height, deviceType: DeviceType.CAR };

} else if (width >= 800) { // 平板:宽度≥800px

return { width, height, deviceType: DeviceType.TABLET };

} else { // 手机:宽度<800px

return { width, height, deviceType: DeviceType.PHONE };

}

}

// 页面中使用示例

@Entry

@Component

struct DeviceAdaptPage {

@State deviceType: DeviceType = DeviceType.PHONE;

async aboutToAppear() {

const info = await getDeviceInfo(getContext(this));

this.deviceType = info.deviceType;

}

build() {

Column() {

Text(`当前设备:${this.deviceType}`)

.fontSize(20)

.padding(10);

// 不同设备显示不同布局

if (this.deviceType === DeviceType.CAR) {

Text('车机专属布局').fontSize(24);

} else if (this.deviceType === DeviceType.TABLET) {

Text('平板专属布局').fontSize(22);

} else {

Text('手机专属布局').fontSize(18);

}

}

.width('100%')

.height('100%');

}

}

三、设备专属适配:手机 / 平板 / 车机差异化开发

1. 手机适配:竖屏优先 + 单手操作(认证基础)

手机适配核心是 “紧凑、易用、单手可及”,符合鸿蒙手机交互规范:

  • 核心功能放在屏幕中下部(单手操作区);
  • 按钮尺寸≥44px,字体≥16px;
  • 支持下拉刷新、上拉加载等手机常用交互。

// 手机适配示例(待办清单首页)

@Component

struct PhoneTodoPage {

@State todoList: string[] = ['待办1', '待办2', '待办3', '待办4'];

@State inputContent: string = '';

build() {

Column({ space: 10 }) {

// 标题栏(中上部)

Text('手机待办清单')

.fontSize(20)

.fontWeight(FontWeight.Bold)

.padding(15)

.width('100%')

.textAlign(TextAlign.Center);

// 输入区(单手可及)

Row({ space: 10 }) {

TextInput({ placeholder: '输入待办内容' })

.width('75%')

.height(44)

.padding(10)

.backgroundColor('#f5f5f5')

.borderRadius(8);

Button('添加')

.width('20%')

.height(44)

.backgroundColor('#007dff')

.fontColor('#fff');

}

.padding(0, 15)

.width('100%');

// 待办列表(核心区域)

List({ space: 10 }) {

ForEach(this.todoList, (item) => {

ListItem() {

Text(item)

.fontSize(16)

.padding(15)

.backgroundColor('#fff')

.borderRadius(8)

.width('100%');

}

})

}

.padding(0, 15)

.width('100%')

.flexGrow(1);

// 悬浮添加按钮(底部右下角,单手可点)

Button('+')

.width(56)

.height(56)

.borderRadius(28)

.backgroundColor('#007dff')

.fontColor('#fff')

.fontSize(24)

.position({ right: 25, bottom: 25 });

}

.width('100%')

.height('100%')

.backgroundColor('#f9f9f9');

}

}

2. 平板适配:横屏 + 分屏 + 多列展示(认证重点)

平板适配核心是 “利用大屏空间,提升效率”,鸿蒙平板支持分屏操作,需针对性优化:

  • 横屏优先布局;
  • 多列展示数据(如左右分栏:待办 + 已完成);
  • 支持拖拽交互(平板触控优势)。

// 平板适配示例(分栏布局)

@Component

struct TabletTodoPage {

@State todoList: string[] = ['写代码', '开会', '健身', '学习鸿蒙'];

@State completedList: string[] = ['上班打卡', '阅读'];

build() {

// 横屏分栏布局(3:7比例)

Row({ space: 15 }) {

// 左侧:待办列表(30%宽度)

Column({ space: 10 }) {

Text('待办')

.fontSize(22)

.fontWeight(FontWeight.Bold)

.padding(10);

List({ space: 10 }) {

ForEach(this.todoList, (item) => {

ListItem() {

Text(item)

.fontSize(18)

.padding(15)

.backgroundColor('#fff')

.borderRadius(8)

.width('100%');

}

})

}

.width('100%')

.flexGrow(1);

Button('添加待办')

.width('100%')

.height(48)

.backgroundColor('#007dff')

.fontColor('#fff')

.margin(10);

}

.width('30%')

.padding(15)

.backgroundColor('#f5f5f5')

.borderRadius(12);

// 右侧:已完成列表(70%宽度)

Column({ space: 10 }) {

Text('已完成')

.fontSize(22)

.fontWeight(FontWeight.Bold)

.padding(10);

List({ space: 10 }) {

ForEach(this.completedList, (item) => {

ListItem() {

Text(item)

.fontSize(18)

.padding(15)

.backgroundColor('#fff')

.borderRadius(8)

.width('100%')

.decoration({ type: TextDecorationType.LineThrough })

.fontColor('#999');

}

})

}

.width('100%')

.flexGrow(1);

}

.width('70%')

.padding(15);

}

.width('100%')

.height('100%')

.backgroundColor('#f9f9f9');

}

}

3. 车机适配:车载场景专属优化(认证高频)

车机适配是鸿蒙全场景的核心亮点,也是认证重点,核心原则:安全、简化、语音优先(驾驶场景第一优先级):

  • 大字体(≥24px)、大按钮(≥60px),减少操作失误;
  • 简化布局,只保留核心功能(如待办查看、语音添加);
  • 支持语音交互(鸿蒙语音助手);
  • 适配车机宽屏比例(16:9/21:9)。

// 车机适配示例(车载待办清单)

import voiceAssistant from '@ohos.voiceAssistant'; // 语音助手模块

@Component

struct CarTodoPage {

@State todoList: string[] = ['上班打卡', '接孩子', '加油', '买 groceries'];

// 语音添加待办(车机核心交互)

async addTodoByVoice() {

try {

// 调用鸿蒙系统语音助手

const result = await voiceAssistant.recognize({

language: 'zh-CN',

scene: 'car' // 车载场景优化

});

if (result.text) {

this.todoList.push(result.text);

promptAction.showToast({ message: `已添加:${result.text}` });

}

} catch (err) {

console.error(`语音识别失败:${err.message}`);

promptAction.showToast({ message: '语音添加失败' });

}

}

build() {

// 车机宽屏布局(横向居中)

Column({ space: 30 }) {

// 标题:大字体+高对比度

Text('车载待办清单')

.fontSize(32) // 车机标题≥32px

.fontWeight(FontWeight.Bold)

.padding(25)

.textAlign(TextAlign.Center)

.width('100%');

// 待办列表:大按钮+间距放大

List({ space: 25 }) {

ForEach(this.todoList, (item) => {

ListItem() {

Text(item)

.fontSize(28) // 列表字体≥28px

.padding(30)

.backgroundColor('#007dff')

.fontColor('#fff')

.borderRadius(16)

.width('100%')

.textAlign(TextAlign.Center);

}

})

}

.width('90%')

.flexGrow(1);

// 语音添加按钮:超大尺寸+醒目颜色

Button('语音添加待办')

.width('85%')

.height(90) // 车机按钮≥80px

.fontSize(26)

.backgroundColor('#00c853')

.fontColor('#fff')

.borderRadius(45)

.onClick(() => this.addTodoByVoice())

.margin({ bottom: 40 });

}

.width('100%')

.height('100%');

}

}
车机适配额外注意事项(认证踩分点):
  • 避免复杂交互:禁止滑动切换页面(驾驶场景危险),优先用按钮跳转;
  • 颜色规范:背景用深色(减少反光),前景色用高对比度(白色 / 亮蓝色);
  • 语音优先:核心功能必须支持语音控制(如语音添加待办、语音标记完成);
  • 适配车机分辨率:常见车载屏比例 16:9、21:9,布局用 flexGrow 自适应,避免固定宽高比。

四、资源分级适配:多设备差异化资源加载(认证 2 分)

鸿蒙支持 “资源目录分级”,自动根据设备类型加载对应资源(图片、字体、尺寸等),无需手动判断,是认证必考点。

1. 资源目录结构(项目中创建)


main_pages
├── resources
│   ├── base
│   │   ├── element
│   │   │   ├── float.json
│   │   │   └── string.json
│   │   └── media
│   ├── phone
│   │   ├── element
│   │   │   └── float.json
│   │   └── media
│   ├── tablet
│   │   ├── element
│   │   │   └── float.json
│   │   └── media
│   └── car
│       ├── element
│       │   └── float.json
│       └── media
 

2. 资源定义示例(float.json)

基础资源(base/float.json):

{

"title_size": 20,

"item_size": 16,

"padding_small": 10,

"padding_large": 20

}
手机专属资源(device.phone/float.json):

{

"title_size": 22,

"item_size": 17,

"padding_small": 12,

"padding_large": 25

}
车机专属资源(device.car/float.json):

{

"title_size": 36,

"item_size": 28,

"padding_small": 30,

"padding_large": 45

}

3. 资源使用方式(自动适配)

在组件中通过 $r('app.float.xxx') 引用资源,系统会根据当前设备自动加载对应目录的配置:


// 自动适配设备的字体尺寸

Text('待办清单')

.fontSize($r('app.float.title_size')) // 手机→22px,平板→24px,车机→36px

.padding($r('app.float.padding_large'));

4. 图片资源适配(认证考点)

不同设备用不同分辨率图片,避免模糊或内存浪费:

  • 手机:图片分辨率 720P/1080P;
  • 平板:图片分辨率 1080P/2K;
  • 车机:图片分辨率 2K/4K(大屏需求);
  • 放置规则:将图片分别放入 device.phone/media、device.tablet/media 等目录,文件名保持一致,系统自动加载。

五、多设备统一入口:动态渲染布局(实战核心)

通过 ScreenAdaptUtil 判断设备类型,在同一个页面中动态渲染对应布局,无需创建多个页面文件,开发效率更高:


// 多设备统一入口页面(认证实操必写)

@Entry

@Component

struct TodoMultiDevicePage {

@State deviceType: DeviceType = DeviceType.PHONE;

@State todoList: string[] = ['上班打卡', '写代码', '开会', '健身', '阅读'];

@State completedList: string[] = ['学习鸿蒙'];

async aboutToAppear() {

// 初始化时获取设备类型

const info = await getDeviceInfo(getContext(this));

this.deviceType = info.deviceType;

}

// 通用添加待办方法(复用逻辑)

addTodo(content: string) {

if (content.trim()) {

this.todoList.push(content);

}

}

build() {

Column() {

// 统一标题(资源自适应)

Text($r('app.string.todo_title'))

.fontSize($r('app.float.title_size'))

.fontWeight(FontWeight.Bold)

.padding($r('app.float.padding_large'))

.width('100%')

.textAlign(TextAlign.Center);

// 根据设备类型动态渲染布局

if (this.deviceType === DeviceType.PHONE) {

// 手机布局

Column({ space: 15 }) {

// 输入区

Row({ space: 10 }) {

TextInput({ placeholder: '输入待办内容' })

.width('75%')

.height(44)

.padding(10)

.backgroundColor('#f5f5f5')

.borderRadius(8);

Button('添加')

.width('20%')

.height(44)

.backgroundColor('#007dff')

.fontColor('#fff')

.onClick(() => this.addTodo((.$element('input')).value));

}

.padding(0, 15);

// 待办列表

List({ space: 10 }) {

ForEach(this.todoList, (item) => {

ListItem() {

Text(item)

.fontSize($r('app.float.item_size'))

.padding(15)

.backgroundColor('#fff')

.borderRadius(8)

.width('100%');

}

})

}

.padding(0, 15)

.flexGrow(1);

// 悬浮按钮

Button('+')

.width(56)

.height(56)

.borderRadius(28)

.backgroundColor('#007dff')

.fontColor('#fff')

.position({ right: 25, bottom: 25 });

}

.width('100%')

.flexGrow(1);

} else if (this.deviceType === DeviceType.TABLET) {

// 平板分栏布局

Row({ space: 15 }) {

// 左侧待办

Column({ space: 10 }) {

Text('待办')

.fontSize($r('app.float.item_size'))

.fontWeight(FontWeight.Bold)

.padding(10);

List({ space: 10 }) {

ForEach(this.todoList, (item) => {

ListItem() {

Text(item)

.fontSize($r('app.float.item_size'))

.padding(15)

.backgroundColor('#fff')

.borderRadius(8)

.width('100%');

}

})

}

.flexGrow(1);

Button('添加待办')

.width('100%')

.height(48)

.backgroundColor('#007dff')

.fontColor('#fff')

.margin(10);

}

.width('30%')

.padding(15)

.backgroundColor('#f5f5f5')

.borderRadius(12);

// 右侧已完成

Column({ space: 10 }) {

Text('已完成')

.fontSize($r('app.float.item_size'))

.fontWeight(FontWeight.Bold)

.padding(10);

List({ space: 10 }) {

ForEach(this.completedList, (item) => {

ListItem() {

Text(item)

.fontSize($r('app.float.item_size'))

.padding(15)

.backgroundColor('#fff')

.borderRadius(8)

.width('100%')

.decoration({ type: TextDecorationType.LineThrough })

.fontColor('#999');

}

})

}

.flexGrow(1);

}

.width('70%')

.padding(15);

}

.width('100%')

.flexGrow(1);

} else if (this.deviceType === DeviceType.CAR) {

// 车机布局

Column({ space: 30 }) {

List({ space: 25 }) {

ForEach(this.todoList, (item) => {

ListItem() {

Text(item)

.fontSize($r('app.float.item_size'))

.padding(30)

.backgroundColor('#007dff')

.fontColor('#fff')

.borderRadius(16)

.width('100%')

.textAlign(TextAlign.Center);

}

})

}

.width('90%')

.flexGrow(1);

Button('语音添加待办')

.width('85%')

.height(90)

.fontSize($r('app.float.item_size'))

.backgroundColor('#00c853')

.fontColor('#fff')

.borderRadius(45)

.onClick(() => this.addTodoByVoice())

.margin({ bottom: 40 });

}

.width('100%')

.flexGrow(1);

}

}

.width('100%')

.height('100%')

.backgroundColor('#f9f9f9');

}

// 车机语音添加待办(复用前文方法)

async addTodoByVoice() {

try {

const voiceAssistant = await import('@ohos.voiceAssistant');

const result = await voiceAssistant.recognize({

language: 'zh-CN',

scene: 'car'

});

if (result.text) {

this.addTodo(result.text);

promptAction.showToast({ message: `已添加:${result.text}` });

}

} catch (err) {

console.error(`语音识别失败:${err.message}`);

promptAction.showToast({ message: '语音添加失败' });

}

}

}

六、认证核心考点总结与踩分技巧

1. 必背考点(占 14 分)

考点

实操要求

扣分点预警

弹性布局

用flexGrow/ 百分比宽度替代固定 px

用固定 px 布局,直接扣 5 分

Grid 自适应

掌握repeat(auto-fit, minmax())用法

写死列数(如columnsTemplate: '3fr')扣 3 分

设备类型识别

用window模块获取屏幕尺寸判断设备

硬编码设备类型(如if (device === 'phone'))扣 2 分

资源分级

正确创建device.xxx资源目录并引用

未分级资源,扣 2 分

车机适配规范

大字体(≥24px)、大按钮(≥80px)、语音支持

车机布局用小字体 / 小按钮,扣 2 分

2. 实操考试评分标准(以 “待办清单适配” 为例)

评分项

分值

达标要求

布局自适应

5 分

手机 / 平板 / 车机布局无拉伸、无溢出

资源适配

3 分

字体、图片根据设备自动切换

设备差异化逻辑

3 分

手机有悬浮按钮、平板分栏、车机语音支持

交互规范

2 分

符合各设备交互规范(如车机无滑动交互)

代码规范性

1 分

代码复用(如通用添加待办方法)

七、常见避坑指南(认证高频错误)

  1. 横竖屏切换布局错乱:未在module.json5中配置configChanges: ["orientation", "screenSize"],需补充配置;
  2. 资源加载失败:资源目录命名错误(如device.phone写成phone),需严格按照device.设备类型命名;
  3. 车机布局溢出:车机屏幕宽高比特殊(如 21:9),用固定宽高比布局导致溢出,需用flexGrow自适应;
  4. 平板分屏异常:未设置windowMode: "windowed",平板分屏时布局崩溃,需在module.json5的 ability 中配置;
  5. 代码冗余:为不同设备创建多个页面文件(如PhoneTodoPage.ets、TabletTodoPage.ets),建议用动态渲染统一入口。

八、总结

鸿蒙全场景适配的核心是 “自适应为基础,差异化为补充”—— 先用 Flex/Grid 实现通用适配,再针对手机、平板、车机的场景特性做优化。只要掌握 “布局弹性化、资源分级化、设备差异化” 三大技巧,就能轻松应对认证 14% 的考点,同时做出符合鸿蒙生态的全场景应用。

九、班级学习

目前班级正火热招募学员!加入班级:https://developer.huawei.com/consumer/cn/training/classDetail/6b5617ffd9264d6daa2f3d9250204f1e?type=1%3Fha_source%3Dhmosclass&ha_sourceId=89000248 ,跟着系列文章系统学,快速掌握鸿蒙开发核心技能,冲刺 HarmonyOS 应用开发者认证!无论你是零基础入门,还是在职开发者提升,都能在班级中收获成长,抢占鸿蒙生态人才红利~

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐