成都制作网站软件,注册城乡规划师报名,鼠标网站模板,企业网站功能是什么从今天开始#xff0c;博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”#xff0c;对于刚接触这项技术的小伙伴在学习鸿蒙开发之前#xff0c;有必要先了解一下鸿蒙#xff0c;从你的角度来讲#xff0c;你认为什么是鸿蒙呢#xff1f;它出现的意义又是… 从今天开始博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”对于刚接触这项技术的小伙伴在学习鸿蒙开发之前有必要先了解一下鸿蒙从你的角度来讲你认为什么是鸿蒙呢它出现的意义又是什么鸿蒙仅仅是一个手机操作系统吗它的出现能够和Android和IOS三分天下吗它未来的潜力能否制霸整个手机市场呢 抱着这样的疑问和对鸿蒙开发的好奇让我们开始今天对ArkUI状态管理的掌握吧
目录
ArkUI状态管理
State装饰器
Prop和Link
Provide和Consume
Observed和ObjectLink
页面路由 ArkUI状态管理
在声明式UI中是以状态来驱动视图进行更新的其中的核心概念就是状态和视图。所谓状态就是驱动视图更新这个数据或者说是我们自定义组件当中定义好的那些被装饰器标记好的变量所谓视图就是指GUI描述渲染得到的用户界面视图渲染好了之后用户就可以对视图中的页面元素产生交互通过点击、触摸、拖拽等互动事件来改变状态变量的值在arkui的内部就有一种机制去监控状态变量的值一旦发现它发生了变更就会去触发视图的重新渲染。所以像这种状态和视图之间的相互作用的机制我们就称之为状态管理机制。 状态管理需要用到多个不同的装饰器接下来我们开始学习状态管理的基本概念以及以下几个装饰器的基本用法和注意事项。
State装饰器
使用State装饰器有以下注意事项 1State装饰器标记的变量必须初始化不能为空值 2State支持Object、class、string、number、boolean、enum类型以及这些类型的数组 3嵌套类型(Object里面的某个属性又是一个Object)以及数组中的对象属性无法触发视图更新以下是演示代码 class Person {name: stringage: numberconstructor(name: string, age: number) {this.name namethis.age age}
}
Entry
Component
struct StatePage {idx: number 1State p: Person[] [new Person(张三, 20)]build(){Column(){Button(添加).onClick((){this.p.push(new Person(张三this.idx, 20 ))})ForEach(this.p,(p, index) {Row(){Text(${p.name}: ${p.age}).fontSize(30).onClick(() {//数组内的元素变更不会触发数组的重新渲染// p.age//数组重新添加、删除或者赋值的时候才会触发数组的重新渲染this.p[index] new Person(p.name, p.age1)})Button(删除).onClick((){this.p.splice(index, 1)})}.width(100%).justifyContent(FlexAlign.SpaceAround)})}.width(100%).height(100%)}
} Prop和Link
Prop和Link这两个装饰器是在父子组件之间数据同步的时候去使用的以下是两者的使用情况
装饰器PropLink同步类型单向同步双向同步允许装饰的变量类型 1Prop只支持string、number、boolean、enum类型 2父组件是对象类型子组件是对象属性 3不可以是数组、any 1父子类型一致string、number、boolean、enum、object、class以及他们的数组 2数组中的元素增、删、替换会引起刷新 3嵌套类型以及数组中的对象属性无法触发视图更新
接下来借助Prop和Link完成一个小案例
我们在父组件中通过prop向子组件传值子组件通过Prop装饰器接受到值之后进行页面渲染这里我们采用了ArkUI提供的堆叠容器和进度条组件实现页面的配置
// 统一卡片样式
Styles function card(){.width(95%).padding(20).backgroundColor(Color.White).borderRadius(15).shadow({radius: 6, color: #1F000000, offsetX: 2, offsetY: 4})
}Component
export struct TaskStatistics {Prop totalTask: number // 总任务数量Prop finishTask: number // 已完成任务数量build() {// 任务进度卡片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()).fontColor(#36D).fontSize(24)Text( / this.totalTask.toString()).fontSize(24)}}}.margin({top: 20, bottom: 10}).justifyContent(FlexAlign.SpaceEvenly).card()}
}
子组件如果修改父组件的值的话需要通过装饰器Link来实现父组件需要通过$来拿值
// 任务类
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})
}Component
export struct TaskList {Link totalTask: number // 总任务数量Link finishTask: number // 已完成任务数量State tasks: Task[] [] // 任务数组// 任务更新触发函数handleTaskChange(){this.totalTask this.tasks.length // 更新任务总数量this.finishTask this.tasks.filter(item item.finished).length // 更新任务数量}build() {Column(){// 任务新增按钮Button(新增任务).width(200).onClick((){this.tasks.push(new Task()) // 新增任务数组this.handleTaskChange()})// 任务列表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: number){Button(){Image($r(app.media.delete)).fillColor(Color.Red).width(20)}.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 // 任务状态是否已完成
}import { TaskStatistics } from ../components/TaskStatistics
import { TaskList } from ../components/TaskList
Entry
Component
struct PropPage {State totalTask: number 0 // 总任务数量State finishTask: number 0 // 已完成任务数量State tasks: Task[] [] // 任务数组build(){Column({space: 10}){// 任务进度卡片TaskStatistics({ totalTask: this.totalTask, finishTask: this.finishTask })// 任务列表TaskList({ totalTask: $totalTask, finishTask: $finishTask })}.width(100%).height(100%).backgroundColor(#F1F2F3)}
}
最终呈现的结果如下 Provide和Consume
这两个装饰器可以跨组件提供类似State和Link的双向同步操作方式很简单父组件之间使用Provide装饰器子组件全部使用Consume装饰器父组件都不需要传递参数了直接调用子组件函数即可 最终呈现的结果如下 虽然相对来说比Prop和Link简便许多但是使用Provide和Consume还是有代价的本来需要传递参数的但是使用Provide不需要传递参数其内部自动帮助我们去维护肯定是有一些资源上的浪费所以说我们能用Prop还是尽量用Prop实在用不了的可以去考虑Provide。
Observed和ObjectLink
这两个装饰器用于在涉及嵌套对象或数组元素为对象的场景中进行双向数据同步:
我们给任务列表中的文本添加一个样式属性当我们点击勾选的话文本就会变灰并且加上一个中划线样式 但是当我们勾选之后视图并没有发生变化原因是我们的Task是一个对象类型数组的元素是对象对象的属性发生修改是不会触发视图的重新渲染的所以这里我们需要使用本次讲解的装饰器来进行解决
我们给class对象设置Observed装饰器 然后在要修改对象属性值的位置进行设置ObjectLink装饰器因为这里一个任务列表通过ForEach遍历出来的所以我们需要将这个位置单独抽离出来形成一个函数然后将要使用的item设置ObjectLink装饰器因为还需要调用函数但是任务列表的函数不能动所以我们也将调用的函数作为参数传递过去
Component
struct TaskItem {ObjectLink item: TaskonTaskChange: () voidbuild(){Row(){if (this.item.finished){Text(this.item.name).finishedTask()}else{Text(this.item.name)}Checkbox().select(this.item.finished).onChange(val {this.item.finished val // 更新当前的任务状态this.onTaskChange()})}.card().justifyContent(FlexAlign.SpaceBetween)}
}
传递过程中为了确保this指向没有发生改变我们在传递函数的时候还需要通过bind函数指定this指向 最终呈现的结果如下 页面路由
页面路由是指在应用程序中实现不同页面之间的跳转和数据传递如果学习过前端vue或react框架的人可以非常简单的理解页面路由跳转的概念以下是在鸿蒙开发中进行页面路由跳转所调用的API函数以及相应函数的作用与前端的vue框架十分类似 Router有两种页面跳转模式分别是 router.pushUrl()目标页不会替换当前页而是压入页面栈因此可以用router.back()返回当前页 router.replaceUrl()目标页替换当前页当前页会被销毁并释放资源无法返回当前页 Router有两种页面实例模式分别是 Standard标准实例模式每次跳转都会新建一个目标页并压入栈顶默认就是这种模式 Single单实例模式如果目标页已经在栈中则离栈顶最近的同url页面会被移动到栈顶并重新加载 了解完页面路由基本概念之后接下来在案例中开始介绍如何使用页面路由
首先我们在index首页定义路由信息
// 定义路由信息
class RouterInfo {url: string // 页面路径title: string // 页面标题constructor(url: string, title: string) {this.url urlthis.title title}
}
接下在struct结构体里面定义路由相关信息以及页面的静态样式 State message: string 页面列表private routers: RouterInfo[] [new RouterInfo(pages/router/test1, 页面1),new RouterInfo(pages/router/test2, 页面2),new RouterInfo(pages/router/test3, 页面3),new RouterInfo(pages/router/test4, 页面4)]build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold).fontColor(#008c8c).height(80)List({space: 15}){ForEach(this.routers,(router, index) {ListItem(){this.RouterItem(router, index 1)}})}.layoutWeight(1).alignListItem(ListItemAlign.Center).width(100%)}.width(100%).height(100%)}}
定义RouterItem函数设置点击函数进行路由跳转
Builder RouterItem(r: RouterInfo, i: number){Row(){Text(i.).fontSize(20).fontColor(Color.White)Blank()Text(r.title).fontSize(20).fontColor(Color.White)}.width(90%).padding(12).backgroundColor(#38f).shadow({radius: 6, color: #4f0000, offsetX: 2, offsetY: 4}).onClick((){// router跳转传递3个参数router.pushUrl(// 跳转路径及参数{url: r.url,params: {id: i}},// 页面实例router.RouterMode.Single,// 跳转失败的一个回调err {if (err) {console.log(跳转失败errCode${err.code} errMsg: ${err.message})}})})}
定义3个路由跳转页设置第四个路由没有跳转页面作对照 注意如果是仅仅是新建一个ArkTS页面的话需要在以下的文件中进行路由配置 如果觉得每次创建一个页面都要进行一次路由路径的创建比较烦的话可以采用以下创建方式会自动帮我们配置好路由路径而不需在去手动设置 在子组件中如果我们想拿到传递过来的参数可以调用getParams函数返回调用back函数 想要加个返回的警告可以采用如下的方式 最终呈现的结果为