阅读视图

发现新文章,点击刷新页面。

鸿蒙Next实战:烟花模拟器

前言

通过上一篇文章可以看出,要在鸿蒙应用中实现特别炫的特效还是比较复杂。动画固然重要,但如果在赶工期的情况下,大家都会优先业务,那有没有简单快速的方法呢?

有的,也用像 Android 和 iOS 里 WebView 的方式,h5 的特效现在是应有尽有,把他嵌入鸿蒙 Next 应用里就可以,那如何在鸿蒙 Next 中使用 WebView 来实现电子烟花?

img

实现要点

  1. 组件拆解
  2. 资源引入
  3. 页面路由
  4. WebView

img

开始实践

因为前面的木鱼和现在的烟花都是同一个小工具应用,公用组件的拆分、页面跳转和资源的引入全有涉及,所以就连同 WebView 一起总结一下。

组件拆解

在 ArkUI 中,UI 显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。这里我们将所有页面的导航拆分成一个公用组件,并定义为 HdNav.ets。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import { router } from '@kit.ArkUI'

@Component
export struct HdNav {
@StorageProp('topHeight')
topHeight: number = 0
@Prop
title: string = 'hello world'
@Prop
hasBorder: boolean = false
@Prop
leftIcon: ResourceStr = $r('app.media.ic_common_back')
@Prop
rightIcon: ResourceStr = $r('sys.media.ohos_ic_public_more')
@Prop
showRightIcon: boolean = true
@Prop
iconColor: ResourceStr = $r('app.color.black')
@Prop
titleColor: string = '#131313'
@BuilderParam
menuBuilder: () => void = this.defaultMenu

@Builder
defaultMenu() {

}

build() {
Row({ space: 16 }) {
Image(this.leftIcon)
.size({ width: 24, height: 24 })
.onClick(() => router.back())
.fillColor(this.iconColor)
Row() {
if (this.title) {
Text(this.title)
.fontWeight(600)
.fontColor(this.titleColor)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.fontSize(18)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
}
.height(56)
.layoutWeight(1)

if (this.showRightIcon) {
Image(this.rightIcon)
.size({ width: 24, height: 24 })
.objectFit(ImageFit.Contain)
.bindMenu(this.menuBuilder)
} else {
Blank()
.width(24)
}
}

.padding({ left: 16, right: 16, top: this.topHeight })
.height(56 + this.topHeight)
.width('100%')
.border({
width: { bottom: this.hasBorder ? $r('app.float.common_border_width') : 0 },
color: $r('app.color.common_gray_bg')
})
}
}

资源引入

应用开发过程中,经常需要用到颜色、字体、间距、图片等资源,在不同的设备或配置中,这些资源的值可能不同。

  • 应用资源:借助资源文件能力,开发者在应用中自定义资源,自行管理这些资源在不同的设备或配置中的表现。
  • 系统资源:开发者直接使用系统预置的资源定义。
1
2
3
4
5
6
7
8
# 引入resouces/base/media下的home_selected的图片
$r('app.media.home_selected')

# 导入resources/rawfile下的index.html文件
$rawfile("index.html")

# 获取resources/rawfile下的audio.mp3音频
await getContext(this).resourceManager.getRawFd("audio.mp3")

页面路由

页面路由 router 根据页面的 uri 找到目标页面,从而实现跳转。以最基础的两个页面之间的跳转为例,具体实现步骤如下:

  1. 在 “Project“窗口,打开 src> main >ets,右键点击 pages 文件夹,创建一个页面。
  2. 在 resources/base/profile 下的 main_pages.json,新建一个 pages 中创建页面的文件名(注意大小写)。
  3. 调用 router.push () 路由到指定页面。
  4. 调用 router.back () 回到首页。

img

WebView

页面加载是 Web 组件的基本功能。根据页面加载数据来源可以分为三种常用场景,包括加载网络页面、加载本地页面、加载 HTML 格式的富文本数据。

页面加载过程中,若涉及网络资源获取,需要配置 ohos.permission.INTERNET 网络访问权限,下面以本地静态文件的方法举例。

  • 将资源文件放置在应用的 resources/rawfile 目录下。

img

