跳到主要内容

ArkUI

系统组件

功能组件

信息常用类功能组件

信息展示类
  • Text组件

  • Image组件

  • LoadingProgress加载动效组件

  • QRCode二维码组件

Text('信息类组件')
// 2.图片组件
// 在线图片
Image('https://foruda.gitee.com/avatar/1750745604966482011/9556293_mayuanwei_1750745604.png!avatar200')
.width(100)
// 本地资源图片
Image($r('app.media.notes'))
.width(100)
// 系统图标:部分图片只设置宽度无法加载,需要宽高同时设置才能展示
Image($r('sys.media.ohos_ic_public_add'))
.width(100)
.height(100)
// 3.加载动效组件
LoadingProgress().width(100)
// 4.二维码组件
QRCode('Dxin').width(100)
信息输入类
  • 文本输入框
  • 搜索框
  • 滑动条
  • 按钮
TextInput() //单行文本输入框
TextArea() //多行文本输入框
Search() //搜索框
Slider() // 滑动组件
Button('按钮') //按钮组件
信息选择类

需要设置对应组件参数

单选
Row() {
Text('男')
Radio({ value: "男", group: "gender" })
}

Row() {
Text('女')
Radio({ value: "女", group: "gender" })
}
多选
Checkbox()
.shape(CheckBoxShape.CIRCLE)
Checkbox()
.shape(CheckBoxShape.ROUNDED_SQUARE)
下拉框
// 下拉框
Select([{ value: 'dxin' }, { value: '帝心' }])
切换
  • 创建包含子组件的Toggle。当ToggleTypeButton时,只能包含一个子组件,如果子组件有文本设置,则相应的文本内容会显示在按钮上。
  • 通过selectedColor属性设置Toggle打开选中后的背景颜色。
  • 通过switchPointColor属性设置Switch类型的圆形滑块颜色,仅对typeToggleType.Switch生效。
// 当ToggleType为Button时,只能包含一个子组件,如果子组件有文本设置,则相应的文本内容会显示在按钮上。
Toggle({ isOn: true, type: ToggleType.Button }) {
Text('切换')
}
// 通过selectedColor属性设置Toggle打开选中后的背景颜色。
.selectedColor('#ffe59f9f')

Toggle({ type: ToggleType.Checkbox })
.selectedColor('#ffc45151')

Toggle({ isOn: true, type: ToggleType.Switch })
// 通过switchPointColor属性设置Switch类型的圆形滑块颜色,仅对type为ToggleType.Switch生效。
.switchPointColor('#ccc')
Toggle案例

Toggle案例

import { promptAction } from '@kit.ArkUI'

@Entry
@ComponentV2
struct ChoseExam {
@Local flag: string = 'Off'

build() {
Column({ space: 30 }) {
Text('Bluetooth Mode:' + this.flag)
Row() {
Text('Bluetooth')
// .layoutWeight(1)
Blank()
Toggle({ type: ToggleType.Switch })
.onChange((isOn: boolean) => {
if (isOn) {
this.flag = 'On'
promptAction.openToast({
message: 'Bluetooth Mode:On',
// duration: 3000
// bottom:400
})
} else {
this.flag = 'Off'
promptAction.openToast({
message: 'Bluetooth Mode:Off'
})
}
})
}
.width('100%')
.height(60)
.backgroundColor('#fff')
.padding({ left: 20, right: 30 })
// .justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.theme_color'))

}
}

常用功能组件

  • 日期选择器DatePicker
  • 信息标记Badge
  • 评分Rating
  • 密码锁PatternLock
// 日期选择器
DatePicker()
.height(150)
// 时间选择器
TimePicker()
.height(150)
// 信息标记
Badge({ count: 3, style: {} }) {
// 要标记的内容
Image($r('sys.media.ohos_ic_public_scan'))
.width(100)
}

// 打分
Rating()
// 密码锁
PatternLock()
.width(200)
  • 列表组件List+ListItem

  • 轮播Swiper

    • Swiper的尺寸靠内容撑开

  • 滚动Scroll

    • 使用Scroll时需要注意:
      • 子组件内容需要超出父组件才会滚动
      • Scroll只能有一个子组件

布局组件

ArkUI常见布局:

  • 线性布局:水平或垂直排列的布局

    • Row:水平排列,垂直居中

    • Column:垂直排列,水平居中

    • 共同点:

      1. 不给定大小时,大小由内容决定
      2. 给定大小时,内容超出不换行
  • 层叠布局:元素可以重叠的布局

    • Stack:后一个子组件层叠在上一个子组件之上,默认居中对齐
  • 弹性布局:对子元素进行排列、对齐、和分配剩余空间的布局

    • Flex
      1. 可以换行,设置元素的间距
      2. 可以设置子元素主轴交叉轴对其方式
  • 栅格布局:规律性划分行列结构,指定子元素占位的布局

    • GridRow+GridCol:设置容器GridRow的总列数,填充子元素GridCol
    • GridRow默认分为四列,通过组件参数columns修改总列数,通过gutter参数设置每一列上下左右的间距。
    • GridCol默认占一列。通过组件参数span修改占了几列。order参数默认为0,可以修改序号来控制摆放的先后顺序。
    interface GridColItem {
    value: string
    order: number
    }

    @Entry
    @Component
    struct GridLayout {
    arr: GridColItem [] = [
    {value:'0',order:13},
    {value:'1',order:8},
    {value:'2',order:9},
    {value:'3',order:10},
    {value:'4',order:4},
    {value:'5',order:5},
    {value:'6',order:6},
    {value:'7',order:0},
    {value:'8',order:1},
    {value:'9',order:2},
    {value:'x',order:3},
    {value:'-',order:7},
    {value:'+',order:11},
    {value:'÷',order:12},
    {value:'·',order:14},
    {value:'=',order:15},
    ]

