组件事件
监听原生组件的事件和设置属性的方式是一样的都是链式调用,值得注意的是,我们注册事件必须使用箭头函数的写法,Next版本禁止使用匿名函数的形式来给组件注册事件
- 匿名函数 function () {}
- 尝试给一个TextInput和一个按钮注册一个值改变事件和点击事件
@Entry
@Component
struct Index {
build() {
Row() {
Column({ space: 15 }) {
Row() {
TextInput({ placeholder: '请输入用户名' })
.backgroundColor('#f4f5f6')
.width('100%').onChange((value) => {
console.log(value)
})
}.padding({
left: 20,
right: 20
})
Row() {
Button("登录")
.width('100%')
.onClick(() => {
AlertDialog.show({
message: '登录成功'
})
})
}.padding({
left: 20,
right: 20
})
}
.width('100%')
}
.height('100%')
}
}
- promptAction弹出需要引入一个包才可以使用的,功能更多一些
- AlertDialog 不需要引入包使用的,功能单一
请注意:在注册事件中的逻辑必须使用箭头函数 () => {}
- 因为function中this指向为undefind
- 箭头函数中的this指向当前struct实例,可以方便的调用方法和获取属性
给前几节课做的登录小案例加上事件
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct Index {
/**
* 手机号
*/
@State phone:string=''
/**
* 验证码
*/
@State code:string=''
/**
* 获取验证码
*/
getCode(){
// 判断手机号是否存在
if(this.phone===''){
promptAction.showToast({
message:'手机号不能为空'
})
return
}
promptAction.showToast({
message:'获取短信验证码成功,当前验证码是1234'
})
}
/**
* 登录
*/
login(){
if(this.phone!='' && this.code==='1234'){
promptAction.showToast({message:'登录成功'})
return
}
promptAction.showToast({message:'登录失败'})
// 恢复手机号跟验证码初始化的值
this.phone=''
this.code=''
}
build() {
Column() {
// 华为账号登录
Column({space:20}){
TextInput({placeholder:'请输入手机号',text:this.phone})
.width(300)
.borderRadius(2)
.onChange((value)=>{
this.phone=value
})
TextInput({placeholder:'请输入验证码',text:this.code})
.type(InputType.Password)
.width(300)
.borderRadius(2)
.onChange((value)=>{
this.code=value
})
Text('短信验证码登录').fontColor(Color.Blue).align(Alignment.Start).width('80%')
.margin({bottom:10})
.onClick(()=>{
this.getCode()
})
}
Button('登录')
.width(300)
.type(ButtonType.Normal)
.borderRadius(10)
.backgroundColor(Color.Red)
.onClick(()=>{
this.login()
})
}.justifyContent(FlexAlign.Center).width('100%').height('100%')
}
}
组件状态管理
在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个UI模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。
@State装饰器:组件内状态
@State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。
需要注意的是,State修饰的类型
Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型以及数组中的对象属性无法触发视图更新
类型必须被指定。
不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。
@Entry
@Component
struct Index {
@State message:string='hello world'
build() {
Column(){
Text(this.message).width(100).height(50).onClick(()=>{
this.message='hello state'
})
}.width('100%').height('100%')
}
}
@Entry
@Component
struct Index {
@State name:string='东林'
@State age:number=18
build() {
Column(){
Text(`${this.name}:${this.age}`)
.width(100)
.height(50)
.onClick(()=>{
this.age++
})
}.width('100%').height('100%')
}
}
class People{
name:string
age:number
constructor(name:string,age:number) {
this.name=name
this.age=age
}
}
@Entry
@Component
struct Index {
@State p:People=new People('东林',18)
build() {
Column(){
Text(`${this.p.name}:${this.p.age}`)
.width(100)
.height(50)
.onClick(()=>{
this.p.age++
})
}.width('100%').height('100%')
}
}
class People {
name: string
age: number
model:Model
constructor(name: string, age: number,model:Model) {
this.name = name
this.age = age
this.model=model
}
}
class Model{
like:string=''
constructor(like:string) {
this.like=like
}
}
@Entry
@Component
struct Index {
@State p: People = new People('东林', 18, new Model('篮球'))
build() {
Column() {
Text(`${this.p.model.like}`)
.width(100)
.height(50)
.onClick(() => {
this.p.model.like = '足球'
})
}.width('100%').height('100%')
}
}
@Prop装饰器:父子单向同步
@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。
允许装饰的变量的类型
Object、class、string、number、boolean、enum类型,以及这些类型的数组。
不支持any,支持undefined和null。
支持Date类型。
嵌套类型以及数组中的对象属性无法触发视图更新
@Entry
@Component
struct Index {
@State name: string = '父亲'
@State age: number = 0
build() {
Column() {
Text(`${this.name} : ${this.age}`)
.width(100)
.height(50)
.onClick(() => {
this.age = 18
}).backgroundColor(Color.Green)
Demo({ age: this.age })
}.width('100%').height('100%')
}
}
@Component
struct Demo {
@Prop age: number = 0
build() {
Column() {
Text(this.age.toString())
.width(100)
.height(50).backgroundColor(Color.Pink)
}.width('100%').height('100%')
}
}
@Link装饰器:父子双向同步
子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。
允许装饰的变量类型
Object、class、string、number、boolean、enum类型,以及这些类型的数组。
支持Date类型。嵌套类型以及数组中的对象属性无法触发视图更新
@Entry
@Component
struct Index {
@State name: string = '父亲'
@State age: number = 0
build() {
Column() {
Text(`${this.name} : ${this.age}`)
.width(100)
.height(50)
.onClick(() => {
this.age = 18
}).backgroundColor(Color.Green)
Demo({ age: this.age })
}.width('100%').height('100%')
}
}
@Component
struct Demo {
@Link age: number
build() {
Column() {
Text(this.age.toString())
.width(100)
.height(50).backgroundColor(Color.Pink).onClick(()=>{
this.age=20
})
}.width('100%').height('100%')
}
}
@Provide和@Consume装饰器:与后代组件双向同步
@Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。
@Provide/@Consume装饰的状态变量有以下特性:
- @Provide装饰的状态变量自动对其所有后代组件可用,即该变量被“provide”给他的后代组件。由此可见,@Provide的方便之处在于,开发者不需要多次在组件之间传递变量。
- 后代通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步,与@State/@Link不同的是,前者可以在多层级的父子组件之间传递。
- @Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,建议类型相同,否则会发生类型隐式转换,从而导致应用行为异常。
允许装饰的变量类型
Object、class、string、number、boolean、enum类型,以及这些类型的数组。
支持Date类型。
嵌套类型以及数组中的对象属性无法触发视图更新
@Entry
@Component
struct Index {
@State name: string = '父亲'
@Provide age: number = 0
build() {
Column() {
Text(`${this.name} : ${this.age}`)
.width(100)
.height(50)
.onClick(() => {
this.age = 18
}).backgroundColor(Color.Green)
Demo()
}.width('100%').height('100%')
}
}
@Component
struct Demo {
@Consume age: number
build() {
Column() {
Text(this.age.toString())
.width(100)
.height(50).backgroundColor(Color.Pink).onClick(()=>{
this.age=20
})
}.width('100%').height('100%')
}
}
@Observed和@ObjectLink装饰器:嵌套类对象属性变化
上文所述的装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。
@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:
- 被@Observed装饰的类,可以被观察到属性的变化;
- 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。
- @Observed用于嵌套类场景中,观察对象类属性变化,要配合自定义组件使用(示例详见嵌套对象),如果要做数据双/单向同步,需要搭配@ObjectLink或者@Prop使用(示例详见@Prop与@ObjectLink的差异)。
允许装饰的变量类型
必须为被@Observed装饰的class实例,必须指定类型。
不支持简单类型,可以使用@Prop。
支持继承Date、Array的class实例
@Observed
class People {
name: string
age: number
model: Model
constructor(name: string, age: number, model: Model) {
this.name = name
this.age = age
this.model = model
}
}
@Observed
class Model {
like: string = ''
constructor(like: string) {
this.like = like
}
}
@Entry
@Component
struct Index {
@State p: People = new People('东林', 18, new Model('篮球'))
build() {
Column() {
child({ m: this.p.model }).onClick(() => {
this.p.model.like = '足球'
})
}.width('100%').height('100%')
}
}
@Component
struct child {
@ObjectLink m: Model
build() {
Column() {
Text(`${this.m.like}`)
.width(100)
.height(50)
}
}
}