  • 鸿蒙 Next 应用代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import web_webview from '@ohos.web.webview';
import { HdNav } from '@mygames/basic';

@Entry
@Component
struct WebComponent {
controller: web_webview.WebviewController = new web_webview.WebviewController();

build() {
Column() {
HdNav({ title: '看烟花秀', showRightIcon: false, iconColor: $r('app.color.black') })

Button('loadData')
.onClick(() => {
try {
this.controller.loadUrl($rawfile("index.html"));

} catch (error) {
console.error(`ErrorCode: ${error.code}, Message: ${error.message}`);
}

})
// 组件创建时,加载www.example.com
Web({ src: $rawfile("index.html"), controller: this.controller })
}
}
}
  • 烟花代码

img

写在后面

到这里鸿蒙 Next 应用实战暂告一段落了。但是鸿蒙系统提供了开箱即用的原生 AI 能力,更方便了我们开发者实现应用的快速智能化,所以,鸿蒙 Next 智能应用实战,待续~

鸿蒙Next实战:电子木鱼

前言

正所谓:Hello Word 是程序员学任何一门语言的第一个程序实践。这其实也是一个不错的正反馈,那如何让学习鸿蒙 Next 更有成就感呢?下面就演示一下从零开发一个鸿蒙 Next 版的电子木鱼,主打就是一个抽象!

img

实现要点

  1. 页面布局
  2. 木鱼点击
  3. 木鱼音效
  4. 动画特效
  5. 自定义弹窗

img

开始实践

页面布局

ArkTS 定义了声明式 UI 描述、自定义组件和动态扩展 UI 元素的能力,配合 ArkUI 开发框架中的系统组件及其相关的事件方法、属性方法等共同构成 UI 开发的主体。我们下面要完成的主要是一个木鱼和设置按钮、自动按钮。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
build() {

Column() {
HdNav({ title: '电子木鱼', showRightIcon: false, iconColor: $r('app.color.white'), titleColor: '#ffffff' })

Row() {
Text(this.woodenType[this.type] + ':'+ this.score).fontSize(22).fontColor("#ffffff").width('100%').textAlign(TextAlign.Center)
}.width("100%").height("8%")

Row() {
Image($r('app.media.setting')).width(25).height(25).margin(16).onClick(() => {
if (this.dialogController != null) {
this.dialogController.open()
}
})
}.width('100%')

Row() {
Image($r('app.media.foreground')).width(40).height(40).margin({left:8,top:5})
}.width('100%')
.onClick(() => {
this.handlePopup = !this.handlePopup
})
.bindPopup(this.handlePopup, {
message: '数据统计功能,正在完善中~',
})

Row() {
if (this.isPresent) {
Text(this.woodenType[this.type] + ': ' + this.woodenFishNum).fontSize(16).fontColor("#ffffff").width('100%').textAlign(TextAlign.Center)
.transition(this.effect)
}
}.width('100%').height('25%')
.alignItems(VerticalAlign.Top)

Row() {
Image($r('app.media.muyu'))
.width(this.isZoomed == true ? this.targetWidth * 1.2 : this.targetWidth * 1)
.height(this.isZoomed == true ? this.targetHeight * 1.2 : this.targetHeight * 1)
}
.width('100%')
.height('25%')
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)

Row() {
Toggle({ type: ToggleType.Switch })
.onChange((isOn: boolean) => {
if(isOn) {
promptAction.showToast({ message: 'auto is on.' })
} else {
promptAction.showToast({ message: 'auto is off.' })
}
})

Text('自动' + this.woodenType[this.type]).fontSize(18).fontColor('#ffffff').height(40).margin({left: 10})

}.width('100%').height('10%').justifyContent(FlexAlign.Center)

}
.height("100%")
.backgroundColor('rgba(0, 0, 0, 1.00)')

}

木鱼点击

木鱼是一张图片,也就是给该图绑定一个点击事件,点击一次有三个动作需要执行:

  • 木鱼有放大的效果
  • 有类似功德文字的飘动
  • 功德数值的累加

而点击的时候要看到实时的效果,所以可以声明三个状态,通过 State 的修改,从而驱动 UI 更新,以下的 animateTo 是给域名的放大添加的一个平滑效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 积分
@State score: number = 0
// 积分文字
@State isPresent: boolean = false
// 木鱼是否放大
@State isZoomed: boolean = false


// 木鱼UI
Image($r('app.media.muyu'))
.width(this.isZoomed == true ? this.targetWidth * 1.2 : this.targetWidth * 1)
.height(this.isZoomed == true ? this.targetHeight * 1.2 : this.targetHeight * 1)
.onClick((event) => {
animateTo({ curve: curves.springMotion() }, () => {
this.isZoomed = !this.isZoomed;

if (this.isZoomed == true) {
this.isPresent = true;
this.score += this.woodenFishNum;
this.onClickPlay();
}
})

// 定时缩小/定时文字消失
setTimeout(() => {this.isZoomed = false;}, 50);
setTimeout(() => {this.isPresent = false}, 600);
})