    build() {
    Column({ space: 30 }) {
    GridRow({ columns: 4 ,gutter:3}) {
    GridCol({ span: 4 }) {
    TextInput()
    .backgroundColor('#fff')
    }
    ForEach(this.arr, (item: GridColItem, index) => {
    GridCol({order:item.order}) {
    Text(item.value)
    .width('100%')
    .height('100%')
    .textAlign(TextAlign.Center)
    }
    .height(50)
    .backgroundColor(item.order===15 ? '#ff99e3c5' : '#ffe9b5b5')
    })

    }
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.theme_color'))
    .padding({top:12})

    }
    }
  • 相对布局:设置子元素相对位置关系的布局

    • RelativeContainer
  • 选项卡布局:设置页签和内容视图切换的布局

    • Tabs+TabContent:选项卡组件,通过页签进行内容视图切换的容器组件,每个页签对应一个内容试图。
    • TabContent组件的tabBar属性设置页签。Tabs的组件参数barPosition控制页签的位置。Tabs的属性方法vertical设置页签是否垂直。

常用组件的通用属性

尺寸类属性

属性名说明
width
height
size宽和高的综合写法
aspectRatio设置组件的宽高比
layoutWeight设置组件在主轴占比
margin设置组件外边距
padding设置组件内边距
constraintSize设置约束尺寸,主要用于尺寸范围的控制

位置类属性

属性说明
position绝对定位,确定子组件相对父组件内容区的位置。自身不占位置。
offset相对偏移,组件相对原本的布局位置进行偏移。和position一起使用时,position生效,offset不生效
背景类属性
backgroundImage设置组件的背景图
backgroundImageSize设置组件的背景图尺寸
backgroundBlurStyle设置组件背景材质模糊。(可以实现类似渐变色的效果)
边框类属性
border设置组件的边框
borderRadius设置组件的边框圆角
clip设置是否裁剪子组件超出自身范围的区域
图形变换类属性
rotate设置组件的旋转
translate设置组件的平移
scale设置组件的缩放

阴影shadow

shadow(options: Optional<ShadowOptions | ShadowStyle>): T

入参类型为ShadowOptions时,可以指定模糊半径、阴影的颜色、X轴和Y轴的偏移量、内外填充模式。

入参类型为ShadowStyle时,可指定不同阴影样式。

Row() {}.width(20).height(150).borderRadius(10)
.backgroundColor('#ffe76666')
//.shadow(ShadowStyle.OUTER_DEFAULT_LG)
.shadow({
radius: 30,
// type: ShadowType.BLUR, // 模糊阴影。模糊方式,那就没有颜色效果了
type: ShadowType.COLOR, // 颜色阴影
color: '#ff0e3991',
offsetX: -30,
offsetY: 20,
fill: false //阴影在外部填充
})

颜色渐变属性

linearGradient线性渐变
linearGradient(options: Optional<LinearGradientOptions>): T
LinearGradientOptions说明
colors必选指定渐变色颜色和其对应的百分比位置的数组
angle线性渐变的起始角度。角度为0度时渐变方向为从下往上(即0点方向)。0点方向顺时针旋转为正向角度。
direction线性渐变的方向,设置angle为非undefined后不生效。设置为GradientDirection.None时,按默认方向渐变。默认值:GradientDirection.Bottom。
repeating为渐变的颜色重复着色。

注意点:

  1. angle与direction两者选一即可,若同时设置,则只有angle生效
  2. 若参数repeating为true,则colors数组中任何元素的百分比位置都不应该为1。
  3. 若colors数组中最后一个元素的百分比位置小于1,且不重复着色,则从该处往后一直都是该纯色。
  4. 颜色数组中,number表示该颜色所处的位置,取值范围为[0, 1.0],设置的值小于0时,按0处理,设置的值大于1.0时,按1.0处理。为了实现多个颜色渐变效果,多个数组中的number类型参数应递增设置。如果后一个数组中的number类型参数小于前一个数组的number类型参数,将按照等于前一个数组number值处理。
Row() {}.width(20).height(150).borderRadius(10)
// 线性渐变
.linearGradient({
// angle:135, //起始角度180
colors: [
['#66ccff', 0.25],
['#9900ff', 0.3],
[Color.Blue, 0.4],
['#66ff66', 0.5],
[Color.Green, 0.6],
[Color.Yellow, 0.7],
[Color.Orange, 0.8],
[Color.Red, 0.9],
['#66ccff', 1]
],
// 如果设置了angle direction不生效
// direction:GradientDirection.Left // 往左渐变
// direction:GradientDirection.LeftTop, // 向左上渐变
// repeating属性存在时,则颜色数组中的最大值不应该出现1.
repeating:true, //默认false 不重复着色。 true,从数组中重头渐变一下
})
sweepGradient角度渐变
sweepGradient(options: Optional<SweepGradientOptions>): T
SweepGradientOptions
center必选为角度渐变的中心点,即相对于当前组件左上角的坐标。
colors必选指定渐变色颜色和其对应的百分比位置的数组
start角度渐变的起点。 默认值:0。
end角度渐变的终点。 默认值:0。
rotation角度渐变的旋转角度。默认值:0。
repeating为渐变的颜色重复着色。
  1. 渐变角度为0~360,若渐变角度超出这一范围,则仅绘制纯色。
  2. 若colors数组中最后一个元素的百分比位置小于1,且不重复着色,则从该处向后一直都是该纯色。
  3. 若repeating参数为true,结束色的百分比位置小于1,则结束角度和起始角度之间的角度差会产生一定的压缩。
//   角度渐变
Row(){}.width(100).height(100)
.borderRadius(50)
.sweepGradient({
center:[25,50],
// 角度的起始和终止点是可选的,。但是如果没写,没效果
start:0,
end:360,
colors: [
['#66ccff', 0.25],
['#9900ff', 0.3],
[Color.Blue, 0.4],
['#66ff66', 0.5],
[Color.Green, 0.6],
[Color.Yellow, 0.7],
[Color.Orange, 0.8],
[Color.Red, 0.9],
['#66ccff', 1]
],
repeating:true, // 这个属性跟颜色数组是否到1有关系
rotation:60 // 起始角度不从0开始了。而是可以指定从多少度开始渐变了
})
radialGradient径向渐变
radialGradient(options: Optional<RadialGradientOptions>): T
RadialGradientOptions
center必选径向渐变的中心点,即相对于当前组件左上角的坐标。
radius必选径向渐变的半径。
colors必选指定渐变色颜色和其对应的百分比位置的数组
repeating为渐变的颜色重复着色。
// 径向渐变
Row(){}.width('100%').height(250).backgroundColor('#ccc')
.radialGradient({
center:['50%',280],
radius:'100%', //径向渐变的尺寸
colors: [
['#66ccff', 0.25],
['#9900ff', 0.3],
[Color.Blue, 0.4],
['#66ff66', 0.5],
[Color.Green, 0.6],
[Color.Yellow, 0.7],
[Color.Orange, 0.8],
[Color.Red, 0.9],
['#66ccff', 1]
],
// repeating:true
})
显示隐藏类属性
visibility设置组件的是否可见:
visibility.Hideden:虽然隐藏了,但是会占位
visibility.None:隐藏,但是不占位
visibility.Visible:显示
opacity设置组件的透明度
displayPriority设置组件的显示优先级。父容器裁剪时,会隐藏掉优先级最低的子元素。
zlndex设置组件的Z轴堆叠顺序

私有属性

Text

属性效果
fontSize设置文字的大小
fontColor设置文字的颜色
fontWeight设置文字的粗细
letterSpacing设置每个文字之间的间距
textAlign设置文字的对齐方式
maxLines设置文字的展示最大行数,超出隐藏
textOverflow设置文字超出的样式
lineHeight设置文字的行间距

image

属性效果
alt设置图片加载时的占位图
objectFit设置图片的填充模式
fillColor设置SVG图片的填充色

事件

组件事件间的区别:不同的ArkUI系统组件在使用时会产生不同的交互,比如:文本输入框可以输入内容、列表可以滚动等,但是不同的组件也可以有相同的交互,比如:任何系统组件都可以点击

组件事件的分类:和属性一样,组件的事件也分为了通用事件和私有事件。所有组件都能添加的叫做通用事件。每个组件特有的事件叫做私有事件。

通用事件

onClick:点击
  • 触发条件:手指按下后抬起,仅支持单指

  • 事件对象:可以获取点击的位置

Button('按钮1')
.onClick(() => {
// 弹窗方式1
// promptAction.openToast({
// message:'点击事件'
// })
// 弹窗方式1
this.getUIContext().getPromptAction().showToast({
message: '点击事件',
duration: 4000
})
})
onFocus、onBlur焦点
  • 触发条件:当用户使用键盘、电视遥控器、车机摇杆/旋钮等非指向性输入设备与应用程序进行间接交互时,焦点在组件间移动时触发。

  • 限制:

    • 存在默认交互逻辑的组件例如ButtonTextlnput等,默认即可获焦
    • 默认无法获焦的组件(例如信息展示类组件中的文本,图片等组件)需要设置focusable属性为true才可触发
  • 事件对象:无

// 多行输入框
TextArea()
.onFocus(() => {
this.getUIContext().getPromptAction().showToast({
message: '输入框获取焦点了---拉起键盘'
})
})
.onBlur(() => {
this.getUIContext().getPromptAction().showToast({
message: '输入框获取失去焦点了---隐藏键盘'
})
})

Text('文本1')
// 默认不会获取焦点
.focusable(true)
onTouch:触摸
  • 触发条件:手指按下后开始触发,支持多指。

  • 事件对象:可以捕获按下、移动、抬起三种状态等

Stack() {
Image($r('app.media.gege'))
.width(200)
.focusable(true) // 获焦能力
.focusOnTouch(true) // 点击获焦能力
// 触摸
.onTouch((e: TouchEvent) => {
if (e.type === TouchType.Down) {
this.getUIContext().getPromptAction().showToast({
message: '按下--'
})
}
if (e.type === TouchType.Up) {
this.getUIContext().getPromptAction().showToast({
message: '抬起--'
})
}
if (e.type === TouchType.Move) {
this.getUIContext().getPromptAction().showToast({
message: '移动-- '
})
}
// 触摸的过程中 优先级>Move
if (e.touches) {
this.getUIContext().getPromptAction().showToast({
message: `touches--${e.touches[0].x}----${e.touches[0].y}`
})
this.xVal = e.touches[0].x
this.yVal = e.touches[0].y
}
})

Row() {
}
.width(50)
.aspectRatio(1)
.backgroundColor('#ff2ad68b')
.borderRadius(25)
.position({
x: this.xVal -25,
y: this.yVal -25
})
}
// 裁剪
.clip(true)
onAreaChange:组件区域变化事件
  • 触发条件:组件大小、位置变化时触发。

