赞
踩
主要为代码开发阶段会使用到的 DevEco Studio
、ArkTS
、ArkUI
三个工具。
https://developer.huawei.com/consumer/cn/
https://developer.huawei.com/consumer/cn/deveco-studio/
找到如下图所示位置,点击下载对应版本的DevEco Studio
安装包:
1. 双击安装包
2. 安装引导:下载与安装软件-快速开始-DevEco Studio使用指南-工具
3. 环境配置:配置开发环境-快速开始-DevEco Studio使用指南-工具
ps: HarmonyOS SDK 路径中不能包含中文字符
创建一个新项目并运行:创建和运行Hello World-快速开始-DevEco Studio使用指南-工具
官网地址:https://www.typescriptlang.org/
静态类型检查功能:每一个变量都有固定的数据类型。
支持基于 if-else 和 switch 的条件控制。
ps:在 TS 中,空字符串、数字0、null、undefined 都被认为是 false ,其他值则为 true。
支持基于 for 和 while 循环,并且为一些内置类型(如 Array)提供快捷迭代语法。
通常利用 function 关键字声明函数,并且支持可选参数、默认参数、箭头函数等特殊语法。
具备面向对象编程的基本语法,如: interface、class、enum 等;
具备 封装、继承、多态 等面向对象基本特征;
ps:凡是在 类 和 接口 内部定义的函数不需要加 function 关键字。
应用复杂时,可以把通用功能抽取到单独的ts文件中,每个文件都是一个模块(module)。
模块可以相互加载,提高代码复用性。
用法、resources资源访问
1. 声明 Image 组件并设置图片源
Image(src:string|PixelMap|Resource)
2. 添加图片属性
组件通用属性:width
、height
、borderRadius
...
图片插值:interpolation(ImageInterpolation.High)
3. 申请网络访问权限:访问控制授权申请-访问控制-安全-开发
声明 Text 组件并设置文本内容
Text(content?:string|Resource)
添加文本属性
声明 Text 组件
TextInput({placeholder?:ResourceStr, text?:ResourceStr})
添加属性和事件
声明 Button 组件,laber 按钮文字
Button(laber?:ResourceStr)
添加属性和事件
声明 Silder 组件
Silder(options?:SilderOptions)
添加属性和事件
声明 Silder 组件
- Progress(options: {value: number, total?: number, type?: ProgressType})
-
- // value:当前进度值
- // total:进度总长,默认值:100
- // type:进度条类型,默认值:ProgressType.Linear 线性
Checkbox(options?: {name?: string, group?: string })
声明 Column 组件 和 Row 组件
- // 纵向布局使用 Column 容器
- Column({space?:number}){}
-
- // 横向布局使用 Row 容器
- Row({space?:number}){}
添加属性和事件
1. 列表项(即 ListItem )特点
数量过多超出屏幕后,会自动提供滚动功能;
既可以纵向排列,也可以横向排列;
2. 代码示例
- List({ space: 8 }) {
- ForEach(
- [1,2,3,4],
- (item) => {
- ListItem() {
- Row({ space: 10 }) {
- Image($r('app.media.icon'))
- .width(100)
-
- Column({ space: 4 }) {
- if (item>4) {
- Text(item)
- .fontSize(20)
- .fontWeight(FontWeight.Bold)
- } else {
- Text(item)
- .fontSize(20)
- .fontWeight(FontWeight.Bold)
-
- Text('¥' + item)
- .fontSize(18)
- .fontColor('#f36')
- }
-
- }
- .height('100%')
- .alignItems(HorizontalAlign.Start)
- }
- .width('100%')
- .height(120)
- .padding(10)
- .backgroundColor('#fff')
- .borderRadius(20)
- }
- }
- )
- }
- .width('100%')
- .layoutWeight(1) // 页面中除其他元素外,剩下高度占满
- .alignListItem(ListItemAlign.Center) // 列表中的元素左右居中
ps:
List 容器内部不能直接跟其他容器的,必须跟 ListItem,ListItem再去套别的元素;
ListItem 内部只能有一个根组件,在有多组件的情况下,必须用一个容器组件包起来;
- Stack(value?: { alignContent?: Alignment })
-
- // alignContent:设置子组件在容器内的对齐方式,默认值:Alignment.Center
ForEach、if-else
- ForEach(
- arr:Array, // 要遍历的数据数组
- (item: any, index?: number) => {
- // 页面组件生成函数
- },
- // 默认自带
- keyGenerator?: (item: any, index?: number): string => {
- // 键生成函数,为数组每一项生成一个唯一标示,组件是否重新渲染的判断标准
- }
- )
示例:
- if (判断条件) {
- // 成立时
- }else {
- // 不成立时
- }
a. 未抽出组件时,完整代码如下:
- @Entry
- @Component
- struct ItemPage {
- build() {
- Column({ space: 8 }) {
- Row() {
- Text('商品列表')
- .fontSize(30)
- .fontWeight(FontWeight.Bold)
- }
- .width('100%')
- .margin({ bottom: 20 })
- }
- }
- }
b. 抽出组件后:
情况一:组件内使用——只有当前页面使用的组件(抽出至当前页面),代码如下:
- // 抽出至当前页面最上面
- @Component
- struct Header {
- private title: ResourceStr
-
- build() {
- // 组件内容如下
- Row() {
- Text('商品列表')
- .fontSize(30)
- .fontWeight(FontWeight.Bold)
- }
- .width('100%')
- }
- }
-
- @Entry
- @Component
- struct ItemPage {
- build() {
- Column({ space: 8 }) {
- // 使用
- Header({ title: '商品列表' })
- .margin({ bottom: 20 })
- }
- }
- }
情况二:全局使用——其他页面也会使用的组件(抽出至组件文件夹),代码如下:
组件的封装
- // 文件路径:entry/src/main/ets/components/CommonComponents
-
- // 1.封装
- @Component
- export struct Header {
- private title: ResourceStr
-
- build() {
- // 组件内容如下
- Row() {
- Text('商品列表')
- .fontSize(30)
- .fontWeight(FontWeight.Bold)
- }
- .width('100%')
- }
- }
封装组件的使用
- // 文件路径:entry/src/main/ets/pages/ItemPage.ets
-
- // 2.引入封装的自定义组件
- import { Header } from '../components/CommonComponents'
-
- @Entry
- @Component
- struct ItemPage {
- build() {
- Column({ space: 8 }) {
- // 3.对应的地方使用组件
- Header({ title: '商品列表' })
- .margin({ bottom: 20 })
- }
- }
- }
全局定义
- // 1、全局自定义构建函数:全局都可以使用
- @Builder ItemCardJB(item: string) {
- // UI描述
- Row({ space: 10 }) {
- Text(item)
- }
- .width('100%')
- .height(120)
- .padding(10)
- .backgroundColor('#fff')
- .borderRadius(20)
- }
-
- @Entry
- @Component
- struct ItemPage {
- build() {
- Column({ space: 8 }) {
- // 2、使用——局部写法
- ItemCardJB('111')
- }
- }
- }
组件内定义
- @Entry
- @Component
- struct ItemPage {
- // 1、局部自定义构建函数:只能该组件内部使用
- @Builder ItemCardJB(item: string) {
- // UI描述
- Row({ space: 10 }) {
- Text(item)
- }
- .width('100%')
- .height(120)
- .padding(10)
- .backgroundColor('#fff')
- .borderRadius(20)
- }
-
- build() {
- Column({ space: 8 }) {
- // 2、使用——局部写法
- this.ItemCardJB('111')
- }
- }
- }
全局定义
- // 1、定义全局公共样式函数
- @Styles function fillScreen(){
- .width('100%')
- .height('100%')
- .backgroundColor('#efefef')
- .padding(20)
- }
-
- @Entry
- @Component
- struct ItemPage {
- build() {
- Column({ space: 8 }) {
- Row() {
- Text('商品列表')
- }
- // 2、使用
- .fillScreen()
- }
- }
- }
组件内定义
- @Entry
- @Component
- struct ItemPage {
- // 1、定义局部公共样式函数
- @Styles fillScreen(){
- .width('100%')
- .height('100%')
- .backgroundColor('#efefef')
- .padding(20)
- }
-
- build() {
- Column({ space: 8 }) {
- Row() {
- Text('商品列表')
- }
- // 2、使用
- .fillScreen()
- }
- }
- }
- // 1、定义——继承模式
- // 1、组件特有属性的样式函数:@Extend(Text),即继承text属性;
- // 2、@Extend() 不能写在组件内,即只能写在全局,即页面顶部
- @Extend(Text) function priceText(){
- .fontSize(18)
- .fontColor('#f36')
- }
-
- @Entry
- @Component
- struct ItemPage {
- build() {
- Column({ space: 8 }) {
- Row() {
- Text('商品列表')
- // 2、使用
- .priceText()
- }
- .width('100%')
- .margin({ bottom: 20 })
- }
- }
- }
状态(Stste):驱动视图更新的数据——被装饰器标记的变量;
视图(View):基于UI描述渲染得到用户界面;
- @Entry
- @Component
- struct Index {
- // 使用
- @State message: string = 'Hello World'
-
- build() {
- Row() {
- Column() {
- Text(this.message)
- .fontSize(50)
- .fontWeight(FontWeight.Bold)
- .fontColor('#36D')
- .onClick(()=>{
- this.message = '小明!'
- })
- }
- .width('100%')
- }
- .height('100%')
- }
- }
ps:
@Stste 装饰器 标记的变量必须初始化,不能为空值;
@Stste 支持Object、class、string、number、boolean、enum 类型以及这些类型的数据;
嵌套类型以及数组中的对象属性无法触发视图更新;
当父子组件之间需要数据同步时,可以使用 @Prop 和 @Link 装饰器。
@Prop | @Link | |
同步类型 | 单向同步 | 双向同步 |
允许装饰的 变量类型 |
|
|
初始化方式 | 不允许子组件初始化 | 父组件传递,禁止子组件初始化 |
ps:
@Link 和 @State 很像,区别在于:@Link 用于子组件来实现双向同步,@State 用于父组件
a. 传递数据类型——普通类型
- // 任务类
- class Task {
- static id: number = 1
- // 任务名称
- name: string = `任务${Task.id++}`
- // 任务状态:是否完成
- finished: boolean = false
- }
-
- // 统一卡片样式
- @Styles function card() {
- .width('90%')
- .padding(20)
- .backgroundColor(Color.White)
- .borderRadius(15)
- .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
- }
-
- @Entry
- @Component
- struct PropPage {
- // 总任务数量
- @State totalTask: number = 0
- // 已完成任务数量
- @State finishTask: number = 0
-
- build() {
- Column({ space: 10 }) {
- // 1、任务进度卡片
- TaskStatistics({ finishTask: this.finishTask, totalTask: this.totalTask })
- // 3、任务列表
- // 传递引用使用 $ ,传递的不是变量值,传递的是引用
- TaskList({ finishTask: $finishTask, totalTask: $totalTask })
- }
- .width('100%')
- .height('100%')
- .backgroundColor('#f1f2f3')
- }
- }
-
- @Component
- struct TaskStatistics {
- @Prop finishTask: number
- @Prop totalTask: 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())
- .fontSize(24)
- .fontColor('#36d')
- Text('/' + this.totalTask.toString())
- .fontSize(24)
- .fontColor('#36d')
- }
- }
- }
- .card()
- .margin({ top: 20, bottom: 20 })
- .justifyContent(FlexAlign.SpaceAround)
- }
- }
-
- @Component
- 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() {
- // 2、新增任务按钮
- Button('新增任务')
- .width(200)
- .margin({ bottom: 20 })
- .onClick(() => {
- this.tasks.push(new Task()) // 在数组中添加一条数据
- this.handleTaskChange()
- })
- // 3、任务列表
- List({ space: 10 }) {
- ForEach(
- this.tasks,
- (item: Task, index: number) => {
- ListItem() {
- Row() {
- Text(`任务${item.name}`)
- Checkbox()
- .select(item.finished)
- .onChange((val) => {
- item.finished = val //更新任务状态
- this.handleTaskChange()
- })
- }
- .card()
- .justifyContent(FlexAlign.SpaceBetween)
- }
- .swipeAction({ end: this.DeleteButton(index) })
-
- }
- )
- }
- .width('100%')
- .alignListItem(ListItemAlign.Center)
- .layoutWeight(1)
- }
-
- }
-
- @Builder DeleteButton(index: number) {
- Button('删')
- .width(40)
- .height(40)
- .backgroundColor(Color.Red)
- .margin(10)
- .onClick(() => {
- this.tasks.splice(index, 1)
- this.handleTaskChange()
- })
- }
- }
b. 传递数据类型——对象类型
- // 任务类
- class Task {
- static id: number = 1
- // 任务名称
- name: string = `任务${Task.id++}`
- // 任务状态:是否完成
- finished: boolean = false
- }
-
- // 统一卡片样式
- @Styles function card() {
- .width('90%')
- .padding(20)
- .backgroundColor(Color.White)
- .borderRadius(15)
- .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
- }
-
- // 任务统计信息
- class StatInfo{
- totalTask: number = 0 // 总任务数量
- finishTask: number = 0 // 已完成任务数量
- }
-
- @Entry
- @Component
- struct PropPage {
- // 统计信息
- @State stat: StatInfo = new StatInfo()
-
- build() {
- Column({ space: 10 }) {
- // 1、任务进度卡片
- TaskStatistics({ finishTask: this.stat.finishTask, totalTask: this.stat.totalTask })
- // 3、任务列表
- // 传递引用使用 $ ,传递的不是变量值,传递的是引用
- TaskList({ stat: $stat })
- }
- .width('100%')
- .height('100%')
- .backgroundColor('#f1f2f3')
- }
- }
-
- @Component
- struct TaskStatistics {
- @Prop finishTask: number
- @Prop totalTask: 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())
- .fontSize(24)
- .fontColor('#36d')
- Text('/' + this.totalTask.toString())
- .fontSize(24)
- .fontColor('#36d')
- }
- }
- }
- .card()
- .margin({ top: 20, bottom: 20 })
- .justifyContent(FlexAlign.SpaceAround)
- }
- }
-
- @Component
- struct TaskList {
- // 任务
- @Link stat: StatInfo
- // 任务数组
- @State tasks: Task[] = []
-
- handleTaskChange() {
- // 更新任务总数
- this.stat.totalTask = this.tasks.length
- // 更新已完成任务总数
- this.stat.finishTask = this.tasks.filter(item => item.finished).length
- }
-
- build() {
- // 子组件只能有一个根元素
- Column() {
- // 2、新增任务按钮
- Button('新增任务')
- .width(200)
- .margin({ bottom: 20 })
- .onClick(() => {
- this.tasks.push(new Task()) // 在数组中添加一条数据
- this.handleTaskChange()
- })
- // 3、任务列表
- List({ space: 10 }) {
- ForEach(
- this.tasks,
- (item: Task, index: number) => {
- ListItem() {
- Row() {
- Text(`任务${item.name}`)
- Checkbox()
- .select(item.finished)
- .onChange((val) => {
- item.finished = val //更新任务状态
- this.handleTaskChange()
- })
- }
- .card()
- .justifyContent(FlexAlign.SpaceBetween)
- }
- .swipeAction({ end: this.DeleteButton(index) })
-
- }
- )
- }
- .width('100%')
- .alignListItem(ListItemAlign.Center)
- .layoutWeight(1)
- }
-
- }
-
- @Builder DeleteButton(index: number) {
- Button('删')
- .width(40)
- .height(40)
- .backgroundColor(Color.Red)
- .margin(10)
- .onClick(() => {
- this.tasks.splice(index, 1)
- this.handleTaskChange()
- })
- }
- }
ps:子组件只能有一个根元素
@Provide 和 @Consume 可以跨组件双向同步(类似于 @Prop 和 @Link 的双向同步):
父子间双向同步;
爷孙间双向同步;
- // 任务类
- class Task {
- static id: number = 1
- // 任务名称
- name: string = `任务${Task.id++}`
- // 任务状态:是否完成
- finished: boolean = false
- }
-
- // 统一卡片样式
- @Styles function card() {
- .width('90%')
- .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')
- .fontSize(20)
- }
-
- // 任务统计信息
- class StatInfo{
- totalTask: number = 0 // 总任务数量
- finishTask: number = 0 // 已完成任务数量
- }
-
- @Entry
- @Component
- struct PropPage {
- // 统计信息
- @Provide stat: StatInfo = new StatInfo()
-
- build() {
- Column({ space: 10 }) {
- // 1、任务进度卡片
- TaskStatistics()
- // 3、任务列表
- TaskList()
- }
- .width('100%')
- .height('100%')
- .backgroundColor('#f1f2f3')
- }
- }
-
- @Component
- struct TaskStatistics {
- @Consume stat: StatInfo
-
- build() {
- Row() {
- Text(`任务进度:`)
- .fontSize(30)
- .fontWeight(FontWeight.Bold)
- Stack() {
- Progress({
- value: this.stat.finishTask,
- total: this.stat.totalTask,
- type: ProgressType.Ring
- })
- .width(100)
- Row() {
- Text(this.stat.finishTask.toString())
- .fontSize(24)
- .fontColor('#36d')
- Text('/' + this.stat.totalTask.toString())
- .fontSize(24)
- .fontColor('#36d')
- }
- }
- }
- .card()
- .margin({ top: 20, bottom: 20 })
- .justifyContent(FlexAlign.SpaceAround)
- }
- }
-
- @Component
- struct TaskList {
- @Consume stat: StatInfo
- // 任务数组
- @State tasks: Task[] = []
-
- handleTaskChange() {
- // 更新任务总数
- this.stat.totalTask = this.tasks.length
- // 更新已完成任务总数
- this.stat.finishTask = this.tasks.filter(item => item.finished).length
- }
-
- build() {
- // 子组件只能有一个根元素
- Column() {
- // 2、新增任务按钮
- Button('新增任务')
- .width(200)
- .margin({ bottom: 20 })
- .onClick(() => {
- this.tasks.push(new Task()) // 在数组中添加一条数据
- this.handleTaskChange()
- })
- // 3、任务列表
- List({ space: 10 }) {
- ForEach(
- this.tasks,
- (item: Task, index: number) => {
- ListItem() {
- Row() {
- Text(`任务${item.name}`)
- Checkbox()
- .select(item.finished)
- .onChange((val) => {
- item.finished = val //更新任务状态
- this.handleTaskChange()
- })
- }
- .card()
- .justifyContent(FlexAlign.SpaceBetween)
- }
- .swipeAction({ end: this.DeleteButton(index) })
-
- }
- )
- }
- .width('100%')
- .alignListItem(ListItemAlign.Center)
- .layoutWeight(1)
- }
-
- }
-
- @Builder DeleteButton(index: number) {
- Button('删')
- .width(40)
- .height(40)
- .backgroundColor(Color.Red)
- .margin(10)
- .onClick(() => {
- this.tasks.splice(index, 1)
- this.handleTaskChange()
- })
- }
- }
ps:
@Provide 和 @Consume 不需要显式传递数据,内部会自己做数据同步,即不用传参(@Provide 提供, @Consume 直接消费);
@Observed 和 @ObjectLink 装饰器用于在涉及嵌套对象或数组元素为对象的场景中进行双向数据同步。
a. 嵌套对象-使用说明
嵌套对象的类型上添加 @Observed 装饰器,不管嵌套几个类型,凡是嵌套的类型都要加上 @Observed 装饰器;
给嵌套对象内部的对象加上 @ObjectLink 装饰器;
如果嵌套对象内部的对象是作为一个方法的参数传递的就不能加,解决办法:定义一个组件,然后将内部嵌套对象传给这个组件,那么这个组件可以作为一个变量,就可以加 @ObjectLink 装饰器了
b. 数组元素对象-使用说明
如图:gfs: Person[]
- // 任务类
- @Observed
- class Task {
- static id: number = 1
- // 任务名称
- name: string = `任务${Task.id++}`
- // 任务状态:是否完成
- finished: boolean = false
- }
-
- // 统一卡片样式
- @Styles function card() {
- .width('90%')
- .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')
- .fontSize(20)
- }
-
- // 任务统计信息
- class StatInfo {
- totalTask: number = 0 // 总任务数量
- finishTask: number = 0 // 已完成任务数量
- }
-
- @Entry
- @Component
- struct PropPage {
- // 统计信息
- @Provide stat: StatInfo = new StatInfo()
-
- build() {
- Column({ space: 10 }) {
- // 1、任务进度卡片
- TaskStatistics()
- // 3、任务列表
- TaskList()
- }
- .width('100%')
- .height('100%')
- .backgroundColor('#f1f2f3')
- }
- }
-
- @Component
- struct TaskStatistics {
- @Consume stat: StatInfo
-
- build() {
- Row() {
- Text(`任务进度:`)
- .fontSize(30)
- .fontWeight(FontWeight.Bold)
- Stack() {
- Progress({
- value: this.stat.finishTask,
- total: this.stat.totalTask,
- type: ProgressType.Ring
- })
- .width(100)
- Row() {
- Text(this.stat.finishTask.toString())
- .fontSize(24)
- .fontColor('#36d')
- Text('/' + this.stat.totalTask.toString())
- .fontSize(24)
- .fontColor('#36d')
- }
- }
- }
- .card()
- .margin({ top: 20, bottom: 20 })
- .justifyContent(FlexAlign.SpaceAround)
- }
- }
-
- @Component
- struct TaskList {
- @Consume stat: StatInfo
- // 任务数组
- @State tasks: Task[] = []
-
- handleTaskChange() {
- // 更新任务总数
- this.stat.totalTask = this.tasks.length
- // 更新已完成任务总数
- this.stat.finishTask = this.tasks.filter(item => item.finished).length
- }
-
- build() {
- // 子组件只能有一个根元素
- Column() {
- // 2、新增任务按钮
- Button('新增任务')
- .width(200)
- .margin({ bottom: 20 })
- .onClick(() => {
- this.tasks.push(new Task()) // 在数组中添加一条数据
- this.handleTaskChange()
- })
- // 3、任务列表
- List({ space: 10 }) {
- ForEach(
- this.tasks,
- (item: Task, index: number) => {
- ListItem() {
- /*
- * 1、传递的是方法: this.handleTaskChange ,因为不是调用方法所以不能写 this.handleTaskChange()。
- * 2、因为this.handleTaskChange这个方法是在子组件里执行,所以这个方法的this就不是父组件的this,而是子组件的this;
- * 因为子组件没有tasks这个数组,所以会找不到;
- * 3、传递的过程中想要保证 this 不要变的办法:
- * 函数在传递时有一个方法 bind(this),即:
- * 把当前父组件中的 this 绑定到这个函数里面,那么这个函数里的 this 就永远都是当前这个父组件
- */
- TaskItem({ item: item, onTaskChange: this.handleTaskChange.bind(this) })
- }
- .swipeAction({ end: this.DeleteButton(index) })
-
- }
- )
- }
- .width('100%')
- .alignListItem(ListItemAlign.Center)
- .layoutWeight(1)
- }
-
- }
-
- @Builder DeleteButton(index: number) {
- Button('删')
- .width(40)
- .height(40)
- .backgroundColor(Color.Red)
- .margin(10)
- .onClick(() => {
- this.tasks.splice(index, 1)
- this.handleTaskChange()
- })
- }
- }
-
- @Component
- struct TaskItem {
- @ObjectLink item: Task
- /*
- * 1、子组件想要调父组件的方法:
- * 可以将这个父组件的方法作为一个参数传递进来,但是在传递过程中会出现this丢失
- * 2、解决this丢失的办法:
- * 父组件传递参数时使用 .bind(this) ,将父组件的this绑定给子组件,即:this.handleTaskChange.bind(this)
- */
- onTaskChange: () => void // 这是一个变量,变量类型是一个函数
-
- build() {
- 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)
- }
- }
ps:
子组件想要调父组件的方法会造成 this 丢失,解决办法:父组件传递参数时使用 .bind(this) ,将父组件的this绑定给子组件,即:this.handleTaskChange.bind(this)
页面路由:指在应用程序中实现不同页面之间的跳转和数据传递。
a. 页面栈的最大容量上限为32个页面,使用 router.clear()
方法可以清空页面栈,释放内存;
b. Router 有两种页面跳转模式,分别是:
router.pushUrl()
:目标页不会替换当前页,而是压入页面栈,因此可以用 router.back()
返回当前页;
router.replaceUrl()
:目标页会替换当前页,当前页会被销毁并释放资源,无法返回当前页;
c. Router 有两种页面实例模式,分别是:
Standard
:标准实例模式,每次跳转都会新建一个目标页并压入栈顶,默认模式;
Single
:单实例模式,如果目标页已经在栈中,则离栈顶最近的同Url页面会被移动到栈顶并重新加载;
- // 在页面顶部引入
- import router from '@ohos.router';
ps:一般在页面中使用 router 时,会自动引入,所以这一句可以不用自己写,跳过;
跳转到指定页面
- // 跳转到指定路径,并传递参数
- router.pushUrl(
- // 1、RouterOptions
- // - url:目标页面路径
- // - params:传递的参数,可选
- {
- url: 'pages/ImagePage',
- params: {id: 1},
- },
- // 2、页面模式:RouterMode 枚举
- router.RouterMode.Single,
- // 3、异常响应的回调函数错误码:
- // 100001:内部错误,可能是渲染失败
- // 100002:路由地址错误
- // 100003:路由栈中页面超过32个
- err => {
- if(err){
- console.log('路由失败')}
- }
- )
获取参数,返回上一页或指定页
- // 获取传递过来的参数
- params: any = router.gerParams()
-
- // router.back()不传参,则返回上一页
- router.back()
-
- // router.back()传参,则返回到指定页,并携带参数
- router.back(
- {
- url: 'pages/ImagePage',
- params: {id: 10},
- }
- )
ps:
如果不是上述方法创建页面,则需要在 base/main_pages.json 文件中配置页面路径,格式如下:
{
"src": [
"pages/Index",
"pages/ImagePage", // 对应页面的路径
]
}
- // 返回前的二次确认弹窗
- router.showAlertBeforeBackPage({
- message:'确认返回吗?'
- })
- // 返回上一页
- router.back()
点击返回后,会前弹出确认弹窗,具体流程如图所示:
点击“确定”,则走 router.back() 返回上一页;
点击“取消”,则停留在当前页面;
属性动画:是通过设置组件的 animation 属性来给组件添加动画,当组件的 width、height、Opacity、backgroundColor、scale、rotate、translate 等属性变更时,可以实现渐变过渡效果。
ps:animation 属性,放在需要变化的样式属性后面,否则不会生效。
显示动画:是通过全局 animateTo 函数来修改组件属性,实现属性变化时的渐变过渡效果。(使用更多的一种方式)
HarmonyOS为开发者提供了Stage应用模型,是应用程序所需能力的抽象提炼,它提供了应用程序必备的组件和运行机制。有了应用模型,开发者可以基于一套统一的模型进行应用开发,使应用开发更简单、高效。
Ability Module 可以创建多个,可以将不同的能力放到不同的模块中开发;其中,通用的功能可以抽取出来放至 Library Module 模块中(Library Module 顾名思义:共享依赖类型的模块)。
源码编译——>打包码——>App 安装包;
所有的 Ability 模块都会被编译成HAP文件(鸿蒙能力类型包),所有的 Library 模块都会被编译成HSP文件(鸿蒙共享类型包);HAP包在运行过程中可以去引用和依赖HSP包;
一个应用内部可能会有很多不同的能力,就会有很多不同的Ability Module,所以会有多个HAP文件;
HAP 文件间的差异:
Entry类型的HAP,是开发应用的主要能力模块,入口HAP文件,只能有一个;
Feature类型的HAP,是拓展功能,可以有多个;
采用这种多HAP文件打包模式的目的:
为了降低不同功能模块间的耦合,每一个模块都可以独立编译和运行;
应用在下载安装时,首先安装核心模块Entry,其他Feature可以选择性安装,这样能降低应用安装时的体积;
3. 很多HAP合并在一起叫Bundle,Bundle有一个自己的名字叫 bundleName ,是应用的唯一标识;
4. Bundle合并打包会变成一个APP,即.app的安装包;
每一个HAP在运行时都会创建一个AbilityStage实例,来展示应用能力组件;
应用能力组件有很多类型,比较常见的有: UIAbility、ExtensionAbility;
UIAbility:包含UI界面的应用组件,是系统调度的基本单元。
在展示组件时,首先会持有一个 WindowStage 实例对象,WindowStage 会持有 Window对象(即:用来绘制UI页面的窗口),窗口里会展示ArkUI Page(UI界面);
ExtensionAbility:拓展的能力组件,如应用卡片、输入法;
文件路径:AppScope > app.json5
代码说明:
- {
- "app": {
- // 应用唯一标识(在创建应用时就设置了),项目发布、打包、部署时都会用到
- // 格式要求:域名倒置的方式进行定义,类似“包名”
- "bundleName": "com.example.myapplication",
- "vendor": "example",
- // 版本——数字格式,versionName、versionCode需同步变化更新
- "versionCode": 1000000,
- // 版本——字符串格式
- "versionName": "1.0.0",
- // 应用列表中的图标
- "icon": "$media:app_icon",
- // 应用列表中的描述字符
- "label": "$string:app_name"
- }
- }
文件路径:entry > src > main > module.json5
代码说明:
- {
- "module": {
- // 申请系统权限
- "requestPermissions": [
- {
- "name": "ohos.permission.INTERNET",
- },
- ],
- // 当前模块的名字
- "name": "entry",
- // 当前模块的类型,一共有三种:entry、feature、shared(共享,即Library模块的类型)
- "type": "entry",
- // 当前模块的描述
- "description": "$string:module_desc",
- // 当前模块的入口名字(默认启动的 ability)
- "mainElement": "EntryAbility",
- // 设备类型:如果有多个模块也可以供给不同的设备类型使用
- "deviceTypes": [
- "phone",
- "tablet"
- ],
- // 当前模块是否跟随应用一起安装
- "deliveryWithInstall": true,
- "installationFree": false,
- // 当前模块包含的所有页面
- "pages": "$profile:main_pages",
- // 一个模块下如果有多个ability,则都需要填入
- "abilities": [
- {
- "name": "EntryAbility",
- // 当前 ability对应的源码路径
- "srcEntry": "./ets/entryability/EntryAbility.ts",
- // 当前 ability的描述
- "description": "$string:EntryAbility_desc",
- /* 注意:
- 因为:该模块是入口模块,且该ability是入口ability,
- 所以:ability的图标和描述(icon、label)就是当前应用的图标和描述。
- =》就是操作系统桌面上的那个应用图标和名称
- */
- // 当前ability的图标
- "icon": "$media:icon",
- // 当前ability的名称
- "label": "$string:EntryAbility_label",
- "startWindowIcon": "$media:icon",
- "startWindowBackground": "$color:start_window_background",
- "exported": true,
- // 当前ability负责的功能需要在skills下指定,与ability之间的跳转有关系
- "skills": [
- // home:代表入口的意思
- {
- "entities": [
- "entity.system.home"
- ],
- "actions": [
- "action.system.home"
- ]
- }
- ]
- }
- ]
- }
- }
修改string文件便捷方法:打开编辑器
官方文档:app.json5配置文件-应用配置文件(Stage模型)-开发基础知识-入门
启动应用的生命周期:onCreate
—— onWindowStageCreate
—— onForeground
—— 加载页面;
将应用程序后台的生命周期:onBackground
;
将应用程序前台的生命周期:onForeground
;
清理应用的生命周期:onWindowStageDestroy
—— onDestroy
;
hilog
日志输出使用- // 带上级别的日志
- /*
- 1、16进制的数字,用来标识不同模块的参数
- 2、tag标记,模块下的功能标识
- 3、日志输出的内容:线上部署时是否公开(public公开 private不公开)
- 4、参数3处占位符具体的值是什么,取决于参数4传递的内容
- */
- hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
PageA:组成当前页面的入口组件;ComponentA:子组件;
创建组件实例
——> 执行build函数
ps:
所有的组件做加载,都是上述步骤;
build函数全部执行完毕后,组件才算绘制完成;
只要页面被销毁,里面的组件肯定被销毁;反过来,如果页面还在,里面的子组件不一定还在,因为用户在互动操作中,里面的部分子组件如果不需要显示了就会被销毁(因为组件不存在隐藏,组件不显示就销毁)。
页面隐藏,不一样页面就被销毁,replaceUrl页面被销毁,pushUrl页面没有被销毁。
入口组件被销毁,里面的子组件也会被销毁。
组件的生命周期钩子
aboutToAppear
:创建组件实例之后,且执行build函数之前触发;
可以在这个钩子里面进行数据的初始化和准备工作,然后build函数中就可以利用这些数据,完成渲染了;
aboutToDisappear
:销毁组件之前触发;
可以在这个钩子里面完成一些数据持久化、数据保存、资源释放等操作;
2. 页面的生命周期钩子
onPageShow
:页面展示之后触发;
onBackPress
:点击“返回”按钮时触发;
onPageHide
:页面隐藏之前触发;
3. 注意
页面的生命周期钩子,只能在加了 @Entry 的入口组件中使用,普通的自定义组件中不能使用;
组件的生命周期钩子,都可以使用;
4. 执行顺序验证
演示步骤——动图
演示步骤——图示
演示步骤——详细说明
(1)加载IndexPage页面
触发入口组件的创建 —— 执行 aboutToAppear 函数(执行完毕,即组件创建完毕)—— 执行build函数(执行完毕后,展示page)—— 触发onPageShow
(2)加载LifeCirclePage页面
ps1:从IndexPage页面,通过pushUrl的方式,跳转到LifeCirclePage页面
ps2:pushUrl方式:即,IndexPage页面是隐藏,不是销毁
首先创建页面组件,执行aboutToAppear函数 —— 隐藏IndexPage页面——展示LifeCirclePage页面
(3)LifeCirclePage页面子组件的显示、隐藏:(if/else控制时)
显示:执行 aboutToAppear 函数 —— 隐藏:执行 aboutToDisappear 函数
(4)LifeCirclePage页面子组件的新增、删除:(foreach控制时)
新增1个:执行 aboutToAppear 函数 —— 删除1个:执行 aboutToDisappear 函数
新增3个:执行 aboutToAppear 函数3次 —— 删除第一个:执行 aboutToDisappear 函数3次,执行 aboutToAppear 函数2次(因为foreach循环渲染有一个特性:当数组中的元素发生变更时,会检查数组中的元素那些发生了变更,对发生了变更的元素进行重新渲染,重新渲染就会把之前的所有元素进行销毁,再重新渲染)
(5)返回IndexPage页面(通过系统提供的返回按钮返回)
触发onBackPress函数(只有系统提供的返回按钮才会触发)—— 执行onPageHide函数(隐藏LifeCirclePage页面)—— 执行onPageShow函数(展示IndexPage页面,同时,栈顶页面销毁)—— LifeCirclePage页面执行aboutToDisappear 函数 —— 子组件执行aboutToDisappear 函数(LifeCirclePage页面销毁,里面的子组件也会被销毁)
(6)跳转到TestPage1页面
配置路径:entry\src\main\module.json5
默认启动模式;
每一个UIAbility只存在唯一实例;
任务列表中,只会存在一个相同的UIAbility;
每次启动UIAbility都会创建一个新的实例;
任务列表中,可能存在一个或多个相同的UIAbility;
ps:
经过实际测试 standard 和 multiton 非常接近,但有不一样的地方:
相同点:每次启动UIAbility都会创建一个新的实例;
不同点:创建新实例,standard模式的旧实例也会并存;multiton模式的旧实例会被移除;
每个UIAbility实例可以设置Key标示;
启动UIAbility时,需要指定Key,存在key相同实例直接被拉起,不存在则创建新实例;
在一个Ability中调用另一个Ability的步骤:
第一步:当前UIAbility方法拉起目标UIAbility
1.1 获取上下文
context = getContext(this) as common.UIAbilityContext
1.2 指定要跳转到的UIAbility的信息
- let want = {
- deviceId:'', // deviceId为空:表示本设备
- bundleName:'com.example.myapplication',
- abilityName:'entry', // moduleName 非必填
- Parameters: {
- // getInstanceKey:自定义方法,生成目标UIAbility实例的key
- instanceKey: this.getInstanceKey()
- }
- }
1.3 尝试拉起目标UIAbility实例
this.context.startAbility(want)
第二步:在AbilityStage的生命周期回调中为目标UIAbility实例生成key
- export default class MyAbilityStage extends AbilityStage{
- onAcceptWant(want: Want): string {
- // 判断当前要拉取的是否是DocumentAbility
- if(want.abilityName === 'DocumentAbility'){
- // 根据参数中的instanceKey参数拼接生成一个key值并返回
- return `DocAbility_${want.parameters.instanceKey}`
- }
- return '';
- }
- }
第三步:在module.json5配置文件中,通过srcEntry参数指定AbilityStage路径
- {
- "module": {
- "name": "entry",
- "type": "entry",
- "srcEntry": "./ets/myabIlitystage/MyAbIlityStage.ts",
- ...
- }
- }
import http from '@ohos.net.http'
- // 1. 创建一个http的请求对象,不可复用
- let httpRequest = http.createHttp()
- // 2. 发起网络请求
- httpRequest.request(
- // 请求url路径
- 'http://localhost:3000/users',
- // 请求选项 HttpRequestOptions
- {
- method:http.RequestMethod.GET,
- extraData: {'param1':'value1'} // k1=v1 & k2=v2
- }
- )
- // Promise:存放未来会完成的结果
- // 3. 处理响应结果
- .then((resp:http.HttpResponse) => {
- if(resp.responseCode === 200){
- // 请求成功
- }
- })
- .catch((err: Error) => {
- // 请求失败
- })
第一步:下载ohpm工具包:DevEco Studio-HarmonyOS SDK下载和升级-华为开发者联盟
第二步:解压工具包,执行初始化命令
将压缩包放在自己习惯的安装路径下 —— 解压到当前文件夹 —— 进入文件夹的“ohpm/bin”目录 —— cmd,执行初始化命令“init.bat” —— 验证是否执行成功“ohpm -v”,出现版本号就是成功
- // windows 环境
- init.bat
-
- // Linux 或 Mac 环境
- ./init.sh
第三步:将ohpm配置到环境变量
- // windows环境,直接在我的电脑配置即可
- 此电脑 > 属性 > 高级系统设置 > 高级 > 环境变量中:
- 1. 新建:名称“ OHPM_HOME ”、路径“ 解压文件夹打开到ohpm这一级的对应路径 ”
- 2. 双击Path —— 新建:添加路径“ %OHPM_HOME%\bin ”
-
- // Linux 或 Mac 环境,其中 OHPM 的路径请替换为 ohpm 的安装路径
- export OHPM_HOME=/home/xx/Downloads/ohpm //OHPM_HOME=ohpm的安装路径
- export PATH=${OHPM_HOME}/bin:${PATH}
官方文档:ohpm使用指导-命令行工具-DevEco Studio使用指南-工具
第一步:下载axios
- // 进入项目目录,输入命令
- ohpm install @ohos/axios
第二步:开放网络权限
- // 在模块的module.json5文件中配置网络权限
- {
- "module": {
- "requestPermissions": [
- {
- "name": "ohos.permission.INTERNET",
- }
- ]
- }
- }
官方地址:https://ohpm.openharmony.cn/#/cn/home
导入axios
import axios from '@ohos/axios'
发送请求并处理响应
- // 请求方式,不同方式使用不同方法
- axios.get(
- 'url', // 请求路径
- {
- params: {'param1':'value1'}, // 请求选项
- data: {'param1':'value1'}
- }
- )
- // 响应结果
- .then(response => {
- if(response.staus === 200){
- console.log('查询成功')
- }else{
- console.log('查询失败')
- }
-
- })
- .catch(error => {
- console.log('查询失败',JSON.stringify(error))
- })
作用:为应用提供 Key-Value 键值型的数据处理能力,支持应用持久化轻量级数据。
1、导入首选项模块
import dataPreference from '@ohos.data.preferences'
2、获取首选项实例,读取指定文件
- dataPreference.getPreferences(this.context,'MyAppPreferences')
- .then(preferences => {
- // 获取成功
- })
- .catch(reason=> {
- // 获取失败
- })
3、数据操作
- // 1.写入数据,如果已经存在,则会覆盖,可利用.has()判断是否存在
- preferences.put('key',val)
- .then(() => preferences.flush()) // 刷到磁盘
- .catch(reason => {}) // 处理异常
-
- // 2.删除数据
- preferences.delete('key')
- .then(() => {})
- .catch(reason => {})
-
- // 3.查询数据
- preferences.get('key','defaultValue')
- .then(value => console.log('查询成功'))
- .catch(reason => console.log('查询失败'))
ps:
key为string类型,要求非空且长度不超过80字节;
value可以是string、number、boolean及以上类型数组,大小不超过8192字节;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。