木鱼音效

木鱼音效是点击时的咚咚的声音,这里就要使用到 HarmonyOS Next 的音频服务。这里需要注意一点,项目运行预览无法播放,一定要模拟器或真机才可以调试音频的播放效果。

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 销毁音效工具
onClickDestroy= ()=>{
AudioMgr.Ins().destroy();
console.log('audio', 'destroy');
}

// 初始化音效工具
onClickInit = ()=>{
AudioMgr.Ins().init();
console.log('audio', 'init');
}

// 播放指定音效
onClickPlay = ()=>{
AudioMgr.Ins().play();
console.log('audio', 'playing');
}

img

动画特效

这里的动画效果主要是点击木鱼,从下网上飘出一个文字然后消失的特效。在鸿蒙中可以通过 TransitionEffect 方法添加效果,首先创建特效,然后再文字上挂载。

1
2
3
4
5
6
7
8
9
// 上移入场特效
private effect: object =
TransitionEffect.OPACITY
// 初始正常大小// 假设动画持续时间为500ms
.combine(TransitionEffect.scale({ x: 1, y: 1 }).animation({ curve: curves.springMotion(0.6, 1.2), duration: 0 }))
// 向上平移150单位// 与上一步同时开始
.combine(TransitionEffect.translate({ x: 0, y: 400 }).animation({ curve: curves.springMotion(0.6, 1.2), duration: 10000, delay: 50 }))
// 淡出至完全透明// 在平移结束后开始淡出
.combine(TransitionEffect.opacity(0).animation({ curve: curves.springMotion(0.6, 1.2), duration: 1000, delay: 0 }));

img

自定义弹窗

经过前面布局,事件绑定,音效播放,一个简单的电子木鱼其实已经完成了。但是为了增添趣味和后期扩展,这里再加一个设置功能,通过按钮打开配置项弹窗,设置包括:

  • 类型选项 (功德、财运、桃花运等)
  • 音效选项 (各种解压的音效素材)
  • 皮肤管理 (木鱼的 UI 界面设置)
  • 数值修改 (对展示的累加数值做任意修改)
  • 其他 (是否关闭音效,是否自动点击等)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// 弹窗层(UI开发-组件-自定义弹窗)
@CustomDialog
struct SettingDialog {
controller?: CustomDialogController

// 父子组件双向同步,文档见 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-link-V5
@Link woodenFishType: number

// 木鱼敲击的数值
@Link woodenFishNum: number

build() {
Column() {

Row() {
Text('愿望:').fontSize(17).fontWeight(600)
Radio({ value: '功德', group: 'word' }).checked(true).onChange((isChecked: boolean) => {
if(isChecked) {
this.woodenFishType = 0
}
})
Text('功德').fontSize(15)
Radio({ value: '财富', group: 'word' }).onChange((isChecked: boolean) => {
if(isChecked) {
this.woodenFishType = 1
}
})
Text('财富').fontSize(15)
Radio({ value: '桃花运', group: 'word' }).onChange((isChecked: boolean) => {
if(isChecked) {
this.woodenFishType = 2
}
})
Text('桃花运').fontSize(15)
}
.width('100%')
.margin({bottom: 12})
.justifyContent(FlexAlign.Start)

Row() {
Text('数值:').fontSize(16).fontWeight(600)
TextInput({text:'1'}).type(InputType.Number).width(180).onChange((value: string) => {
this.woodenFishNum = parseInt(value)
})
}
.width('100%')
.margin({bottom: 12})
.justifyContent(FlexAlign.Start)

Row() {
Text('音效:').fontSize(16).fontWeight(600)
Toggle({ type: ToggleType.Switch })
}
.width('100%')
.margin({bottom: 12})
.justifyContent(FlexAlign.Start)

Row() {
Text('皮肤:').fontSize(16).fontWeight(600)
Radio({ value: '默认', group: 'skin' }).checked(true)
Text('木鱼').fontSize(15)
Radio({ value: '悟空', group: 'skin' })
Text('黑悟空').fontSize(15)
Radio({ value: '典韦', group: 'skin' })
Text('典韦').fontSize(15)
}
.width('100%')
.margin({bottom: 12})
.justifyContent(FlexAlign.Start)

}.padding({top: 28, left: 15})

}
}

这里需要注意的是:父子组件的数据传递。因为自定义弹窗和木鱼是两个不同的组件,而点击弹窗中的比如类型切换或修改的数值,全部要更新到木鱼组件的展示当中。