  • 事件对象:可以获取变化前、后的大小和位置状态

// 多行输入框
TextArea()
.onFocus(() => {
this.getUIContext().getPromptAction().showToast({
message: '输入框获取焦点了---拉起键盘'
})
})
.onBlur(() => {
this.getUIContext().getPromptAction().showToast({
message: '输入框获取失去焦点了---隐藏键盘'
})
})
.onAreaChange(() => {
// 组件绘制的时候会瞬间触发一次
// 组件大小、位置变化时触发。
this.getUIContext().getPromptAction().showToast({
message: `TextArea触发了---onAreaChange ${e.position.x}`
})
})

私有事件

组件事件类型触发条件事件对象
TextonCopy长按文本内部区域弹出剪贴板后,点击复制按妞。(需要使用属性方法copyOption来开启复制,否则长按是没有反应的)复制的文本内容
ImageonError图片加载异常时触发加载异常时返回的报错信息Image('https://xxx.jpg')
TextInputonChange输入内容发生变化时文本框正式上屏的文本内容
CheckBoxonChange选中状态发生变化时触发选中的状态
ToggleonChange开关状态发生变化时触发开启或关闭的状态
SlideronChange滑块滑动时触发当前选中的值
ListonReachEnd能滚动时,列表到达末尾位置时触发。回弹触底触发第二次。不满一屏时也会触发一次。无事件对象
ListonReachStart同上,触顶无事件对象
// 组件私有属性
Text('长按后选择复制')
// 需要开启复制
.copyOption(CopyOptions.InApp)
// 点击复制后触发
.onCopy((e) => {
// 使用轻吐司效果进行展示
this.getUIContext().getPromptAction().openToast({
message: `复制成功:${e}`
})
})

Image('https://xxx.jpg')
// 图片加载失败后触发
.onError((e) => {
this.getUIContext().getPromptAction().openToast({
message: `图片加载失败:${e.message}`
})
})
TextInput()
.onChange((e) => {
this.getUIContext().getPromptAction().openToast({
message: `当前输入的内容为:${e}`
})
})
Checkbox()
.onChange((e) => {
this.getUIContext().getPromptAction().openToast({
message: `当前选中的状态为:${e}`
})
})
Toggle({ isOn: false, type: ToggleType.Switch })
.onChange((e) => {
this.getUIContext().getPromptAction().openToast({
message: `当前开关的状态为:${e}`
})
})

Slider()
.onChange((e) => {
this.getUIContext().getPromptAction().openToast({
message: `当前滑动到的位置:${e}`
})
})
List({ space: 10 }) {
ListItem() {
Row()
.width('100%')
.height('200')
.backgroundColor('#ff9575e3')
}

ListItem() {
Row()
.width('100%')
.height('200')
.backgroundColor('#ff9575e3')
}

ListItem() {
Row()
.width('100%')
.height('200')
.backgroundColor('#ff9575e3')
}

ListItem() {
Row()
.width('100%')
.height('200')
.backgroundColor('#ff9575e3')
}

ListItem() {
Row()
.width('100%')
.height('200')
.backgroundColor('#ff9575e3')
}

ListItem() {
Row()
.width('100%')
.height('200')
.backgroundColor('#ff9575e3')
}
}
.layoutWeight(1)
// 设置边缘滑动效果
.edgeEffect(EdgeEffect.None) //关闭回弹,避免触底两次。方便在一些触底加载业务的时候使用。避免执行两次业务
.backgroundColor('#ffcfddd9')
.onReachEnd(() => {
this.getUIContext().getPromptAction().openToast({
message: `触底了`
})
})
.onReachStart(() => {
this.getUIContext().getPromptAction().openToast({
message: `触顶了`
})
})

自定义组件

  • 为什么要自定义组件

​ ArkUI提供了很多功能强大的系统组件,能够帮助我们快速实现一些常见的功能和UI效果。但是各个业务领域一般都会有一些特殊的定制需求,当系统组件不能够完全契合时,就需要自定义组件

  • 什么是自定义组件?

​ 自定义组件就是通过开发者组合系统组件和封装业务逻辑形成一个新的组件,系统组件是系统定义的组件,自定义组件是开发者定义的组件

  • 如何进行自定义组件?
    • 创建自定义组件:按照语法规范定义一个新的组件,可以反复使用。创建自定义组件也叫做封装组件或声明组件,每个ets文件都可以声明多个自定义组件,组件名遵循大驼峰规范且唯一
    • 使用自定义组件:与像使用系统组件一样,使用组件名()

自定义组件的导入及导出

组件所在的文件是可以直接使用这个组件的,但是由于自定义组件可以复用的便利性,我们也可以将自己的自定义的组件分享给自己需要开发的其他页面和组件使用,也可以分享给同项目的成员使用。当然,我们也可以使用其他项目成员分享组件来开发自己的页面,这个就需要对组件进行导入导出了。

  • 默认导出语法:export default 自定义名称。导出后,可以在当前模块内任意位置导入该组件

  • 默认导入语法:import 自定义名称 from 组件路径。导入后可以在当前文件任意位置使用该组件

在自定义组件中可以使用 @Preview 进行预览


多个组件导入导出

组件是构建页面的基本单元,我们通常会定义很多组件,也会引用很多的组件,这种多组件的导入导出也称为命名导入和命名导出

  • 命名导出语法:需要导出组件的struct前面添加export

  • 命名导入语法:import {组件1,组件2,组件3...} from 组件路径


自定义组件属性

  • 什么是自定义组件的属性?

​ 在自定义组件时,组件内声明的变量,这个变量就可以称为自定义组件的属性

  • 为什么要自定义组件的属性?

​ 我们在自定义组件时,可以将使用到的系统组件参数、属性或事件定义为组件属性,从而实现使用自定义组件像使用组通组件一样灵活多变。 ​ 比如:系统组件Text的文字颜色可以修改,如果我们自定义组件用了Text,自定义组件也可以实现文字颜色的修改

  • 组件传值

    在使用自定义组件时传递参数也叫做组件传值,传递的值会覆盖自定义组件中定义的属性值(也可以传递函数),自定义组件默认支持通用属性和通用事件**(在调用处进行使用)**。

