HarmonyOS 开发学习笔记

  • 一、开发准备
    • 1.1、了解ArkTs语言
    • 1.2、TypeScript语法
      • 1.2.1、变量声明
      • 1.2.2、条件控制
      • 1.2.3、函数
      • 1.2.4、类和接口
      • 1.2.5、模块开发
    • 1.3、快速入门
  • 二、ArkUI组件
    • 2.1、Image组件
    • 2.2、Text文本显示组件
    • 2.3、TextInput文本输入框组件
    • 2.4、Button按钮组件
    • 2.5、Slider滑动条组件
    • 2.6、Column和Row
    • 2.7、循环控制
    • 2.8、List
    • 2.9、自定义组件
  • 三、状态管理
    • 3.1、@State装饰器
    • 3.2、任务统计案例
    • 3.3、@Prop、@Link、@Provide和@Consume
    • 3.4、@Observe和@ObjectLink
  • 四、页面路由
  • 五、动画
    • 5.1、属性动画
    • 5.2、显式动画
    • 5.3、组件转场动画
    • 5.4、实现摇杆功能
  • 六、Stage模型
    • 6.1、Stage模型概述
    • 6.2、Stage应用配置文件
    • 6.3、UIAbility生命周期
    • 6.4、页面及组件生命周期
    • 6.5、UIAbility的启动模式
  • 七、网络连接
    • 7.1、Http数据请求
    • 7.2、第三方库axios
  • 八、数据持久化
    • 8.1、用户首选项
    • 8.2、关系型数据库
  • 九、通知
    • 9.1、基础通知
    • 9.2、进度条通知
    • 9.3、通知行为意图

一、开发准备

1.1、了解ArkTs语言


ArkTs的优势

  • 声明式UI
  • 状态管理

特点:

  • 开发效率高、开发体验好
  • 性能优越
  • 有多系统适配,接入功能

1.2、TypeScript语法

1.2.1、变量声明

TypeScript在JavaScript的基础上加入了静态类型检查功能,因此每一个变量都有固定的数据类型。

// string : 字符串,可以用引号或双引号let msg: string = 'hello world!'// number: 数值,整数、浮点数都可以let age: number = 21// boolean:布尔let finished: boolean = true// any: 不确定类型,可以是任意类型let a:any = 'jack'a = 21// union: 联合类型,可以是多个指定类型中的一种let u: string|number|boolean = 'rose'u = 18// Object:对象let p = {name:'jack',age:21}console.log(p.name)console.log(p['name'])//Array:数组,元素可以是任意其它类型let names: Array<string> = ['Jack','Rose']let ages: number[] = [21,18]console.log(names[0])

1.2.2、条件控制

TypeScript与大多数开发语言类似,支持基于if-else和switch的条件控制。

let num:number = 21//判断是否是偶数if(num%2===0){console.log(num + "是偶数")}else{console.log(num + "是奇数")}//判断是否是正数if(num>0){console.log(num + "是正数")}else if(num<0){console.log(num + "是负数")}else{console.log(num +"是0")}

注意:在TypeScript中,空字符串、数字0、null、undefined都被认为是false,其它值则为true

let grade: string = "A"switch (grade) {case 'A':{console.log('优秀')break}case 'B' : {console.log('合格')break}case 'c':{console.log('不合格')break}default: {console.log('非法输入')break}}

TypeScript支持for和while循环,并且为一些内置类型如Array等提供了快捷迭代语法。

//普通forfor(let i = 1; i <= 10; i++){console.log('点赞'+i+'次')}//whilelet i = 1;while(i <= 10){console.log('点赞'+i+'次')i++;}
// 定义数组let names: string[] = ['Jack','Rose']// for in 送代器,遍历得到数组角标for (const i in names) {console.log(i+':'+names[i]) }// for of 送代器,直接得到元素for (const name of names) {console.log(name)}

1.2.3、函数

TypeScript通常利用function关键字声明函数,并且支持可选参数、默认参数、箭头函数等特殊语法。

//无返回值函数function sayHello(name:string):void{console.log('您好,'+name+'!');}sayHello("jack")//有返回值函数function sum(x:number,y:number):number{return x+y;}let result = sum(21,18)//箭头函数let sayHi =(name:string )=>{console.log('您好'+name+'!')}sayHi("zhangsan")

函数-可选参数

// 可选参数,在参数名后加 " />function sayHello(name?: string){// 判断name是否有值,如果无值则给一个默认值name = name ? name :"陌生人"console.log('你好,' + name +'!')}sayHello('Jack')sayHello()

函数-默认参数

// 参数默认值,在参数后面赋值,表示参数默认值//如果调用者没有传参,则使用默认值function sayHello(name: string ='陌生人'){console.log('你好,' +'name'+"!")}sayHello('Jack')sayHello()

1.2.4、类和接口

TypeScript具备面向对象编程的基本语法,例如interface、class、enum等。也具备封装、继承、多态等面向对象基本特征。

类、接口、枚举

//定义枚举enum Msg{HI ='Hi'HELLO Hello}//定义接口,抽象方法接收举参数interface A{say(msg: Msg):void}//实现接口class B implements A {say(msg: Msg): void {console.log(msg+',I am B')}}// 初始化对象let a:A = new B()//调用方法,传递放举参数a.say(Msg.HI)

继承

