HarmonyOS 鸿蒙Next-ArkUI应用开发训练营学习笔记及心得(一)
HarmonyOS 鸿蒙Next-ArkUI应用开发训练营学习笔记及心得(一) 一、缘起
接触鸿蒙系统还是在3年前参加HDC活动的时候,那会儿刚刚发布HarmonyOS,感觉特别新奇,心想着终于有一款属于国产自研操作系统,之后就开始一直关注,但是始终没有真正的去研究去深入学习。
今年,正好因为产品中可能会涉及底层开发,于是开始研究HarmonyOS,并且参加了各种公开的课程,也认识了一些大咖:郭峰老师,连志安老师,包括本次课程的“九弓子”老师,都是HarmonyOS的专家!
而在这个过程中,不仅仅学习研究基于HarmonyOS的应用开发,还研究了OpenHarmony设备开发,基于ModelBox开发板进行AI开发等等。
而本次学习《实操ArkUI应用开发》,也是正好有个全面的了解,跟着“九弓子”老师一步步深入浅出的进行应用开发,了解ArkUI的一些特性。
二、缘续
学习的过程本身就很有乐趣,在本次学习的过程中,截止到目前有2次直播课,还有3节实操课程,相对来说都能够很好的理解,一步步详细的讲解,个人也利用空余的时间进行了完整的编码体验,初步了解了ArkUI的一些特性,接下来就分享自己的一些学习笔记和心得:
2.1 开发入门
在这个阶段,主要讲解了HarmonyOS应用开发有哪些方法,其中ArkUI又包含哪些内容,这里简单总结就是如下图:
ArkUI开发中,主要涉及JS开发范式和eTS/TS开发,这个与相应的文档上也能够一一对应,有个很重要的事情必须强调,那就是开发过程中一定要学会看文档,并且要利用文档,参考相应的API进行开发,很多组件、接口、示例代码在文档中都可以找到,这里特别要分享文档的地址和截图:
截图:
接着老师从以下几个特性来一一介绍了声明式UI和类Web范式的相关开发内容:
- 如何安装DevEco Studio以及如何创建项目
这个本身之前就已经体验了,所以相对来说非常轻松,对于很多入门开发者来说也没有太大的难度,其中有几个注意点给大家分享一下:
- 安装前检查环境,包括npm、nodejs等
- deveco studio对于nodejs的版本有要求,在v14.9.1~15.0.0之间,很多小伙伴因为电脑已经安装了nodejs的高版本,因此可以直接选择重新下载安装nodejs即可(参考下图)
- 运行真机的时候需要登录华为云开发者账号后授权,并且在选择机型后要双击运行按钮才可运行(最开始单击一直没用,还以为出bug了呢。。。)
- 声明式UI开发
这里面主要讲解了Flex布局以及一些基础的组件,包括组件的封装、父子组件双向绑定、条件渲染与声明生命周期等,然后通过这些组件来构建一个登录页,最终效果如下图:
这里面重点要给大家分享的是基础组件和一些装饰器:
常用的组件包括基础组件、容器组件、媒体组件、绘制组件、画布组件,本次课程里主要用到了基础组件中的Button、Blank、Divider、Image、Text、TextArea、TextInput,其他诸如单选框、复选框、日期、滚动条、进度条等等组件也都属于基础组件(跟JS中的组件类似);另外还有容器组件中的Column、Flex、List、ListItem、Panel、Scroll、Row、Stack、Grid、GridContainer等,画布组件中的Canvas;
至于其他相关组件大家可以去文档中查看其具体用法,根据实际项目需要进行选择即可。
- 类web范式开发
为了跟eTS开发进行对比,老师还特地演示了类web范式开发,提供了三件套:html、js、css,以及组件封装、父子组件传值、emit事件传递等等内容
这里因为不是本次重点,所以不详细跟大家分享了
2.2 前端实战篇
这节内容老师主要介绍如何通过组件的使用、组件的布局、数据模型的创建、装饰器的使用、页面渲染等来创建前端页面UI,最终实现如下效果:
大部分内容参考文档就可以完成,这里主要分享个人觉得比较重要的几个知识点:
- 屏幕宽高的获取
直接上代码,这里应用到了canvas组件的特性:
// 用Canvas获取屏幕宽高的临时解决方案
if(this.screen_height == 0) {
Canvas(this.ctx)
.width("100%")
.height("100%")
.position({x:0,y:0})
.onReady(() => {
let testBase64 = this.ctx.toDataURL("image/jpeg",0.1)
this.testImg = testBase64
})
Image(this.testImg)
.onComplete((e)=>{
this.screen_width = px2vp(e.width)
this.screen_height = px2vp(e.height)
})
}
- 数据传递与数据绑定(装饰器的应用)
这里主要是分享@Provide和@Consume之间的数据传递应用、@Observed和@ObjectLink之间的数据绑定应用:
装饰器 | 装饰内容 | 说明 |
---|---|---|
@Observed | 类 | @Observed应用于类,表示该类中的数据变更被UI页面管理。 |
@ObjectLink | 被@Observed所装饰类的对象 | 装饰的状态数据被修改时,在父组件或者其他兄弟组件内与它关联的状态数据所在的组件都会更新UI。 |
@Consume | 基本数据类型,类,数组 | @Consume装饰的变量在感知到@Provide装饰的变量更新后,会触发当前自定义组件的重新渲染。 |
@Provide | 基本数据类型,类,数组 | @Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。 |
@Observed和@ObjectLink具体逻辑如下:
示例代码:
//父组件ViewB中的类对象ClassB,其包含的对象ClassA与子组件ViewA数据同步时,通过ObjectLink将数据c值的变化状态通知给父组件同步变化。[@Observed](/user/Observed)
class ClassA {
public name : string;
public c: number;
constructor(c: number, name: string = 'OK') {
this.name = name;
this.c = c;
}
}
class ClassB {
public a: ClassA;
constructor(a: ClassA) {
this.a = a;
}
}
@Component
struct ViewA {
label : string = "ep1";
[@ObjectLink](/user/ObjectLink) a : ClassA;
build() {
Column() {
Text(`ViewA [${this.label}]: a.c=${this.a.c}`)
.fontSize(20)
Button(`+1`)
.width(100)
.margin(2)
.onClick(() => {
this.a.c += 1;
})
Button(`reset`)
.width(100)
.margin(2)
.onClick(() => {
this.a = new ClassA(0); // 错误:ObjectLink装饰的变量a是不可变的
})
}
}
}
@Component
struct ViewB {
@State b : ClassB = new ClassB(new ClassA(10));
build() {
Flex({direction: FlexDirection.Column, alignItems: ItemAlign.Center}) {
ViewA({label: "ViewA #1", a: this.b.a})
ViewA({label: "ViewA #2", a: this.b.a})
Button(`ViewB: this.b.a.c += 1` )
.width(320)
.margin(4)
.onClick(() => {
this.b.a.c += 1;
})
Button(`ViewB: this.b.a = new ClassA(0)`)
.width(240)
.margin(4)
.onClick(() => {
this.b.a = new ClassA(0);
})
Button(`ViewB: this.b = new ClassB(ClassA(0))`)
.width(240)
.margin(4)
.onClick(() => {
this.b = new ClassB(new ClassA(0));
})
}
}
}
Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。Consume在感知到Provide数据的更新后,会触发当前view的重新渲染。
示例代码:
@Entry
@Component
struct CompA {
[@Provide](/user/Provide)("reviewVote") reviewVotes : number = 0;
build() {
Column() {
CompB()
Button() {
Text(`${this.reviewVotes}`)
.fontSize(30)
}
.onClick(() => {
this.reviewVotes += 1;
})
}
}
}
@Component
struct CompB {
build() {
Column() {
CompC()
}
}
}
@Component
struct CompC {
[@Consume](/user/Consume)("reviewVote") reviewVotes : number;
build() {
Column() {
Button() {
Text(`${this.reviewVotes}`)
.fontSize(30)
}
.onClick(() => {
this.reviewVotes += 1;
})
}
}
}
2.3 后端实战篇
本篇主要讲解了前端与后端交互逻辑,包括文件交互选择图片、轻量数据库存储、关系型数据库、SQL语句、DataAbility、数据库操作增删改查、eTS路由传参等知识
这部分内容比较多,核心给分享以下几个知识点:
- 文件交互选择图片
这里面主要用到了featureAbility.startAbilityForResult这个API,其中featureAbility即FA,用于启动Ability调用,本次调用获取文件权限,因此代码如下:
//需要导入相应的模块
import featureAbility from '@ohos.ability.featureAbility'
//涉及到权限部分需要上真机进行验证
chooseFile() {
featureAbility.startAbilityForResult(
{
want:
{
action: "ohos.hidisk.intent.action.choose"
},
}
).then((data) => {
console.info("===============>startAbility=================>");
console.info(JSON.stringify(data))
if(data.want) {
let img_url = data.want.parameters["select-item-list"][0].decodedPath
console.info(JSON.stringify(img_url))
let content_list = JSON.parse(JSON.stringify(this.article.content))
content_list.forEach((item,index)=>{
if(item.content_index == this.userClick) {
item.content_data = "dataability://" + img_url
}
})
this.article.content = content_list
}
});
}
- 轻量级数据库存储
轻量级数据存储功能通常用于保存应用的一些常用配置信息,并不适合需要存储大量数据和频繁改变数据的场景。应用的数据保存在文件中,这些文件可以持久化地存储在设备上。需要注意的是,应用访问的实例包含文件所有数据,这些数据会一直加载在设备的内存中,直到应用主动从内存中将其移除前,应用可以通过Storage的API进行数据操作。
直接上代码:
// 1.导入相关的模块
import dataStorage from '@ohos.data.storage';
import featureAbility from '@ohos.ability.featureAbility';
// 用于获取文件存储路径
// 2.获取Storage实例。读取指定文件,将数据加载到Storage实例,用于数据操作。
var path;
var context = featureAbility.getContext();
context.getFilesDir().then((filePath) => {
path = filePath;
console.info("===============>getFilesDirPromsie==================>");
});
let promise = dataStorage.getStorage(path + '/mystore');
// 3.存入数据
promise.then((storage) => {
let getPromise = storage.put('startup', 'auto');
getPromise.then(() => {
console.info("Succeeded in putting the value of startup.");
}).catch((err) => {
console.info("Failed to put the value of startup with err: " + err);
})
}).catch((err) => {
console.info("Failed to get the storage.");
})
// 4.读取数据
promise.then((storage) => {
let getPromise = storage.get('startup', 'default');
getPromise.then((value) => {
console.info("The value of startup is " + value);
}).catch((err) => {
console.info("Failed to get the value of startup with err: " + err);
})
}).catch((err) => {
console.info("Failed to get the storage.")
})
// 5.数据持久化(可以通过flush或者flushSync方法将Storage实例回写到文件中)
storage.flush();
- 关系型数据库
关系型数据库是在SQLite基础上实现的本地数据操作机制,提供给用户无需编写原生SQL语句就能进行数据增删改查的方法,同时也支持原生SQL语句操作。
主要开发步骤包括:
// 1.创建数据库
import data_rdb from '@ohos.data.rdb'
const CREATE_TABLE_TEST = "CREATE TABLE IF NOT EXISTS test (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT NOT NULL, " +
"age INTEGER, " +
"salary REAL, " +
"blobType BLOB)";
const STORE_CONFIG = {name: "rdbstore.db"}
data_rdb.getRdbStore(this.context,STORE_CONFIG, 1, function (err, rdbStore) {
rdbStore.executeSql(CREATE_TABLE_TEST)
console.info('create table done.')
})
// 2.插入数据
var u8 = new Uint8Array([1, 2, 3])
const valueBucket = {"name": "Tom", "age": 18, "salary": 100.5, "blobType": u8}
let insertPromise = rdbStore.insert("test", valueBucket)
// 3.查询数据
let predicates = new data_rdb.RdbPredicates("test");
predicates.equalTo("name", "Tom")
let promisequery = rdbStore.query(predicates)
promisequery.then((resultSet) => {
console.log("resultSet column names:" + resultSet.columnNames)
console.log("resultSet column count:" + resultSet.columnCount)
}).catch((err) => {
console.info('Query failed.')
})
// 4.调用结果集
resultSet.goToFirstRow()
const id = resultSet.getLong(resultSet.getColumnIndex("id"))
const name = resultSet.getString(resultSet.getColumnIndex("name"))
const age = resultSet.getLong(resultSet.getColumnIndex("age"))
const salary = resultSet.getDouble(resultSet.getColumnIndex("salary"))
const blobType = resultSet.getBlob(resultSet.getColumnIndex("blobType"))
resultSet.close()
- eTS路由传参
路由传参主要是在页面跳转的时候需要传递相关的参数,这里同样用到router模块来实现,具体代码样例如下:
openArticle(){
router.push({
url:"pages/articlepage",
params:{
article_id:this.article.id
}
})
}
通过params方式将参数传递至下一个页面,在新的页面中通过如下方式读取参数:
router.getParams().article_id
更多关于HarmonyOS 鸿蒙Next-ArkUI应用开发训练营学习笔记及心得(一)的实战教程也可以访问 https://www.itying.com/category-93-b0.html
学习
感谢分享
学习一下
棒棒哒~
继续努力,
太强了,关于 @Observed
和 @ObjectLink
的使用好详细
在视频中明显没有这么详细的介绍,甚至在这篇分享中看到了补充!!!
强烈建议大家一起来学习~~~!!~~!~
感谢认可~
学习
在HarmonyOS鸿蒙系统的Next-ArkUI应用开发训练营中,学习者通常会接触到ArkUI这一专为鸿蒙系统设计的UI开发框架。ArkUI采用TypeScript和eTS(Enhanced TypeScript)作为主要开发语言,旨在提供高效、简洁的UI开发体验。
学习过程中,你可能会了解到ArkUI的组件系统、布局方式、事件处理机制以及数据绑定等核心概念。通过训练营的实践项目,你将有机会动手开发简单的鸿蒙应用,从而加深对ArkUI框架的理解。
心得方面,学习者可能会感受到ArkUI在跨设备协同、原生组件与自定义组件结合、以及性能优化等方面的独特优势。同时,也可能会遇到一些挑战,如对不同设备屏幕的适配、复杂交互逻辑的实现等。
在学习过程中,建议重点掌握ArkUI的基础语法和组件使用,同时关注鸿蒙系统的最新特性和更新。通过不断实践和探索,你将能够开发出更加优秀的鸿蒙应用。
如果在学习过程中遇到具体问题或疑问,可以查阅官方文档或参考相关资料。如果问题依旧没法解决请联系官网客服,官网地址是https://www.itying.com/category-93-b0.html。