  • 如何设计及使用自定义组件属性

书写位置:在struct内,build外定义属性,类似于变量数据。语法:属性名:类型=初始值。自定义组件定义属性并会不影响组件的使用与效果,该属性通过this.属性名使用在组件的属性方法时,才会发挥作用。

自定义组件使用

组件扩展

@Styles:定义组件重用样式

如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续

方便维护,可以提炼公共样式进行复用,也叫做定义组件重用样式。

  • 仅支持通用属性和通用事件
  • 可以定义在组件内,也可以定义在全局,但不支持导出,只能在当前文件内使用
  • 组件内与全局的重用样式同时使用时组件内优先生效

组件内定义重用样式:

  • 在struct内,build外,定义一个函数
  • 对这个函数使用@Styles装饰器修饰
  • 函数内书写提炼的重复样式属性
  • 对需要提炼的组件使用函数名的属性

全局定义重用样式:

  • 组件外使用function关键字定义一个函数
  • 对这个函数使用@Styles装饰器修饰
  • 函数内书写提炼的重复样式属性
  • 对需要提炼的组件使用函数名的属性

不支持导出,仅在当前组件内使用。

@Extends:

与定义重用样式相同的时都是用来提炼样式的。

但扩展组件样式用于指定某个组件进行样式扩展,即支持通用属性、通用事件,也支持私有属性和私有事件,同时支持参数传递

定义扩展组件样式说明:

  • 扩展指定组件,只有被扩展的组件才能使用
  • 仅支持在全局定义,同样不支持导出
  • 扩展组件样式支持传递参数

语法说明:

  • 组件外使用function关键字定义一个函数
  • 对这个函数使用@Extend()装饰器修饰
  • ()内写入需要扩展的组件名
  • 函数内书写提炼的重复的属性或事件
  • 不同的属性或事件可以通过参数接收
  • 对需要提炼的组件使用函数名的属性

多态样式

组件的内部状态,ArkUI提供了一下五种状态:normal正常态、focused获焦态、pressed按压态、disabled禁用态、selected选中

状态管理

V1版本

  • @State装饰器:组件内状态
  • @Prop装饰器:父子单向同步
  • @Link装饰器:父子双向同步
  • @Provide装饰器和@Consume装饰器:与后代组件双向同步
  • @Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

V2版本


渲染控制

条件渲染

if/else:条件渲染

循环渲染

ForEach:循环渲染

数据懒加载

ForEach适用的场景:数据源不变的列表渲染。数据懒加载适用的场景:长列表的优化渲染。

优化原理:从数据源中按需迭代数据,每次迭代时根据滚动容器可视区域按需创建相应组件,当组件滑出可视区域外时,框架会销毁并回

收组件以降低内存占用。

数据懒加载语法:LazyForEach( 数据源,结构体生成函数 [,键值生成函数 ] )

实现步骤:

  1. 实现IDataSource管理数据源的监听器和更新
  2. 定义数据源
  3. 使用LazyForEach渲染数据源
//src/main/ets/utils/LazyForEachType.ets
export class LazyForEachType<T> implements IDataSource {
data: T[] = []
listeners: DataChangeListener[] = []

totalCount(): number {
return this.data.length
}

getData(index: number): T {
return this.data[index]
}

registerDataChangeListener(listener: DataChangeListener): void {
// 如果当前监听器数组中找不到这个监听器。那就注册进数组
if (this.listeners.indexOf(listener)===-1){
this.listeners.push(listener)
}
}

unregisterDataChangeListener(listener: DataChangeListener): void {
// 如果监听器数组中有这个监听器,就卸载
const index = this.listeners.indexOf(listener)
if (index>-1) {
this.listeners.splice(index,1)
}
}

// 添加数据源的方法
pushData(data:T[]){
this.data.push(...data)
// 添加完数据源让视图更新
this.listeners.forEach((listener) => {
listener.onDataReloaded() //刷新数据
})
}
}
// src/main/ets/pages/LazyForEachPage.ets
import { LazyForEachType } from '../utils/LazyForEachType'

// 使用数据懒加载渲染列表数据
@Entry
@Component
struct LazyForEachPage {
// 数据源数组
pmArr: string [] = [
`我醉欲眠卿且去 明朝有意抱琴来`,
`卡布奇诺今犹在 不见当年倒茶人`,
`时来运转皆同力 运去英雄不自由`,
`堪笑一场梦颠倒 元来此生恰浮云`,
// `再见少年拉满弓 不惧岁月不俱风`,
// `我今因病魂颠倒 唯梦闲人不梦君`,
// `君埋泉下泥削骨 我寄人间雪满头`,
// `南来北往徒自老 故人稀`,
// `日拱一卒无有尽 功不唐捐终入海`,
// `何事人间频乞食 此心已是负彩霞`,
// `峨眉山月半轮秋 影入平羌江水流`,
// `天生我才必有用 千金散尽还复来`,
// `垂杨紫陌洛城东 今年花胜去年红`,
// `劝君莫负艳阳天 恩爱欢愉趁少年`,
// `何须浅碧深红色 自是花中第一流`,
// `我醉欲眠卿且去 明朝有意抱琴来`,
// `山有木兮木有枝 心悦君兮君不知`,
// `曾经沧海难为水 除却巫山不是云`,
// `玲珑骰子安红豆 入骨相思知不知`,
// `直道相思了无益 未妨惆怅是清狂`,
// `金风玉露一相逢 便胜却人间无数`,
// `从此无心爱良夜 任他明月下西楼`,
// `欲寄彩笺兼尺素 山长水阔知何处`,
// `若似月轮终皎洁 不辞冰雪为卿热`,
// `得成比目何辞死 愿作鸳鸯不羡仙`,
// `月孤明,风又起,杏花稀 `,
// `城南小陌又逢春 只见梅花不见人`,
]
// 定义懒加载数据源 泛型是数组中每一项的类型
listData: LazyForEachType<string> = new LazyForEachType()

build() {
Column({ space: 30 }) {
List() {
LazyForEach(this.listData, (item: string) => {
ListItem() {
Row() {
Text(item)
}
.width('100%')
.height(100)
.backgroundColor('#ffd2b0b0')
.justifyContent(FlexAlign.Center)
}
})
}
.width('100%')
.layoutWeight(1)
// 触底的时候给懒加载数据源添加数组数据
.onReachEnd(() => {
this.listData.pushData(this.pmArr)
})
// 避免两次处理添加两次数组
.edgeEffect(EdgeEffect.None)
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.theme_color'))

}
}

手势系统

手势是一系列基础事件不断上报积累后,达成一定特点时所被识别成的交互结果,比如:点击长按、拖动、滑动、旋转、捏合等

ArkUI系统组件会自动识别和响应部分手势,比如:按钮的点击、列表的滑动等,我们也可以在组件上显式绑定,自定义手势触发后的事件

组件可以通过组合手势设置多个互不影响的手势。

常用手势

常用手势名称触发条件
点击手势TapGesture点击手势支持单次点击和多次点击
长按手势LongPressGesture默认时长500毫秒触发
拖动手势PanGesture:默认滑动距离5vp触发
捏合手势PinchGesture最少2指,最多5指,默认最小捏合距离5vp触发
旋转手势RotationGesture最少2指,最多5指,默认最小旋转1度时触发
滑动手势SwipeGesture:最小识别触发速度为100vp/s

语法:

组件.getsture(
手势(触发事件参数)
.事件((事件对象) => {
// 触发事件时的回调函数
})
)
import { promptAction } from '@kit.ArkUI'

// 常见手势
@Entry
@Component
struct GesturePage {
@State angleVale: number =0
build() {
Column() {
Image($r('app.media.gege'))
.rotate({angle:this.angleVale})
.width(300)
.aspectRatio(1)
.backgroundColor('#ff6fde8b')
// 绑定手势
.gesture(
// // 拖动手势
// PanGesture()
// // 拖动过程中的更新状态
// .onActionUpdate((e) => {
// // fingerList当前手指信息
// //localX手指在当前元素区域的坐标
// this.getUIContext().getPromptAction().openToast({
// message: `当前坐标:${e.fingerList[0].localX},${e.fingerList[0].localY}`
// })
// })
// // 拖动开始
// .onActionStart((e) => {
// promptAction.openToast({ message: '拖动开始' })
// })
// // 拖动结束
// .onActionEnd((e) => {
// promptAction.openToast({ message: '拖动结束' })
// })
// // // 拖动取消
// .onActionCancel(() => {
// promptAction.openToast({ message: '拖动取消' })
// })
// 点击手势
// TapGesture()
// .onAction((e) => {
// promptAction.openToast({ message: '点击' })
// })
// 旋转手势
RotationGesture()
.onActionUpdate((e) => {
promptAction.openToast({ message: '旋转了'+e.angle })
// 让图片随着手指旋转(需要真机运行,预览器和模拟器无法模拟双指旋转)
this.angleVale = e.angle
})
)


}
.width('100%')
.height('100%')
}
}

组合手势:

手势识别组合,即两种及以上手势组合为复合手势,支持顺序识别、并发识别和互斥识别。

语法:

组件.getsture(
gestureGroup(识别模式,手势1,手势2...)
)

案例:实现长按后滑动选择功能,体验组合手势绑定的语法

组合手势案例之长按语音输入效果

// 组合手势案例:仿微信语音输入效果
// @Local
/*
* @Local
* 手势系统:长按手势 拖动手势
* onAreaChange
* 联合类型
* */
@Entry
@ComponentV2
struct CombinedGestures {
@Local isShow: Visibility = Visibility.Hidden
screenWidth: number = 0
@Local selectType: 'default' | 'cancel' | 'toText' = 'default'

build() {
Column({ space: 30 }) {
Blank()
.layoutWeight(1)
Row() {
Text('取消')
.SelectText(-30)
.backgroundColor(this.selectType === 'cancel' ? Color.Red : Color.Gray)
Text('转文字')
.SelectText(30)
.backgroundColor(this.selectType === 'toText' ? Color.Red : Color.Gray)
}
.backgroundColor('#ffbed9e2')
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
.layoutWeight(1)
// 默认不显示 按下的时候显示
.visibility(this.isShow)

Button('长按语音输入')
.margin({ bottom: 40 })
.gesture(
GestureGroup(
//顺序模式
GestureMode.Sequence,
//手势1 长按
LongPressGesture()
// 长按的时候显示文字选项
.onAction(() => {
this.isShow = Visibility.Visible
}),
// 手势2 拖动手势
PanGesture()
.onActionUpdate((e) => {
// 判断往左还是右
if (e.fingerList[0].globalX < this.screenWidth / 2) {
// 左
this.selectType = 'cancel'
} else {
// 右
this.selectType = 'toText'
}
})
.onActionEnd(() => {
this.isShow = Visibility.Hidden
})
)
)
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.theme_color'))
.onAreaChange((_old, _new) => {
// promptAction.openToast({message:_new.width.toString()})
this.screenWidth = _new.width as number
})
}
}

@Extend(Text)
function SelectText(angleVale: number) {
.width(150)
.height(50)
.textAlign(TextAlign.Center)
.fontSize(30)
.rotate({ angle: angleVale })
.borderRadius(25)
}

自定义构建函数

  • 什么是自定义构建函数?

ArkUI提供轻量的UI元素复用机制@Builder,其内部UI结构固定,仅与使用方进行数据传递。开发者可将重复使用的UI元素抽象成函数,

在build函数中调用。@Builder装饰的函数也称为“自定义构建函数”

