本章将介绍 DevEco Studio 的工程开发技巧,以及如何使用预览器和模拟器查看项目的运行效果。
在开发过程中,开发者常常需要对应用进行修改和迭代,比如增加新功能、调整应用配色,或修改某个组件的样式。借助DevEco Studio 的预览器,开发者可以在无需重新构建整个项目的情况下查看应用的修改后的效果。在某些需要使用到系统能力场景下,开发者可以使用 DevEco Studio 的模拟器所提供的能力,快速验证应用在不同设备和系统环境下的运行效果。
4.1 自定义组件预览
创建一个名为MyPreview 的新的 HarmonyOS 项目,并打开工程开发面板。
在pages 文件夹下新建一个名为CardItemView的ArkTS文件,并实现基础的详情页 UI,代码如下:
//第4章/CardItemView.ets
@Component
export struct CardItemView {
@Prop image: Resource
@Prop title: string
@Prop describe: string
build() {
Column({ space: 10 }) {
Image(this.image)
.objectFit(ImageFit.Contain)
.width('100%')
.borderRadius(16)
Text(this.title)
.fontSize(23)
.fontWeight(FontWeight.Bold)
Text(this.describe)
.fontSize(14)
.margin(10)
}
.margin(10)
.borderWidth(1)
.borderColor('#D5D5D5')
.borderRadius(16)
}
}
自定义组件CardItemView包含 3 个属性:image、title、describe,用于显示详情页的图片、图片标题、图片描述的内容。使用@Prop 装饰器对属性进行装饰,使组件属性支持从父级视图传递参数值,从而渲染不同的 UI。
使用注释装饰器@Preview 构建一个预览视图MyPreview,将CardItemView 组件作为预览组件放置在MyPreview组件的build()函数中,并为CardItemView 组件的参数赋予初始值,代码如下:
//第4章/CardItemView.ets
@Component
export struct CardItemView {...}
@Preview({title:'CardItemView'})
@Component
struct MyPreview {
build() {
CardItemView({
image:$r('app.media.WatermelonJuice'),
title:'Watermelon Juice',
describe:'Cool down with nature's sweetest hydration! Our freshly blended watermelon juice is a vibrant, thirst-quenching delight packed with vitamins A and C.'
})
}
}
打开预览器,选择预览器顶部导航栏中的“组件预览”选项,开发者可对单个自定义组件进行实时预览,如图 4-1 所示。
图 4-1 自定义组件预览
4.2 自定义页面预览
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。
@Entry装饰器允许开发者将自定义组件作为UI页面的入口,在每一个HarmonyOS项目模板中Index.ets中都会使用@Entry装饰器进行入口定义。通过这种方式,开发者可以定义一个页面的初始结构和布局,从而实现对整个应用界面的控制和展示。
4.2.1 导入图片素材
将下载好的图片素材拖放media文件夹中,路径为entry/src/main/resources/base/media。如图 4-2 所示。
图 4-2 图片素材准备
4.2.2 实现DrinkModel接口
在 ets 目录下新建一个名为 model 的目录,并新建一个名为DrinkModel的ArkTS文件,在该文件下定义一个名为 DrinkModel的接口,用于描述卡片对象的结构和参数,每个卡片项目都有一个唯一的标识符、图片名称、图片标题、图片描述信息 4 个参数,代码如下:
//第4章/DrinkModel.ets
export interface DrinkModel {
id: number
image: Resource
title: string
describe: string
}
通过const声明一个DrinkModel 类型的数组drinkData用于存储初始的卡片内容,并创建 5 个卡片项目数据,代码如下:
//第4章/DrinkModel.ets
export const drinkData: DrinkModel[] = [
{
id: 1,
image: $r('app.media.WatermelonJuice'),
title: 'Watermelon Juice',
describe: 'Freshly blended watermelon with no added sugar, rich in vitamins A and C. Perfect summer refresher!'
},
{
id: 2,
image: $r('app.media.IcedLemonTea'),
title: 'Iced Lemon Tea',
describe: 'Classic black tea with freshly squeezed lemon juice and a hint of honey.'
},
{
id: 3,
image: $r('app.media.MangoSmoothie'),
title: 'Mango Smoothie',
describe: 'Creamy blend of ripe mangoes, yogurt and a dash of cinnamon. Dairy-free option available.'
},
{
id: 4,
image: $r('app.media.IcedMocha'),
title: 'Iced Mocha',
describe: 'Espresso combined with dark chocolate and cold milk, topped with whipped cream.'
},
{
id: 5,
image: $r('app.media.GreenDetox'),
title: 'Green Detox',
describe: 'Kale, spinach, green apple and lemon juice for a nutrient-packed energy boost.'
}
]
4.2.3 预览Index页面
回到 Index 视图中,由于drinkData 和DrinkModel归属于model目录,CardItemView组件归属于CardItemView.ets文件,因此在使用时需要使用import 关键字在 Index.ets 文件中引入相关文件,代码如下:
import { drinkData, DrinkModel } from '../model/DrinkModel'
import { CardItemView } from './CardItemView'
在 build()函数中使用 @State关键字声明一个DrinkModel[]类型的数组drinks,用于初始化来源于drinkData的数据。使用List 组件和ListItem 子组件,通过ForEach()函数通过drinks 数组来遍历数据,并渲染CardItemView 组件,代码如下:
//第4章/Index.ets
@Entry
@Component
struct Index {
@State drinks: DrinkModel[] = drinkData
build() {
List({ space: 10 }) {
ForEach(this.drinks, (item: DrinkModel) => {
ListItem() {
CardItemView({
image:item.image,
title:item.title,
describe:item.describe
})
}
})
}
.width('100%')
.height('100%')
}
}
CardItemView 组件在接收到DrinkModel 类型的item 参数后,将其传分别传递给image、 title、describe参数,用于显示卡片项的内容,如图 4-3 所示。
图 4-3 自定义页面预览
4.2.4 切换横屏预览
当应用使用横屏开发场景时,开发者可以点击预览器中设备顶部操作上中间的“屏幕方向”按钮来切换应用预览的方向,如图 4-4 所示。
图 4-4 横屏预览效果
4.2.5 开启多设备预览
由于不同设备的屏幕分辨率、形状、大小等不同,开发者常常需要在不同的设备上查看应用的UI布局和交互效果。
在预览器中,开发者可以打开预览器顶部操作栏中的 Profile Manager 选项,点击打开Multi-profile preview开关,即可同时在预览器窗口中查看多设备上的应用运行效果。如图 4-6 所示。
图 4-6 多设备预览
4.2.6 双向预览
Inspector是DevEco Studio中的一个内置工具,用于调试和优化 UI 界面,开发者在编写代码时,Inspector会实时渲染代码对应的UI界面,帮助开发者快速查看效果。
在预览器中,开发者可以点击预览器顶部操作栏中的Inspector选项,开启双向预览功能后,开发者将可以看到代码编辑器、预览器和组件树三者之间的联动变化,如图 4-7 所示。
图 4-7 双向预览功能
4.3 实现页面导航
页面导航是指在应用程序中从一个页面跳转到另一个页面的过程,在HarmonyOS 应用开发中,推荐开发者使用Navigation 来实现页面导航的相关功能。
创建一个名为MyNavigation 的新的 HarmonyOS 项目,并打开工程开发面板。
4.3.1 导入图片素材
将下载好的图片素材拖放media文件夹中,路径为entry/src/main/resources/base/media。如图 4-8 所示。
图 4-8 导入素材
4.3.2 实现IndexView页面
使用Column 组件作为页面父级容器,使用Blank、Image、Text、Button 基础组件构建UI,代码如下:
@Entry
@Component
struct Index {
@State welcomeMessage: string = 'I hope you like this App ❤️'
build() {
Column({space:10}) {
this.contentView()
Blank()
Button('Get Started', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.backgroundColor(Color.Blue)
.width('90%')
.onClick(() => {})
}
.height('100%')
.width('100%')
}
@Builder contentView() {
Column() {
Image($r('app.media.wacoom'))
.objectFit(ImageFit.Contain)
.width('90%')
Text('Hi,Welcome')
.fontSize(23)
.fontWeight(FontWeight.Bold)
Text(this.welcomeMessage)
}
.margin({top:32})
}
}
自定义组件contentView 用于构建主体内容区域的UI,其中 Image 组件加载导入的图片素材wacoom,并使用 objectFit 确保图片按比例适应布局。Text 组件用于显示欢迎标题和欢迎语,分别通过 fontSize 和 fontWeight 修饰字体样式。自定义组件contentView 整体内容通过 Column 组件排列,并添加 margin 调整间距,使界面结构清晰,布局紧凑美观。
在IndexView页面的构建上,使用 Column 组件进行垂直布局,并显示自定义组件contentView 和Button 组件。在预览器中,开发者可以预览 IndexView 页面的 UI,如图 4-9 所示。
图 4-9 IndexView 页面预览
4.3.3 实现DetailView页面
在 page 目录下新建一个名为DetailView 的 ArkTS 文件,使用@Component 装饰器创建一个自定义组件DetailView,并实现DetailView页面的UI,代码如下:
@Component
export struct DetailView {
@Prop param:string
build() {
Column({space:10}) {
Blank()
Text('This is DetailView')
Text(this.param)
Blank()
Button('Back', { type: ButtonType.Capsule, stateEffect: true })
.backgroundColor(Color.Green)
.width('60%')
.onClick(() => {})
}
.height('100%')
.width('100%')
}
}
DetailView 组件使用Column 组件作为页面父级容器,使用Blank、Text、Button 基础组件构建UI,其中param 参数用于从父级视图传入参数值来动态显示文字内容。
4.3.4 实现页面跳转
回到 Index.ets 文件中,声明一个NavPathStack类型的状态变量作为控制器来管理Navigation组件,该控制器用于与导航组件进行绑定,代码如下:
@Provide('NavPathStack') pageStack: NavPathStack = new NavPathStack()
@Provide 装饰器用于将指定变量实现共享和操作导航堆栈,从而实现全局一致的导航状态管理。pageStack表示导航路径堆栈,初始值为一个新的NavPathStack实例。
下一步,使用Navigation 组件为IndexView页面构建页面导航,设置导航标题及目标路径,代码如下:
import { DetailView } from './DetailView'
@Entry
@Component
struct Index {
@Provide('NavPathStack') pageStack: NavPathStack = new NavPathStack()
@State welcomeMessage: string = 'I hope you like this App ❤️'
build() {
Navigation(this.pageStack) {
Column({space:10}) {
this.contentView()
Blank()
Button('Get Started', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.backgroundColor(Color.Blue)
.width('90%')
.onClick(() => {
this.pageStack.pushPath({ name: 'DetailView',param:this.welcomeMessage })
})
}
.height('100%')
.width('100%')
}
.title('IndexView')
.navDestination(this.pagesMap)
}
@Builder contentView() {...}
@Builder pagesMap(name: string,param: string) {
if (name == 'DetailView') {
DetailView({param:param})
}
}
}
Navigation 组件需要绑定pageStack控制器,用于实现页面的导航管理,包括页面入栈、出栈以及返回等功能。使用title 修饰器构建页面标题,使用navDestination 修饰器构建页面跳转的目标页面。navDestination 修饰器指向一个自定义的路径pagesMap,开发者可以使用@Builder 实现自定义路径,并通过传入参数的方式动态指定跳转的目标页面。
在Button 的点击事件中,使用pageStack的pushPath方法实现页面的跳转功能,在传入跳转页面的名称和参数,实现页面跳转的同时,将指定的参数值传递给目标页面。
完成后,在预览器中查看页面导航及跳转交互效果,如图 4-10 所示。
图4-10 IndexView导航预览
4.3.5 实现页面返回
来到DetailView.ets 文件,使用NavDestination 组件作为目标页面的父级容器,并使用@Consume装饰器将pageStack 注入到服务中,代码如下:
@Component
export struct DetailView {
@Consume('NavPathStack') pageStack: NavPathStack
@Prop param:string
build() {
NavDestination() {
Column({space:10}) {
Blank()
Text('This is DetailView')
Text(this.param)
Blank()
Button('Back', { type: ButtonType.Capsule, stateEffect: true })
.backgroundColor(Color.Green)
.width('60%')
.onClick(() => {
this.pageStack.pop()
})
}
.height('100%')
.width('100%')
}
.title('DetailView')
}
}
在Button 的点击事件中,使用pageStack的pop方法实现页面的返回功能。
在预览器中,开发者可以预览页面跳转到目标页面的交互和传参效果,并可通过点击DetailView导航的“返回”按钮或底部的 back 按钮回到IndexView页面,如图 4-11 所示。
图 4-11 DetailView导航预览
4.4 服务卡片预览
服务卡片是 HarmonyOS 提供的一种轻量级的交互界面,它以卡片的形式展示应用的核心功能或重要信息。用户无需打开应用,直接通过服务卡片即可快速获取信息或进行简单操作。
创建一个名为MyWidget的新的 HarmonyOS 项目,并打开工程开发面板。
4.4.1 创建服务卡片
点击鼠标左键选中src 目录,点击鼠标右键右键以此选择“新建” > Service Widget > Static Widget,在“模版选择”弹窗中选中 Hello World 模版,如图 4-12 所示。
图 4-12 选择卡片模版
点击Next按钮,系统会打开Configure Your Service Widget 弹窗,在该弹窗中完善服务卡片的配置信息,如图 4-13 所示。
图 4-13 配置服务卡片信息
服务卡片配置信息说明如下:
- Service widget name:卡片的名称,默认名称为widget;
- Display name:卡片预览面板上显示的卡片名称,默认名称为widget;
- Description:卡片的描述信息,默认内容为This is a service widget;
- Language:开发语言,默认为 ArkTS;
- Support dimension:卡片的规格,默认为 22,开发者可以勾选 12 和 2*4;
- Default dimension:初始的卡片规格;
- Ability name:服务卡片的Form Ability;
- Module name:卡片所属的模块;
点击 Finish 按钮,DevEco Studio 将自动在 ets 目录下创建一个新的服务卡片项目。
4.4.2 预览WidgetCard页面
打开 widget 目录下的WidgetCard.ets 文件,该文件是服务卡片的初始页面文件。打开预览器,开发者可以看到,服务卡片提供3种场景的预览效果,分别为极窄(Minimum)、默认(Default)、极宽(Maximum),如图 4-14 所示:
图 4-14 服务卡片预览
在实际开发过程中,开发者需要关注服务卡片在三种不同尺寸下的显示效果,以便适应不同屏幕尺寸的设备,为用户带来更好的用户体验。
4.5 预览场景模拟
在实际项目中,由于预览场景与真机运行环境之间的差异问题,有可能导致在预览时调用某些接口无法返回真实的值的情况。为解决这一问题,HarmonyOS提供了一个实现预览场景的模拟功能的工具— — Hamock。
Hamock的核心功能是通过模拟接口和数据,让开发者能够在预览环境中获得更接近真实运行环境的体验。它允许开发者在不改变业务逻辑的情况下,对 UI 组件的属性、方法或导入的模块接口进行模拟。简单来说,Hamock仅在预览环境生效,不影响真机运行。
创建一个名为MyMockData的新的 HarmonyOS 项目,并打开工程开发面板。
4.5.1 添加Hamock依赖
由于使用到Hamock工具,开发者需要在项目的基础配置文件中添加依赖,文件路径为entry/oh-package.json5,代码如下:
"devDependencies": {
"@ohos/hamock": "1.0.0"
}
4.5.2 实现电量信息显示
@kit.BasicServicesKit是一个封装了电池状态和充放电状态查询接口的模块,使用该模块可以获取实时的设备电池状态和充电信息。导入@kit.BasicServicesKit模块中的batteryInfo工具接口到文件中,代码如下:
import { batteryInfo } from '@kit.BasicServicesKit'
声明number类型的状态变量level、status,表示电池的剩余电量百分比和充电状态,并分别初始化为batteryInfo.batterySOC 和 batteryInfo.chargingStatus的值,代码如下:
@State level: number = batteryInfo.batterySOC
@State status: number = batteryInfo.chargingStatus
使用enum 关键字声明ChargingStatus枚举,用于定义电池充电状态的不同可能值。并在创建 Index 中创建getStatusText()方法根据充电状态返回对应的文本描述,代码如下:
import { batteryInfo } from '@kit.BasicServicesKit'
enum ChargingStatus {
NONE = 0,
ENABLE = 1,
DISABLE = 3,
FULL = 4
}
@Entry
@Component
struct Index {
build() {...}
getStatusText(): string {
switch (this.status) {
case ChargingStatus.ENABLE: return '充电中'
case ChargingStatus.DISABLE: return '停止充电'
case ChargingStatus.FULL: return '已充满'
default: return '状态未知'
}
}
}
ChargingStatus枚举中,NONE表示充电状态未知,ENABLE表示设备当前正在充电,DISABLE表示设备已经停止充电,FULL表示电池已经充满。
getStatusText()方法使用switch-case语句对多个条件进行逻辑判断,switch-case语句的作用是根据变量的值执行不同的代码块。比如当status 的值为ChargingStatus.ENABLE 时,ChargingStatus.ENABLE 返回的文字是“充电中”。
4.5.3 实现页面UI
使用Column、Row组件作为父级容器,使用SymbolGlyph、Text、Blank基础组件构建UI,代码如下:
import { batteryInfo } from '@kit.BasicServicesKit'
enum ChargingStatus {...}
@Entry
@Component
struct Index {
@State level: number = batteryInfo.batterySOC
@State status: number = batteryInfo.chargingStatus
build() {
Column() {
Row({space:10}) {
SymbolGlyph($r('sys.symbol.battery_75percent'))
.fontSize(23)
Column({space:5}) {
Text('电池')
.fontSize(17)
Text(this.getStatusText())
.fontSize(14)
}.alignItems(HorizontalAlign.Start)
Blank()
Text(`${this.level}%`)
.fontSize(17)
.fontWeight(FontWeight.Bold)
}
.width('80%')
.padding(20)
.backgroundColor('#F5F5F5')
.borderRadius(16)
}
.width('100%')
.height('100%')
}
getStatusText(): string {...}
}
Text 组件使用模板字符串方法,将level的值转换为带%的文字格式。由于level变量使用@State 装饰器装饰,当level 的值发生变化时,ArkUI 会自动触发系统UI更新。下一个Text 组件则直接调用getStatusText()方法,显示设备当前的充电状态文字。
此时在预览器中,由于预览器无法调用batteryInfo 接口获得真实的电量信息,因此电量与充电状态文字分别 0 和未知。如图 4-15 所示。
图 5-15 电量信息预览
而在模拟器中,由于模拟真机允许调用系统接口,因此开发者可以在模拟器中查看真实环境下的电量信息,如图 4-16 所示。
图 4-16 模拟环境下预览效果
4.5.4 使用Hamock预览场景
为了使得在预览器中预览效果与模拟器中预览效果接近,提高开发体验,在Index.ets 文件中导入Hamock工具,代码如下:
import { MockSetup } from '@ohos/hamock'
使用@MockSetup 装饰器声明一个自定义方法mockBehavior(),并在该方法中为level、status 参数赋值,代码如下:
import { batteryInfo } from '@kit.BasicServicesKit'
import { MockSetup } from '@ohos/hamock'
enum ChargingStatus {...}
@Entry
@Component
struct Index {
@State level: number = batteryInfo.batterySOC
@State status: number = batteryInfo.chargingStatus
@MockSetup mockBehavior() {
this.level = 75
this.status = 1
}
build() {
Column() {...}
getStatusText(): string {...}
}
@MockSetup装饰器用于在应用生命周期开启前自动触发自定义方法,该方法仅在预览场景下生效,而不会生效在应用实际运行场景中。
在预览器中,开发者可以看到UI预览场景下的模拟效果,如图 4-17 所示。
图 4-17 Hamock 预览场景效果
4.6 本章小结
本章介绍了 ArkUI 的多种预览方式,包括自定义组件预览、页面导航、服务卡片预览和场景模拟。通过灵活的预览功能,开发者可以在不同设备和屏幕方向下查看 UI 效果,提升调试效率。同时,利用页面导航实现的页面跳转与返回等功能,使多页面之间的交互开发更加顺畅。
此外,本章还引入了场景模拟技术,使复杂交互的测试更加直观。通过本章的学习,开发者可以掌握 ArkUI 预览的核心方法,在开发阶段快速验证 UI 设计,提升项目开发效率和用户体验。