当然鸿蒙也提供了 @Link 装饰器,用于与其父组件中的数据源共享相同的值,可以结合上面代码和下方截图参考其用法。

img

写在后面

到这里,一个通用型的鸿蒙 Next 版电子木鱼就完成了。不管是组件交互还是布局都还好,唯一让我觉得不适应的是动画特效。

如果用这种方式实现电子烟花肯定不行,所以下次将换一种方法快速实现烟花秀,以及页面间的跳转,待更新~

鸿蒙Next实战:环境搭建

前言

作为独立开发者,如果我们错过了传统移动 App,和后起小程序的红利,那万物互联 + AI 的应用开发就得抓住了。

虽然个人上架应用平台难易都差不多,但是鸿蒙生态当前正需要广大开发者参与,一旦上架,相比其他平台,流量扶持力度更大,变现能力也更容易。

所以,我们可以先开发一些应用占个位置,后面再逐渐迭代完善;那么,第一步就先从搭建开发环境开始吧。

img

鸿蒙 Next 简介

鸿蒙 Next,英文 HarmoneyOS Next,又叫纯血版鸿蒙,2023 年 8 月 4 日开发者预览版,2024 年 6 月正式对外公布。鸿蒙 NEXT 采用了全鸿蒙内核,完成了对 Linux 内核的全面替换,并去掉了安卓开放源代码项目(AOSP)等代码,实现了真正的自主可控,他能支持华为及合作厂商的多种智能终端设备,包括手机、平板、智慧屏等等。

img

运行环境要求

针对开发 HarmonyOS 应用及元服务,华为基于 IntelliJ IDEA Community 开源版本打造了一个集成开发环境(IDE)——DevEco Studio,能将开发的应用和服务同时运行在 HarmonyOS 和 OpenHarmony 系统上,为保证 DevEco Studio 正常运行,建议您的电脑配置满足如下要求:

  • 操作系统:Windows10 64 位,内部版号大于 18363
  • 内存:8GB 及以上
  • 硬盘:100GB 及以上
  • 分辨率:1280*800 像素及以上
  • 其他: 开启了 Hyper-V 虚拟化

img

DevEco Studio

下载

进入 huawei 开发者平台下载,最好找 5.0 以上又不是最新版本就行。还有 DevEco Studio 支持 Windows 和 macOS 系统,但我本人用的是 Windows,所以就以 win 系统演示。

img

安装

下载完成后,双击下载的 “deveco-studio-xxxx.exe”,进入 DevEco Studio 安装向导。因为 DevEco Studio 提供开箱即用的开发体验,将 HarmonyOS SDK、Node.js、Hvigor、OHPM、模拟器平台等进行合一打包,几乎无需额外下载配置就能马上跑项目,但是要运行模拟器需要一点配置,比如 Windows 版本要大于 18363。

img

模拟器设置

DevEco Studio 提供预览、模拟器、真机三种方式查看项目的运行效果,但是给予的测试权限不一。最高的真机,目前需要搭载了鸿蒙 Next 的手机才行,且鸿蒙无法自己升级到 Next,所以折中的方法就是用模拟器。官方现在要求使用模拟器需申请,而且本地电脑开始了虚拟化技术 Hyper-V。

img

img

img

项目运行

DevEco Studio 安装完成后,可以通过运行 Hello World 工程来验证环境设置是否正确。接下来以创建一个支持 Phone 设备的工程为例进行介绍。

img

项目结构

可能当时为了适应主流开发语言,加上自己新出的 ArkTS 有三种工程类型可供选择,而我就是为 ArkTS 而来,所以以下和后面要做的应用也都是 ArkTS。

  • ArkTS 工程目录结构(Stage 模型)
  • C++ 工程目录结构(Stage 模型)
  • JS 工程目录结构(FA 模型)

img

项目创建后,结构就是上图,而关于每个目录或文件的用法,就要去官方文档查看 “工程目录结构”,这里就略过进入到简单例子的演示。

img

第一个 Hello World

创建页面

在 “Project” 窗口,点击 “entry > src > main > ets > pages”,打开 “Index.ets” 文件,进行页面的编写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Index.ets
@Entry
@Component
struct Index {
@State message: string = 'Hello World'

build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}

预览效果

在编辑窗口右上角的侧边工具栏,点击 Previewer,打开预览器。

img

写在后面

如果你有应用端相关的开发经验,上手鸿蒙应用还是比较容易的。而如果你像我一样是做后端的,或很少接触前端,那也可以跟着我一起代码实战,简单实现一个鸿蒙 Next 的电子木鱼和电子烟花秀,文章待更新~

❌