  • 定义构建函数与自定义组件的区别:

@Builder装饰的函数,禁止在内部定义状态变量,只能通过参数传递或者访问所属组件的状态变量完成数据交互,可以将自定义构建函数理解是一个无状态变量的自定义组件

  • 定义构建函数的分类:

@Builder装饰器有两种使用方式,分别是在自定义组件内部的私有自定义构建函数和定义在全局的全局自定义构建函数


私有自定义构建函数

  • 创建私有自定义构建函数
  1. 组件定义一个函数

  2. 使用@Builder修饰这个函数

  3. 函数内定义需要复用的结构体

可以定义参数接收状态变量并使用。在函数内可以通过this访问所在组件的状态变量

  • 使用私有自定义构建函数
    • 使用this.私有自定义构建函数名() 可以进行调用。
    • 函数定义参数时,可以在调用的时候向函数传递参数。

全局自定义构建函数

  • 创建全局自定义构建函数
  1. 组件内定义一个函数
  2. 使用@Builder修饰这个函数
  3. 函数内定义需要复用的结构体

可以定义参数接收状态变量并使用。

  • 使用全局自定义构建函数
    • 使用全局自定义构建函数名() 可以进行调用
    • 函数定义参数时,可以在调用的时候向函数传递参数

参数传递规则

  1. 按值传递:

    调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder函数内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递。

  2. 按引用传递

    按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引l起@Builder函数内的UI刷新。

自定义构建函数参数传递的限制条件:

  1. 按引用传递且传入一个参数时,才会触发UI更新
  2. 传入的参数必须是对象字面量形式,才会触发UI更新
  3. 多个参数或同时按值传递与按引用传递,不会触发UI更新

引用自定义构建函数

  • 什么是引用自定义构建函数?

当开发者创建自定义组件并需要为其添加特定功能(例如页面跳转功能)时,如果直接在组件内嵌入事件方法,会导致所有该自定义组件的实例都增加此功能。为了解决此问题,ArkUI引l入了@BuilderParam装饰器,用于装饰指向自定义构建函数的变量。

  • 引用自定义构建函数的作用

@BuilderParam用于装饰指向@Builder方法的变量,开发者可以在初始化自定义组件时,可以使用组件传值或尾随闭包对@BuilderParam装饰的自定义构建函数进行传参赋值。在自定义组件内部,通过调用@BuilderParam为组件增加特定功能。

  • @BuilderParam

    • 定义:引用自定义构建函数需要使用@BuilderParam装饰器在组件内进行定义

      1. 定义引用自定义构建函数和定义组件变量一样

      2. 使用@BuilderParam修饰常规变量后,这个常规变量则表示为引用自定义构建函数

    • 初始化:@BuilderParam装饰的方法只能被自定义构建函数初始化

      1. 使用所属自定义组件的自定义构建函数或者全局的自定义构建函数,在本地初始化@BuilderParam装饰的方法
      2. 使用父组件自定义构建函数初始化子组件@BuilderParam装饰的方法
@Builder
NewToggleBuilder(){
Toggle({
isOn:true,
type:ToggleType.Switch
})
}

@BuilderParam
newToggle:()=>void = this.NewToggleBuilder
  • 本地初始化
  1. 组件内定义一个变量,类型为无返回值的函数类型
  2. 使用@BuilderParam修饰这个变量
  3. 定义一个自定义构建函数
  4. 使用自定义构建函数作为变量的初始值
  5. 引用自定义构建函数
  • 父组件初始化
  1. 定义一个子组件
  2. 子组件内定义一个变量,类型为无返回值的函数类型
  3. 使用@BuilderParam修饰这个变量
  4. 定义一个自定义构建函数
  5. 使用自定义构建函数作为变量的初始值
  6. 子组件引用自定义构建函数
  7. 父组件使用子组件,通过传递参数修改子组件自定义构建函数初始值(如果子组件只有一个引用自定义构建函数,可以使用尾随闭包)

案例:自定义组件中有多个应用自定义构建函数

自定义组件中有多个应用自定义构建函数

子组件

@Component
export default struct CustomAndRefComChild {
titleContent: string = '内容'

// 临时的布局效果:自定义构建函数
@Builder
leftContent() {
Text('左边')
}

@Builder
rightContent() {
Text('右边')
}

// 引用(使用)自定义构建函数 不能加在小括号
@BuilderParam
left: () => void = this.leftContent
@BuilderParam
right: () => void = this.rightContent


build() {
Row() {
this.left()
Text(this.titleContent)
.layoutWeight(1)
.textAlign(TextAlign.Center)
this.right()
}
.width('100%')
.height(60)
.padding(10)
.backgroundColor('#ff75d48a')
}
}

父组件

import CustomAndRefComChild from '../view/CustomAndRefComChild'

// 自定义组件与引用自定义组件
@Entry
@Component
struct CustomAndRefCom {
@Builder
bluetoothLeft() {
Image($r('sys.media.phone_arrow_down_left_circle_fill'))
.width(20)
}

@Builder
bluetoothRight() {
Image($r('sys.media.ohos_ic_public_arrow_right'))
.width(20)
.aspectRatio(1)
}

@Builder
developerLeft() {
Toggle({ type: ToggleType.Switch })
}

@Builder
developerRight() {
Checkbox()
.shape(CheckBoxShape.ROUNDED_SQUARE)
}

build() {
Column({ space: 30 }) {
CustomAndRefComChild({ titleContent: '文本' })
CustomAndRefComChild({
titleContent: '蓝牙',
left: () => {
//要传递一个自定义构建函数
this.bluetoothLeft()
},
right: () => {
this.bluetoothRight()
}
})
CustomAndRefComChild({
titleContent: '开发者模式',
left: () => {
this.developerLeft()
},
right: () => {
this.developerRight()
}
})
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.theme_color'))
}
}

案例:尾随闭包方式

尾随闭包引用自定义构建函数

子组件

// 如果引用自定义构建函数有且仅有一个的情况下。在父组件调用时候,不必每次都创建自定义构建函数进行传递。可以使用尾随闭包
@Component
export default struct CustomAndRefComChildExam {
titleContent: string = '内容'

@Builder
rightContent() {
Text('右边')
}

// Require 必传
@Require
@BuilderParam
right: () => void = this.rightContent

build() {
Row() {
Text(this.titleContent)
.layoutWeight(1)
this.right()
}
.width('100%')
.height(60)
.padding(10)
.backgroundColor('#ff75d48a')
}
}

父组件

import CustomAndRefComChildExam from '../view/CustomAndRefComChildExam'

@Entry
@Component
struct CustomAndRefExam {
build() {
Column({ space: 30 }) {
CustomAndRefComChildExam() {
// 引用构建函数 用来布局
Image($r('app.media.gege')).width(40)
}

CustomAndRefComChildExam({ titleContent: '蓝牙' }) {
Toggle({ type: ToggleType.Switch })
}

CustomAndRefComChildExam({ titleContent: '音量' }) {
Slider()
.layoutWeight(1)
}
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.theme_color'))
}
}

UIAbility组件的使用