class Rectangle [//成员变量private width: numberprivate length: number// 构造函数constructor(width: number, length: number) [this.width = widththis.length = length}//成员方法public area(): number{return this.width * this.length}}//定义正方形class Square extends Rectangle{constructor(side: number) {//调用类构造super(side,side)}}let s = new Square(10)console.log('正方形面积为:'+s.area())

1.2.5、模块开发

应用复杂时,我们可以把通用功能抽取到单独的ts文件中,每个文件都是一个模块 (module)模块可以相互加载,提高代码复用性。

rectangle.ts// 定义矩形类,并通过export导出export class Rectangle {//成员变量public width: numberpublic length: number//构造函数constructor(width: number, length: number) {this.width = widththis.length = length}}//定义工具方法,求矩形面积,并通过export导出export function area(rec: Rectangle): number{return rec.width * rec.length}
//通过import语法导入,from后面写文件的地址import {Rectangle, area} from '../rectangle'// 创建Rectangle对象let r = new Rectangle(10, 20)//调用area万法console.log('面积为:' + area(r))

1.3、快速入门

  • 装饰器

    • @Component:标记自定义组件
    • @Entry:标记当前组件是入口组件
    • @State:标记该变量是状态变量,值变化时会触发UI刷新
  • 自定义组件

    struct Index{}
    • 可复用的UI单元
  • UI描述

    build(){}
    • 其内部以声明式万式描述UI结构
  • 内置组件:ArkUI提供的组件

    Row(){Column(){Text(this.message)}}
    • 容器组件: 用来完成页面布局,例如 Row、Column
    • 基础组件:自带样式和功能的页面元素,例如 Text
  • 属性方法

    .fontSize(50).fontWeight(FontWeight.Bold).width('100%').height('100%')
    • 设置组件的UI样式
  • 事件方法

    .onClick(()=>{//...处理事件})
    • 设置组件的UI样式

二、ArkUI组件

2.1、Image组件

Image:图片显示组件

  1. 声明Image组件并设置图片源:
    Image(src: string|PixelMap|Resource)
    1. string格式,通常用来加载网络图片,需要申请网络访问权限: ohos.permission.INTERNET
      Image('https://xxx .png')
    2. PixelMap格式,可以加载像素图,常用在图片编辑中
      Image(pixelMapobject)
    3. Resource格式,加载本地图片,推荐使用
      Image($r('app.media.mate60')) 对应在目录中的media目录下的图片
      Image($rawfile('mate60.png'))对应在目录中rawfile目录下的图片
  2. 添加图片属性
    Image($r('app.media.icon')).width(100)宽度.height(120) //高度.borderRadius(10) //边框圆角.interpolation(ImageInterpolation.High) //图片插值

2.2、Text文本显示组件

  1. 声明Text组件并设置文本内容
    Text(content" />: string|Resource)

    1. string格式,直接填写文本内容
      Text('图片宽度')
    2. Resource格式,读取本地资源文件
      Text($r('app.string.width_label'))
  2. 添加文本属性
Text('注册账号').lineHeight(32) // 行高.fontSize(20) // 字体大小.fontColor('#ff1876f8') // 字体颜色.fontweight(FontWeight.Medium) //字体粗细

2.3、TextInput文本输入框组件

  1. 声明TextInput组件

    TextInput( {placeholder" />: ResourceStr, text?: ResourceStr])
    1. placeHoder:输入框无输入时的提示文本
      TextInput([placeholder:'请输入账号或手机号'})
    2. text:输入框当前的文本内容
      TextInput({text:'itcast'})
  2. 添加属性和事件

TextInput({text:'当前输入文本'}).width(150) // 宽.height(30) // 高.backgroundColor('#FFF') // 背景色.type(InputType.Password) // 输入框类型
名称描述
Normal基本输入模式。支持输入数字、字母、下划线、空格、特殊字符
Password密码输入模式。支持输入数字、字母、下划线、空格、特殊字符
Email邮箱地址输入模式。支持数字,字母,下划线,以及@字符.
Number纯数字输入模式
PhoneNumber电话号输入模式,支持输入数字、+、-、*、#、长度不限

2.4、Button按钮组件

  1. 声明Button组件,label是按钮文字
    Button(label?: ResourceStr)
    1. 文字型按钮
      Button('点我')
    2. 自定义按钮,在Button内嵌套其它组件
      Button(){Image($r('app.media.search')).width(20).margin(10)}
  2. 添加属性和事件
    Button('点我').width(100).height(30).type(ButtonType.Normal) // 按钮类型.onClick(() => {//处理点击事件}
名称描述
Capsule胶囊型按钮 (圆角默认为高度的一半)
Circle圆形按钮
Normal普通按钮 (默认不带圆角)

2.5、Slider滑动条组件

Silder(options?: SliderOptions )
slider({min: 0,// 最小值max: 100,// 最大值value: 30 ,// 当前值step: 1,// 滑动步长style: SliderStyle.OutSet,// InSetdirection: Axis.Horizontal,// Verticalreverse: false //是否反向滑动)}

属性与事件

.width('90%').showTips(true)//是否展示value 百分比提示.blockColor('#36d').onChange(value => {//value就是当前滑块值})

2.6、Column和Row

属性方法名说明参数
justifyContent设置子元素在主轴方向的对齐格式FlexAlign枚举
alignItems设置子元素的交叉轴方向的对齐方式Row容器使用VerticalAlign枚举,Column容器使用HorizontalAlign枚举
@Entry@Componentstruct Index {build() {Column({space: 20}){Text('item1')Text('item2')Text('item3')Text('item4')}.width('100%').height('100%').margin({top:10}).padding({top:20})justifyContent(FlexAlign.Center)alignItems(HorizontalAlign.Center)}}




2.7、循环控制

class Item{name:stringimage:ResourceStrprice:numberdiscount:numberconstructor(name: string,image:ResourceStr,price:number,discount:number=0) {this.name = namethis.image = imagethis.price = pricethis.discount = discount}}@Entry@Componentstruct ItemPage {private items :Array<Item> = [new Item('荣耀',$r('app.media.honor30'),3999),new Item('oppo',$r('app.media.oppoR17'),2999),new Item('iPhone',$r('app.media.iPhoneX'),8999,1899),]build() {Column({space:8}) {Row(){Text('商品列表').fontWeight(FontWeight.Bold).fontSize(30)}.width('100%').margin({bottom:20})ForEach(this.items,item=>{Row({space:10}){Image(item.image).width(100)Column(){Text(item.name).fontSize(20).fontWeight(FontWeight.Bold)if (item.discount) {Text('原价:¥'+item.price).decoration({type:TextDecorationType.LineThrough,color:Color.Black})Text('现价:¥'+(item.price-item.discount)).fontSize(18).fontColor('#F36')Text('折扣:¥'+item.discount).fontSize(18).fontColor('#F36')}else {Text('原价:¥'+item.price).fontSize(18).fontColor('#F36')}}}.width('80%').justifyContent(FlexAlign.Start)})}.width('100%').height('100%')}}

2.8、List

列表(List)是一种复杂容器,具备下列特点

  1. 列表项(ListItem)数量过多超出屏幕后,会自动提供滚动功能
  2. 列表项 (ListItem)既可以纵向排列,也可以横向排列

2.9、自定义组件

  1. 自定义组件\

    @Componentstruct MyComponent {private title: stringbuild(){//组UI描述Text(this.title)}}
    @Entry@Componentstruct XxxPage{build(){//引用组件MyComonent({title:'订单列表'})}}
  2. 自定义构建函数,可以定义在全局或组件内

    //全局自定义构建函数@Builder function XxxBuilder(){//UI描达}
    @Componentstruct XxxComponent {//组件内自定义构建函数@Builder YyyBuilder(){//UI描达}build(){XxxBuilder()this.YyyBuilder()}}
  3. @Styles装饰器,仅可封装组件通用属性

//全局公共样式@Styles function fillScreen(){.width('100%').height('100%')}
@Entry@Componentstruct XxxPage [//组件内公共样式@Styles normalBackground(){.backgroundColor('#EFEFEF').padding(14)}build(){Row() {/*...*/}.filiScreen().normalBackground()}}
  1. @Extend装饰器,仅可定义在全局,可以设置组件特有属性
@Extend(Text) function priceText(){.fontSize(18).fontColor('#F36')}

三、状态管理

3.1、@State装饰器

在声明式UI中,是以状态驱动视图更新:

  • 状态(State):指驱动视图更新的数据(被装饰器标记的变量)
  • 视图(View):基于UI描述渲染得到用户界面

注意

  • @State装饰器标记的变量必须初始化,不能为空值
  • @State支持Object、class、string、number、boolean、enum类型以及这些类型的数组
  • 嵌套类型以及数组中的对象属性无法触发视图更新

3.2、任务统计案例

//任务类class Task {static id: number = 1//任务名称name: string = `任务${Task.id++}`//任务状态finished: boolean = false}@Styles function card() {.width('95%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })}//任务完成样式@Extend(Text) function finishedTask() {.decoration({ type: TextDecorationType.LineThrough }).fontColor('#B1B2B1')}@Entry@Componentstruct propPage {//总任务数量@State totalTask: number = 0//已完成任务数量@State finishTask: number = 0//任务数量@State tasks: Task[] = []handleTaskChange() {//1.更新任务数量this.totalTask = this.tasks.length//通过过滤得出已完成的数量this.finishTask = this.tasks.filter(item => item.finished).length}build() {Column({ space: 10 }) {//1. 任务进度Row() {//文本Text('任务进度').fontSize(30).fontWeight(FontWeight.Bold)//栈排列方式,两个容器相覆盖Stack() {Progress({value: this.finishTask,total: this.totalTask,type: ProgressType.Ring}).width(100)Row() {Text(this.finishTask.toString()).fontSize(24).fontColor('#36D')Text('/' + this.totalTask.toString()).fontSize(24)}}}.card().margin({ top: 10, bottom: 20 }).justifyContent(FlexAlign.SpaceEvenly)//2.新增任务按钮Button('新增任务').width(200).onClick(() => {//1.新增任务数据this.tasks.push(new Task)//2.更新任务数量this.handleTaskChange()})//3. 任务列表List({ space: 10 }) {ForEach(this.tasks,(item: Task, index) => {ListItem() {Row() {Text(item.name).fontSize(20)Checkbox().select(item.finished).onChange(val => {item.finished = val//通过过滤得出已完成的数量this.handleTaskChange()})}.card().justifyContent(FlexAlign.SpaceBetween)}//增加后面的滑动功能.swipeAction({end: this.DeleteButton(index)})})}.width('100%').layoutWeight(1).alignListItem(ListItemAlign.Center)}.width('100%').height('100%').backgroundColor('#f1f2f3')}//构建函数 删除按钮@Builder DeleteButton(index){Button().width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(5)//点击事件,当点击当前按钮时,会删除该任务.onClick(()=>{this.tasks.splice(index,1)//更新数量this.handleTaskChange()})}}

3.3、@Prop、@Link、@Provide和@Consume

当父子组件之间需要数据同步时,可以使用@Prop和@Link装饰器

@Prop@Link
同步类型单向同步双向同步
允许装饰的变量类型@Prop只支持string、number、boolean、enum类型;父组件对象类型,子组件是对象属性;不可以是数组、any父子类型一致: string、number、boolean、enum、object、class,以及他们的数组;数组中元素增、删、替换会引起刷新嵌套类型以及数组中的对象属性无法触发视图更新
初始化方式不允许子组件初始化父组件传递,禁止子组件初始化


修改后的代码

//任务类class Task {static id: number = 1//任务名称name: string = `任务${Task.id++}`//任务状态finished: boolean = false}@Styles function card() {.width('95%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })}//任务完成样式@Extend(Text) function finishedTask() {.decoration({ type: TextDecorationType.LineThrough }).fontColor('#B1B2B1')}class TaskNum {//总任务数量totalTask: number = 0//已完成任务数量finishTask: number = 0}@Entry@Componentstruct propPage {//总任务数量@State tasknum: TaskNum =new TaskNum()build() {Column({ space: 10 }) {//1. 任务进度TaskStatus({totalTask:this.tasknum.totalTask,finishTask:this.tasknum.finishTask})//2.新增任务按钮//3. 任务列表TaskList({tasknum: $tasknum})}.width('100%').height('100%').backgroundColor('#f1f2f3')}}@Componentstruct TaskStatus {//总任务数量@Prop totalTask: number//已完成任务数量@Prop finishTask: numberbuild() {Row() {//文本Text('任务进度').fontSize(30).fontWeight(FontWeight.Bold)//栈排列方式,两个容器相覆盖Stack() {Progress({value: this.finishTask,total: this.totalTask,type: ProgressType.Ring}).width(100)Row() {Text(this.finishTask.toString()).fontSize(24).fontColor('#36D')Text('/' + this.totalTask.toString()).fontSize(24)}}}.card().margin({ top: 10, bottom: 20 }).justifyContent(FlexAlign.SpaceEvenly)}}@Componentstruct TaskList {//总任务数量@Link tasknum: TaskNum//任务数量@State tasks: Task[] = []handleTaskChange() {//1.更新任务数量this.tasknum.totalTask = this.tasks.length//通过过滤得出已完成的数量this.tasknum.finishTask = this.tasks.filter(item => item.finished).length}build() {Column(){//2.新增任务按钮Button('新增任务').width(200).margin({bottom:10}).onClick(() => {//1.新增任务数据this.tasks.push(new Task)//2.更新任务数量this.handleTaskChange()})//3. 任务列表List({ space: 10 }) {ForEach(this.tasks,(item: Task, index) => {ListItem() {Row() {Text(item.name).fontSize(20)Checkbox().select(item.finished).onChange(val => {item.finished = val//通过过滤得出已完成的数量this.handleTaskChange()})}.card().justifyContent(FlexAlign.SpaceBetween)}//增加后面的滑动功能.swipeAction({end: this.DeleteButton(index)})})}.width('100%').layoutWeight(1).alignListItem(ListItemAlign.Center)}}//构建函数 删除按钮@Builder DeleteButton(index){Button().width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(5)//点击事件,当点击当前按钮时,会删除该任务.onClick(()=>{this.tasks.splice(index,1)//更新数量this.handleTaskChange()})}}



修改后的代码

//任务类class Task {static id: number = 1//任务名称name: string = `任务${Task.id++}`//任务状态finished: boolean = false}@Styles function card() {.width('95%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })}//任务完成样式@Extend(Text) function finishedTask() {.decoration({ type: TextDecorationType.LineThrough }).fontColor('#B1B2B1')}class TaskNum {//总任务数量totalTask: number = 0//已完成任务数量finishTask: number = 0}@Entry@Componentstruct propPage {//统计信息@Provide tasknum: TaskNum =new TaskNum()build() {Column({ space: 10 }) {//1. 任务进度TaskStatus()//2.新增任务按钮//3. 任务列表TaskList()}.width('100%').height('100%').backgroundColor('#f1f2f3')}}@Componentstruct TaskStatus {//总任务数量@Consume tasknum: TaskNumbuild() {Row() {//文本Text('任务进度').fontSize(30).fontWeight(FontWeight.Bold)//栈排列方式,两个容器相覆盖Stack() {Progress({value: this.tasknum.finishTask,total: this.tasknum.totalTask,type: ProgressType.Ring}).width(100)Row() {Text(this.tasknum.finishTask.toString()).fontSize(24).fontColor('#36D')Text('/' + this.tasknum.totalTask.toString()).fontSize(24)}}}.card().margin({ top: 10, bottom: 20 }).justifyContent(FlexAlign.SpaceEvenly)}}@Componentstruct TaskList {//总任务数量@Consume tasknum: TaskNum//任务数量@State tasks: Task[] = []handleTaskChange() {//1.更新任务数量this.tasknum.totalTask = this.tasks.length//通过过滤得出已完成的数量this.tasknum.finishTask = this.tasks.filter(item => item.finished).length}build() {Column(){//2.新增任务按钮Button('新增任务').width(200).margin({bottom:10}).onClick(() => {//1.新增任务数据this.tasks.push(new Task)//2.更新任务数量this.handleTaskChange()})//3. 任务列表List({ space: 10 }) {ForEach(this.tasks,(item: Task, index) => {ListItem() {Row() {Text(item.name).fontSize(20)Checkbox().select(item.finished).onChange(val => {item.finished = val//通过过滤得出已完成的数量this.handleTaskChange()})}.card().justifyContent(FlexAlign.SpaceBetween)}//增加后面的滑动功能.swipeAction({end: this.DeleteButton(index)})})}.width('100%').layoutWeight(1).alignListItem(ListItemAlign.Center)}}//构建函数 删除按钮@Builder DeleteButton(index){Button().width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(5)//点击事件,当点击当前按钮时,会删除该任务.onClick(()=>{this.tasks.splice(index,1)//更新数量this.handleTaskChange()})}}

3.4、@Observe和@ObjectLink

@obiectLink和@Observed装饰器用于在涉及嵌套对象或数组元素为对象的场景中进行双向数据同步

@Observedclass Person {name: stringage: numbergf: Personconstructor(name: string, age: number,gf" />: Person) {this.name = namethis.age = agethis.gf = gf}}
@Componentstruct Child {@ObjectLink p: Personbuild() {Column() {Text(`s[this.p.name] : s[this.p.age]`)}}}
@Entry@Componentstruct Parent {@State p: Person = new Person('Jack', 21, new Person( ' Rose'18))@State gfs: Person[] = [new Person('萝丝'18)new Person('露西'19),]build() {Column() {Child([p: this.p.gf}).onClick(() => this.p.gf.age++)Text('====== 女友列表 ======!')ForEach(this.gfs,p=>{Child([p: p}).onClick(() => p.age++)}}}}}

最终修改代码

//任务类@Observedclass Task {static id: number = 1//任务名称name: string = `任务${Task.id++}`//任务状态finished: boolean = false}@Styles function card() {.width('95%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })}//任务完成样式@Extend(Text) function finishedTask() {.decoration({ type: TextDecorationType.LineThrough }).fontColor('#B1B2B1')}class TaskNum {//总任务数量totalTask: number = 0//已完成任务数量finishTask: number = 0}@Entry@Componentstruct propPage {//统计信息@Provide tasknum: TaskNum =new TaskNum()build() {Column({ space: 10 }) {//1. 任务进度TaskStatus()//2.新增任务按钮//3. 任务列表TaskList()}.width('100%').height('100%').backgroundColor('#f1f2f3')}}@Componentstruct TaskStatus {//总任务数量@Consume tasknum: TaskNumbuild() {Row() {//文本Text('任务进度').fontSize(30).fontWeight(FontWeight.Bold)//栈排列方式,两个容器相覆盖Stack() {Progress({value: this.tasknum.finishTask,total: this.tasknum.totalTask,type: ProgressType.Ring}).width(100)Row() {Text(this.tasknum.finishTask.toString()).fontSize(24).fontColor('#36D')Text('/' + this.tasknum.totalTask.toString()).fontSize(24)}}}.card().margin({ top: 10, bottom: 20 }).justifyContent(FlexAlign.SpaceEvenly)}}@Componentstruct TaskList {//总任务数量@Consume tasknum: TaskNum//任务数量@State tasks: Task[] = []handleTaskChange() {//1.更新任务数量this.tasknum.totalTask = this.tasks.length//通过过滤得出已完成的数量this.tasknum.finishTask = this.tasks.filter(item => item.finished).length}build() {Column(){//2.新增任务按钮Button('新增任务').width(200).margin({bottom:10}).onClick(() => {//1.新增任务数据this.tasks.push(new Task)//2.更新任务数量this.handleTaskChange()})//3. 任务列表List({ space: 10 }) {ForEach(this.tasks,(item: Task, index) => {ListItem() {TaskItem({item:item,onTaskChange:this.handleTaskChange.bind(this)})}//增加后面的滑动功能.swipeAction({end: this.DeleteButton(index)})})}.width('100%').layoutWeight(1).alignListItem(ListItemAlign.Center)}}//构建函数 删除按钮@Builder DeleteButton(index){Button().width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(5)//点击事件,当点击当前按钮时,会删除该任务.onClick(()=>{this.tasks.splice(index,1)//更新数量this.handleTaskChange()})}}@Componentstruct TaskItem {@ObjectLink item:TaskonTaskChange :()=>voidbuild() {Row() {if(this.item.finished){Text(this.item.name).finishedTask().fontSize(20)}else {Text(this.item.name).fontSize(20)}Checkbox().select(this.item.finished).onChange(val => {this.item.finished = val//通过过滤得出已完成的数量this.onTaskChange})}.card().justifyContent(FlexAlign.SpaceBetween)}}
  • 子组件调用父组件时可能会出现this丢失
    • 在传递函数到子组件时,绑定 .bind(this)

四、页面路由

页面路由是指在应用程序中实现不同页面之间的跳转和数据传递

  • 页面栈的最大容量上限为32个页面使用router.clear()方法可以清空页面栈,释放内存
  • Router有两种页面跳转模式,分别是:
    • router.pushUrl(): 目标页不会替换当前页,而是压入页面栈,因此可以用router.back()返回当前页
    • router.replaceUrl(): 目标页替换当前页,当前页会被销毁并释放资源,无法返回当前页
  • Router有两种页面实例模式,分别是:
    • Standard :标准实例模式,每次跳转都会新建一个目标页并压入栈顶。默认就是这种模式
    • Single:单实例模式,如果目标页已经在栈中,则离栈顶最近的同Ur亿页面会被移动到栈顶并重新加载

使用:

  1. 首先要导入HarmonyOS提供的Router模块
    import router from'@ohos .router';
  2. 然后利用router实现跳转、返回等操作
// 跳转到指定路径,并传递参数router.pushUrl({/*RouterOptions- url:目标页面路径- params:传递的参数(可选)*/url:'pages/ImagePage'params: {id: 1}},//页面模式:RouterMode枚举router.RouterMode.Singleerr =>{/*异常响应回调函数,错误码:- 100001:内部错误,可能是染失败- 100002:路由地址错误- 100003:路由栈中页面超过32 */if(err){console.log('路由失败。')}})
//获取传递过来的参数params: any = router.getParams()// 返回上一页router.back()//返回到指定页,并携带参数router.back({url: 'pages/Index',params: {id: 10}})

五、动画

5.1、属性动画

属性动画是通过设置组件的animation属性来给组件添加动画,当组件的width、height、Opacity、backgroundColor、scale、rotate、translate等属性变更时,可以实现渐变过渡效果。

Text('^_^').position({x: 10,// x轴坐标y:0 // y轴坐标}).rotate({angle: 0,// 旋转角度centerX:'50%',//旋转中心横坐标centerY:'50%',//旋转中心纵坐标}).animation({duration: 1000,curve: Curve.EaseInOut})
名称参数类型必填描述
durationnumber设置动画时长;默认值: 1000;单位:毫秒。
temponumber动画播放速度。;数值越大,速度越快;默认值:1。
curvestring | Curve设置动画曲线。;默认值: Curve.EaselnOut,平滑开始和结束。
delaynumber设置动画延迟执行的时长;默认值:0,单位:毫秒。
iterationsnumber设置播放次数。;默认值: 1,取值范围:[-1,+∞);说明:设置为-1时表示无限次播放。
playModePlayMode设置动画播放模式,默认播放完成后重头开始播放;默认值: PlayMode.Normal。
onFinish()=> void状态回调,动画播放完成时触发。

5.2、显式动画

显式动画是通过全局animateTo函数来修改组件属性,实现属性变化时的渐变过渡效果。

Text('^_^').position({x: 10,// x轴坐标y:0 // y轴坐标}).rotate({angle: 0,// 旋转角度centerX:'50%',//旋转中心横坐标centerY:'50%',//旋转中心纵坐标})
//显调用animateTo函数触发动四animateTo({duration: 1000},//动画参数()=>{//修改组件属性关联的状态交品})
名称参数类型必填描述
durationnumber设置动画时长;默认值: 1000;单位:毫秒。
temponumber动画播放速度。;数值越大,速度越快;默认值:1。
curvestring | Curve设置动画曲线。;默认值: Curve.EaselnOut,平滑开始和结束。
delaynumber设置动画延迟执行的时长;默认值:0,单位:毫秒。
iterationsnumber设置播放次数。;默认值: 1,取值范围:[-1,+∞);说明:设置为-1时表示无限次播放。
playModePlayMode设置动画播放模式,默认播放完成后重头开始播放;默认值: PlayMode.Normal。
onFinish()=> void状态回调,动画播放完成时触发。

5.3、组件转场动画

组件转场动画是在组件插入或移除时的过渡动画通过组件的transition属性来配置

if(this.isShow){Text('^_^').transition({//转场动画参数opcity:0,rotate: {angle: -360],scale:{x:0,y: 0}})}
//显式调用animateTo函数触发动画animateTo({duration: 1000},// 动画参数()=>{this.isShow = false}}
参数名称参数类型必填参数描述
typeTransitionType类型,默认包括组件新增和删除。默认是ALL
opacitynumber不透明度,为插入时起点和删除时终点的值。默认值:1,取值范围:[0,1]
translate{x” />
rotate{x?: number,y?: number,z?: number,angle: number|string,centerX?: number|string,centerY?: number| string}旋转效果:angle是旋转角度,其它参数与scale类似

5.4、实现摇杆功能

注意点

onTouch()函数

.onTouch(this.handleTouchEvent.bind(this))//自定义onTouch函数handleTouch(event:TouchEvent){}

获取手指位置坐标

//手指x坐标let x = event.touches[0].x//手指y坐标let y = event.touches[0].x

计算手指与中心点连线和x轴正半轴的夹角,单位是弧度

let angle = Math.atan2(x,y)//sinsin = Math.sin(angle)//coscos = Math.sin(angle)

使遥感连续的动画

animateTo({curve:curves.responsiveSpringMotion()},()=>{//要执行动画的函数})

switch屏幕事件

switch(event.type){case TouchType.Up:case TouchType.Down:case TouchType.Move:}

修改指定事务的角度

if(Math.abs(angle *2)<Math.PI){this.src = $r('app.media.fish')}else{this.src = $r('app.media.fish_rev')angle = angle <0?angle + Math.PI:angle - Math.PI}this.angle = angle*180/Math.PI

六、Stage模型

6.1、Stage模型概述

6.2、Stage应用配置文件

app.json5主要包含以下内容:

  • 应用的全局配置信息,包含应用的包名、开发厂商、版本号等基本信息。
  • 特定设备类型的配置信息。

module.json5主要包含以下内容:

  • Module的基本配置信息,例如Module名称、类型、描述、支持的设备类型等基本信息。
  • 应用组件信息,包含UIAbility组件和ExtensionAbility组件的描述信息。
  • 应用运行过程中所需的权限信息。

6.3、UIAbility生命周期

6.4、页面及组件生命周期

@Entry@Componentstruct PageA {@State show: boolean = truebuild(){Column(){Button('show').onClick(() => {this,show = !this .show}if(this.show){ComponentA()}}}}
@Componentstruct ComponentA {build(){Row(){Text(' component A')}}}

6.5、UIAbility的启动模式

  1. singLeton启动模式
    每一个UIAbility只存在唯一实例。是默认启动模式任务列表中只会存在一个相同的UIAbility
  2. standard启动模式
    每次启动UIAbitity都会创建一个新的实例。在任务列表中可能存在一个或多个相同的UIAbility
  3. specified启动模式
    每个UIAbility实例可以设置Kev标示例直接被拉起,不存在则创建新实例启动UIAbiTity时,需要指定key,存在key相同实
// 1当前UIAbility调用startAbility方法拉起目标UIAbility// 1.1.获取上下文context = getContext(this) as common.UIAbilityContext// 1.2指定要跳转到的UIAbility的信息let want = {deviceId:'',// deviceId为空表示本设备bundleName: 'com.example .myapplication',abilityName: 'DocumentAbility',moduleName: 'entry',// moduleName非必选parameters: {//getInstanceKey: 自定义方法,生成目标UIAbility实例的keyinstanceKey: this.getInstancekey()}}//1.3.尝试拉起目标UIAbility实例this.context.startAbility(want)
//2.在Abilitystage的生命周期回调中为目标UIAbility实例生成keyexport default class MyAbilityStage extends AbilityStageonAcceptWant(want: Want): string {//判断当前要拉取的是否是DocumentAbilityif(want.abilityName === 'DocumentAbility'){// 根据参数中的instanceKey参数拼接生成一个key值并返回return `DocAbility_${want.parameters.instanceKey}`}return'';}}
//3在module.json5配置文件中,通过srcEntry参数指定AbilityStage路径{"module":{"name": "entry""type": "entry""srcEntry": "./ets/myabilitystage/MyAbilityStage.ts"...}}

七、网络连接

7.1、Http数据请求

  1. 导入http模块

    import http from '@ohos .net.http'
  2. 使用http模块发送请求,处理响应

    // 2.1.创建一个http的请求对象,不可复用let httpRequest = http.createHttp()// 2.2.发起网络请求httpRequest.request('http://localhost:3000/users',{method: http.RequestMethod.GET,extraData: {'param1':'valuel' } // k1=v1&k2=v2})// 2.3.处理响应结果then( resp =>{if(resp.responseCode === 200){//请求成功}}).catch( err =>{// 请求失败});

HttpRequestOptions

名称描述类型
methodRequestMethod请求方式,GET、POST、PUT、DELETE等
extraDataString|Object请求参数
headerObject请求头字段
connectTimeoutnumber连接超时时间,单位毫秒,默认是60000ms
readTimeoutnumber读取超时间,同上

HttpResponse

名称描述类型
responseCodeResponseCode响应状态码
headerObject响应头
cookiesstring响应返回的cookies
resultstring|object响应体,默认是JSON字符串
resultTypeHttpDataType返回值类型。

7.2、第三方库axios

  1. 下载和安装ohpm、

    • 下载ohpm工具包
    • 解压工具包,执行初始化命令
      # windows环境init.bat#Linux和Mac环境./init.sh
    • 将ohpm配置到环境变量
      # windows环境,直接在我的电脑配置即可#Linux和Mac环境,其中ohpm的路径替换为ohpm的安装路径export OHPM_HOME=/xx/ohpmexport PATH=${OHPM_HOME}/bin:${PATH}
  2. 下载和安装axios

    • 下载axios
      # 进入项目目录,然后输入下面命令ohpm install @ohos/axios
    • 开放网络权限
      # 在模块的module.json5文件中配置网路权限{"module":{"requestPermissions": [{"name":"ohos.permission.INTERNET"}]}}
  3. 使用axios

    • 导入axios
      # 导入axios模块import axios from '@ohos/axios'
    • 发送请求并处理响应
      axios.get(//请求方式'url',//请求路径{params:{ 'param1': 'valuel' },//请求选项data: { 'paraml': 'valuel' }}).then( response => {if(response.status !== 200){console.log('查询失败')}console.log('查询成功')}).catch(error => {console.log('查询失败',JSON.stringify(error))})}
名称类型描述
statusnumber响应状态码
headersObject响应头
dataany服务端返回的响应体

八、数据持久化

8.1、用户首选项

用户首选项(Preference)为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据

  1. 导入首选项模块

    import dataPreference from '@ohos.data.preferences'
  2. 获取首选项实例,读取指定文件

    dataPreference.getPreferences(this.context,'MyAppPreferences').then(preferences => {//获取成功}).catch(reason =>{//获取失败})
  3. 数据操作

    // 3.1.写入数据,如果已经存在则会覆盖,可利用.has()判断是否存在preferences.put('key', val).then(() => preferences.flush()) // 刷到磁盘.catch(reason => {}) // 处理异常//3.2.删除数据preferences.delete(' key').then(() => []).catch(reason => [])//3,3查询数据preferences.get('key','defaultValue').than(value=>console.log('查询成功')).catch(reason=>console.log('查询失败'))

说明:

  • Key为string类型,要求非空且长度不超过80字节
  • Value可以是string、number、boolean及以上类型数组大小不超过8192字节
  • 数据量不要超过一万条

PreferencesUtil

import preferences from '@ohos.data.preferences'import { expect } from '@ohos/hypium'class PreferencesUtil{prefMap:Map<string,preferences.Preferences> = new Map()//同步方法async loadPreference(context,name:string){try {let pref = await preferences.getPreferences(context,name)this.prefMap.set(name,pref)console.log(`testTag`,`加载Preferences[${name}]成功`);}catch (e){console.log(`testTag`,`加载Preferences[${name}]失败`);}}async putPreference(name:string,key:string,value:preferences.ValueType){if (!this.prefMap.has(name)) {console.log(`testTag`,`Preferences[${name}]尚未初始化`)return}try {let pref = this.prefMap.get(name)//写入数据await pref.put(key,value);//刷盘await pref.flush()console.log(`testTag`,`保存Preferences[${name}.${key} = ${value}]成功`)}catch (e){console.log(`testTag`,`保存Preferences[${name}.${key} = ${value}]失败`,JSON.stringify(e))}}async getPreference(name:string,key:string,defaultValue:preferences.ValueType){if (!this.prefMap.has(name)) {console.log(`testTag`,`Preferences[${name}]尚未初始化`)return}try {let pref = this.prefMap.get(name)//读取数据let value = await pref.put(key,defaultValue);console.log(`testTag`,`读取Preferences[${name}.${key} = ${value}]成功`)}catch (e){console.log(`testTag`,`读取Preferences[${name}.${key}]失败`,JSON.stringify(e))}}}const preferencesUtil = new PreferencesUtil()export default preferencesUtil as PreferencesUtil

8.2、关系型数据库

关系型数据库(RDB)是基于SQLite组件提供的本地数据库,用于管理应用中的结构化数据。例如:记账本、备忘录。


  1. 初始化数据库

    • 导入关系型数据库模块
    import relationalStore from '@ohos.data.relationalStore';
    • 初始化数据库表
    //2..rdb配置const config = {name:'MyApplication.db',// 数据库文件名securityLevel: relationalStore.SecurityLevel.S1 //数据库安全级别}//2.2.初始化表的SOLconst sql = `CREATE TABLE IF NOT EXISTS TASK (ID INTEGER PRIMARY KEY,NAME TEXT NOT NULL,FINISHED bit)`// 2.3.获取rdbrelationalStore.getRdbStore(this.context,config,(err,rdbStore) => {// 2.4执行SgL,后续的所有增删改查都是使用rdbstore对象rdbStore.executeSql(sql)})
  2. 增、删、改数据

    • 新增数据
    // 1.1.准备数据let task = {id: 1,name: '任务1',finished: false};// 1.2.新增this.rdbStore.insert(this.tableName, task)
    • 修改
    //2.1要更新的数据let task = {'finished': true};//2.2.查询条件,RdbPredicates就是条件谓词let predicates = new relationalStore.RdbPredicates(this,tableName)predicates.equalTo('ID',id)//2.3.执行更新this.rdbStore.update(task, predicates)
    • 删除
    //3.1.查询条件let predicates = new relationalStore.RdbPredicates(this.tableName)predicates.equalTo('ID',id)//3.2.执行删除this.rdbStore.delete(predicates)
  3. 查询数据

    • 查询数据
    // 1.1.查询条件let predicates = new relationalstore.RdbPredicates(this.tableName)//1.2.执行查询let result= await this.rdbStore.query(predicates, ['ID','NAME','FINISHED'])
    • 解析结果
    // 2.1.准备数组保存结果let tasks: any[] = []//2.2循环遍历结果集,判断是否结果是否遍历到最后一行while (!result.isAtLastRow){// 指针移动到下一行数据 result.goToNextRow()// 根据字名获取字index,从而获取字段值let id = result.getLong(result.getColumnIndex('ID'));let name = result.getString(result.getColumnIndex('NAME'));tasks.push(fid,name})}
IDNAMEFINISHED
1任务1false
2任务2false
3任务3true

九、通知

9.1、基础通知

应用可以通过通知接口发送通知消息,提醒用户关注应用中的变化。用户可以在通知栏查看和操作通知内容。

通知常见的使用场景:

  • 显示接收到的短消息、即时消息等。
  • 显示应用的推送消息,如广告、版本更新等。
  • 显示当前正在进行的事件,如下载等。
  1. 导入notification模块

    import notificationManager from '@ohos.notificationManager'
  2. 发布通知

    //2.1.构建通知请求let request: notificationManager.NotificationRequest = {id: 10,content:{ // 通知内容: }}//2.2.发布通知notificationManager.publish(request).then(() => console.log('发送通知成功')).catch(reason => console,log('发送通知失败'JSON.stringify(reason)))
  3. 取消通知

    //取消指定id的通知notificationManager.cancel(10)// 取消当前应用所有通知notificationManager.cancelAll()
类型描述
NOTIFICATION_CONTENT_BASIC_TEXT普通文本类型。
NOTIFICATION_CONTENT_LONG_TEXT长文本类型。
NOTIFICATION_CONTENT_MULTILINE多行文本类型。
NOTIFICATION_CONTENT_PICTURE图片类型。
  • 普通文本类型通知由标题、文本内容和附加信息三个字段组成,其中标题和文本内容是必填字段。
let notificationRequest = {id: 1,content: {contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, // 普通文本类型通知normal: {title: 'test_title',//通知标题text: 'test_text',//通知内容additionalText: 'test_additionalText',//通知附加的内容}}}NotificationManager.publish(notificationRequest, (err) => {if (err) {console.error(`[ANS] failed to publish, error[${err}]`);return;}console.info(`[ANS] publish success`);});

  • 长文本类型通知继承了普通文本类型的字段,同时新增了长文本内容、内容概要和通知展开时的标题。通知默认显示与普通文本相同,展开后,标题显示为展开后标题内容,内容为长文本内容。
let notificationRequest = {id: 1,content: {contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_LONG_TEXT, // 长文本类型通知longText: {title: 'test_title',//通知标题text: 'test_text',//通知内容additionalText: 'test_additionalText',//通知附加的内容longText: 'test_longText',//通知的长文本briefText: 'test_briefText',//通知的概要和总结expandedTitle: 'test_expandedTitle',//通知展开时的标题}}}// 发布通知NotificationManager.publish(notificationRequest, (err) => {if (err) {console.error(`[ANS] failed to publish, error[${err}]`);return;}console.info(`[ANS] publish success`);});

  • 多行文本类型通知继承了普通文本类型的字段,同时新增了多行文本内容、内容概要和通知展开时的标题。通知默认显示与普通文本相同,展开后,标题显示为展开后标题内容,多行文本内容多行显示。
let notificationRequest = {id: 1,content: {contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_MULTILINE, // 多行文本类型通知multiLine: {title: 'test_title',//通知标题text: 'test_text',//通知内容briefText: 'test_briefText',//通知的概要和总结longTitle: 'test_longTitle',//展开后的标题lines: ['line_01', 'line_02', 'line_03', 'line_04'],}}}// 发布通知NotificationManager.publish(notificationRequest, (err) => {if (err) {console.error(`[ANS] failed to publish, error[${err}]`);return;}console.info(`[ANS] publish success`);});

  • 图片类型通知继承了普通文本类型的字段,同时新增了图片内容、内容概要和通知展开时的标题,图片内容为PixelMap型对象,其大小不能超过2M。
let imagePixelMap: PixelMap = undefined; // 需要获取图片PixelMap信息let notificationRequest: notificationManager.NotificationRequest = {id: 1,content: {contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE,picture: {title: 'test_title',text: 'test_text',additionalText: 'test_additionalText',briefText: 'test_briefText',//通知概要expandedTitle: 'test_expandedTitle',picture: this.pixel//像素图}}};async aboutToAppear(){//获取资源管理器let rm = getContext(this).resourceManager;//读取图片let file = await rm.getMediaContent($r('app.media.watchGT4'))//创建PixelMapimage.createImageSource(file.buffer).createPixelMap().then(value => this.pixel = value).catch(reason => console.log('testTag,载图片异常'JSON.stringify(reason)))}// 发布通知notificationManager.publish(notificationRequest, (err) => {if (err) {console.error(`Failed to publish notification. Code is ${err.code}, message is ${err.message}`);return;}console.info('Succeeded in publishing notification.');});


更多属性

let notificationRequest = {id: 1,content: {contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, // 普通文本类型通知normal: {title: 'test_title',//通知标题text: 'test_text',//通知内容additionalText: 'test_additionalText',//通知附加的内容}},deliveryTime: new Date().getTime(),showDeliveryTime: true,groupName: 'wechat',slotType: notify.slotType.SOCIAL_COMMUNICATION,}
类型枚举说明状态栏图标提示音横幅
SOCIAL_COMMUNICATION社交类型
SERVICE_INFORMATION服务类型×
CONTENT_INFORMATION内容类型××
OTHER_TYPES其它×××

9.2、进度条通知

进度条通知会展示一个动态的进度条,主要用于文件下载、长任务处理的进度显示。


isSupportTemplate()是查询模板是否支持接口,目前仅支持进度条模板。

  1. 导入模块。

    import NotificationManager from '@ohos.notificationManager';
  2. 查询系统是否支持进度条模板,查询结果为支持downloadTemplate模板类通知。

    NotificationManager.isSupportTemplate('downloadTemplate').then((data) => {console.info(`[ANS] isSupportTemplate success`);let isSupportTpl: boolean = data; // isSupportTpl的值为true表示支持支持downloadTemplate模板类通知,false表示不支持// ...}).catch((err) => {console.error(`[ANS] isSupportTemplate failed, error[${err}]`);});
  3. 构造进度条模板对象,并发布通知。

    let template = {name:'downloadTemplate',data: {title: '标题:',fileName: 'music.mp4',progressValue: 30,progressMaxValue:100,}}//构造NotificationRequest对象let notificationRquest = {id: 1,slotType: notify.SlotType.OTHER_TYPES,template: template,content: {contentType: notify.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,normal: {title: template.data.title + template.data.fileName,text: "sendTemplate",additionalText: "30%"}},deliveryTime: new Date().getTime(),showDeliveryTime: true}notify.publish(notificationRquest).then(() => {console.info(`[ANS] publish success `);}).catch((err) => {console.error(`[ANS] failed to publish, error[${err}]`);});

9.3、通知行为意图

我们可以给通知或其中的按钮设置的行为意图 (want),从而实现拉起应用组件或发布公共事件等能力

接口名描述
getWantAgent(info: WantAgentInfo, callback: AsyncCallback): void创建WantAgent。
trigger(agent: WantAgent, triggerInfo: TriggerInfo, callback” />//1.意图行为信息let wantInfo = {wants: [{deviceId:,bundleName:‘com.example.myapplication’,abilityName:‘EntryAbility’,action:,entities:[]}},operationType: wantAgent.OperationType.START_ABILITY,requestCode: 0,wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG]}// 2.创建wantAgent实例this.wantAgentInstance = await wantAgent.getwantAgent(wantInfo)// 3.通知请求let request: notify.NotificationRequest = {id: 999,template: template,wantAgent; this.wantAgentInstance,// 设置通知意图content: {//..}}

Copyright © maxssl.com 版权所有 浙ICP备2022011180号