  • 什么是UlAbility组件?

UIAbility组件是一种包含UI的应用组件,为应用提供绘制界面的窗口,主要用于和用户交互。每一个UIAbility组件实例都会在最近任务列表中显示一个对应的任务。

  • 设计理念:

    • 支持应用组件级的跨端迁移和多端协同。
    • 支持多设备和多窗口形态,
  • 划分原则:

    • UIAbility组件是系统调度的基本单元,一个应用可以包含一个或多个UIAbility组件。建议使用“一个UlAbility+多个页面的方式
  • 声明配置

​ 为使应用能够正常使用UIAbility,需要在module.json5配置文件的abilities标签中声明UIAbility的名称、入口、标签等相关信息。默认情况下已经配置了入口UIAbility

{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:notes",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:notes",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"ohos.want.action.home"
]
}
]
}
],
"extensionAbilities": [
{
"name": "EntryBackupAbility",
"srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
"type": "backup",
"exported": false,
"metadata": [
{
"name": "ohos.extension.backup",
"resource": "$profile:backup_config"
}
],
}
]
}
}
  • 启动模式

UIAbility的启动模式是指UIAbility实例在启动时的不同呈现状态,可以在module.json5文件中通过设置**launchType**字段进行设置。针对不同的业务场景,系统提供了三种启动模式:

  1. singleton(单实例模式)

    singleton启动模式为单实例模式,也是默认情况下的启动模式。每次调用时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例。

  2. multiton/standard(多实例模式)(以前叫standard)

​ 每次调用时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。

  1. specified(指定实例模式)

specified启动模式为指定实例模式,针对一些特殊场景使用:例如文档应用中每次新建文档希望都能新建一个文档实例,重复打 开一个已保存的文档希望打开的都是同一个文档实例。


  • 启动页面

    指定UIAbility的启动页面:应用中的UIAbility在启动过程中,需要指定启动页面,否则应用启动后会因为没有默认加载页面而导致白

    屏。可以在UlAbilityonWindowStageCreate中,通过WindowStage对象的loadContent方法设置启动页面

  • 上下文信息

​ 获取UIAbility的上下文信息:UIAbility类拥有自身的上下文信息,为UlAbilityContext类的实例,UIAbilityContext类拥有abilitylnfoCurrentHapModulelnfo等属性。

​ 通过UIAbilityContext可以获取UIAbility的相关配置信息,如包代码路径、Bundle名称、Ability名称和应用程序需要的环境

状态等属性信息,以及可以获取操作UlAbility实例的方法,如:startAbilityterminateSelfconnectServiceExtensionAbility

  • 获取UIAbility拉起方的信息

​ 拉起方(UlAbilityA)通过startAbility启动目标方(UIAbilityB)时,UIAbilityB可以通过parameters参数获取UlAbilityAPidBundleNameAbilityName等信息。


ArkUI的应用状态管理

@Local组件内部状态

  • 被@Local装饰的变量无法从外部初始化,因此必须在组件内部进行初始化。
  • 当被@Local装饰的变量变化时,会刷新使用该变量的组件。
  • @Local支持观测number、boolean、string、Object、class等基本类型以及ArraySetMapDate等内置类型。
  • @Local的观测能力仅限于被装饰的变量本身。
    • 当装饰简单类型时,能够观测到对变量的赋值;
    • 当装饰对象类型时,仅能观测到对对象整体的赋值;
    • 当装饰数组类型时,能观测到数组整体以及数组元素项的变化;
    • 当装饰Array、Set、Map、Date等内置类型时,可以观测到通过API调用带来的变化。详见观察变化
  • @Local支持null、undefined以及联合类型

@Param

@Param不仅可以接受组件外部输入,还可以接受@Local的同步变化。

@Param装饰的变量需要给初始化值。如果不给,则必须配合 @Require使用。此时父组件调用时必须传值。

@Param装饰的变量不允许在组件内部进行修改。如果想要修改,需要配合@Once 使用。以为父组件传递过来的数据只使用一次,以后我自己修改,跟父组件没关系。即,不能子到父同步数据。

如果想要子--->父同步数据,则需要使用@Event装饰一个函数。在父组件调用时,给该函数传递具体的业务,例如修改数据。此时可以做到子--->父数据同步。其实本质是父组件修改的变量数据同步给了子组件。相当于儿子想干的事情。在父亲那里完成了。

加载中...