装饰器,状态管理和if判断(HarmonyOS学习第六课)

@Builder装饰器-自定义构建函数

前面介绍了如何创建一个自定义组件。该自定义组件内部UI结构固定,仅与使方法进行数据传递。ArkUI还提供了一种更轻量的UI 元素复用机制@Builder,@Builder

所装饰的函数遵循build( )函数语法规则,开发者可以将重复使用的UI 元素抽象成一个方法,在 build 方法里调用。 为了简化语言,我们将@Builder 装饰的函数也称为"自定义构建函数"。从API version 9开始,该装饰器支持在ArkTS卡片中使用。

装饰器使用说明:

语法的定义

TypeScript 复制代码
@Builder MyBuilderFunction( ){...}

使用方法

TypeScript 复制代码
this.MyBuilderFunction(){...}
  • 允许在自定义组件内定义一个或多个@Builder 方法,该方法被认为是该组件的私有、特殊类型的成员函数。

  • 自定义构建函数可以在所属组件的build 方法和其他自定义构建函数中调用,但不允许在组件外调用。

  • 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递

全局自定义构建函数

语法的定义

TypeScript 复制代码
@BUilder function MyGlobalBuilderFunction(){...}

使用方法

TypeScript 复制代码
MyGlobalBuilderFunction()
  • 全局的自定义构建函数可以被整个应用获取,不允许使用this和bind方法。

  • 如果不涉及组件状态变化,建议使用全局的自定义构建方法

参数传递规则

自定义构建函数的参数传递有按值传递和按引用传递两种,均需遵守以下规则:

参数的类型必须与参数声明的类型一致,不允许undefined、nu1l和返回undefined、null的表达式

引用传参(最常用)

TypeScript 复制代码
//自定义一个构建函数
//引用传参使用$$符号
@Builder
function  MyBulilder($$:{username:string}){
​
//用的时候也要使用$$
  Column(){
    Text(`hello ${$$.username}`)
      .fontSize(40)
      .margin(20)
  }
}
​
@Entry
@Component
​
struct  Parent{
​
  @State person_name: string='张三'
​
  build(){
    Column(){
      Divider()
      MyBulilder({username: this.person_name})
      Button('改变值').onClick(()=>{
        this.person_name ='李四'
      })
    }
  }
}

值传参(了解 并不实用)

TypeScript 复制代码
//自定义一个构建函数
//按值传参
@Builder
function  MyBulilder(username:string){
​
​
  Column(){
    Text(`hello ${username}`)
      .fontSize(40)
      .margin(20)
  }
}
​
@Entry
@Component
​
struct  Parent{
​
  @State person_name: string='张三'
​
  build(){
    Column(){
      Divider()
      MyBulilder(this.person_name)
      Button('改变值').onClick(()=>{
        this.person_name ='李四'
      })
    }
  }
}

状态管理

状态管理概述

在前文的描述中,我们构建的页面多为静态界面。如果希望构建一个动态的、有交互的界面,就需要引入"状态"的概念

在本章节开始的案例中,用户与应用程序的交互触发了文本状态变更,状态变更引起了UI 渲染,UI 从"Hello world"变更为"Hello rkUI",这个过程就用到了状态。

在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个U模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。

自定义组件拥有变量,变量必须被装饰器装饰才可以成为状态变量,状态变量的改变会引起UI的渲染刷新。如果不使用状态变量,UI只能在初始化时渲染,后续将不会再刷新。下图展示了state和iew(UI)之间的关系

  • View(UI):UI 渲染,指将build 方法内的 UI 描述和@Builder 装饰的方法内的UI 描述映射到界面。

  • state:状态,指驱动U更新的数据。用户通过触发组件的事件方法,改变状态数据。状态数据的改变,引起UI的重新渲染。

基本概念

  • 状态变量: 被状态装饰器装饰的变量,状态变量值的改变会引起UI的染更新。示例:@State num: number=1,其中,@State是状态装饰器,num是状态变量。

  • **常规变量:**没有被状态装饰器装饰的变量,通常应用于辅助计算。它的改变永远不会引起UI的刷新。以下示例中increaseBy 变量为常规变量。

  • 数据源/同步源:状态变量的原始来源,可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。以下示例中数据源为count:1。

  • 命名参数机制:父组件通过指定参数传递给子组件的状态变量,为父子传递同步参数的主要手段。示例:CompA:({aPrp:this.aProp})。

  • **从父组件初始化:**父组件使用命名参数机制,将指定参数传递给子组件。子组件初始化的默认值在有父组件传值的情况下,会被覆盖。

  • **初始化子节点:**父组件中状态变量可以传递给子组件,初始化子组件对应的状态变量。

  • 本地初始化:在变量声明的时候赋值,作为变量的默认值。示例:@statecount:number =0

管理组件拥有的状态

@State装饰器-组件内状态

@state 装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。

在状态变量相关装饰器中,@state是最基础的,使变量拥有状态属性的装饰器,它也是大部分状态变量的数据源。从Pversion9开始,该装饰器支持在ArkTs 卡片中使用。

概述

@State装饰的变量,与声明式范式中的其他被装价变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。

装饰器使用说明

使用场景

装饰简单类型的变量 以下示例为@state装饰的简单类型,count被@state装饰成为状态变量,count的改变引起Button组件的刷新:

  1. 当状态变量count改变时,查询到只有Button组件关联了它;

  2. 执行Button组件的更新方法,实现按需刷新。

示例

点击修改姓名将会在张三,李四之间进行切换

点击修改年龄将会按照你设定的规则进行年龄的加减

当子组件上传参数的时候将使用子组件的参数

当没有的时候默认是父组件的数据

@Prop装饰器-父子单项同步

@Prop 装饰的变量可以和父组性建立单向的同步关系。@Prop装饰的变量是可变但是变化不会同步回其父组件。从API version 9开始,该装饰器支持在rkTS 卡中使用。

概述

  • @Prop 装饰的变量和父组件建立单向的同步关系:

  • @Prop变量允许在本地修改,但修改后的变化不会同步回父组件

  • 当父组件中的数据源更改时,与之相关的@Prop 装饰的变量都会自动更新。如果子组件已经在本地修改了@Prop 装饰的相关变量值,而在父组件中对应的@state装饰的变量被修改后,子组件本地修改的@Prop装饰的相关变量值将被覆盖

限制条件

  • @Prop 修饰复杂类型时是深拷贝,在拷贝的过程中除了基本类型、Map、Set、Date、Array 外,都会丢失类型。

  • @Prop装饰器不能在@Entry装饰的自定义组件中使用

装饰器使用规则说明

使用场景:

父组件@State 到子组件@Prop简单数据类型同步

以下示例是@state 到子组件@Prop 简单数据同步,父组件ParentComponent的状态变量 countDownstartValue 初始化子组件 CountDowncomponent 中eprop 装饰的 count,点击"Try again",,count 的修改仅保留在 CountDownComponent不会同步给父组件ParentComponent。

ParentComponent的状态变量countDownstartValue 的变化将重冒CountDownComponent的count。

代码:

TypeScript 复制代码
@Component
struct MyChid{
  @Prop
  age: number  =33
  private increase: number=1 //私有
  build(){
    Column(){
      if (this.age>=18){
        Text(`已经age成年了:${this.age}`).height(80)
      }else
      {
        Text(`age未成年了:${this.age}`).height(80)
      }
      Button('-修改子组件age').onClick(()=>{
        this.age-=this.increase
      })
        .height(80)
        .width(250)
        .margin(5)
    }
  }
}
​
@Entry
@Component
struct Myparent {
  @State
  init_age: number =16
​
  build(){
    Column(){
      Text(`父组件初始值:${this.init_age}`).height(80)
​
      Button('+修改父组件age').onClick(()=>{
        this.init_age+=1
      })
        .height(80)
        .margin(5)
        .width(250)
​
      Divider()
      MyChid({age:this.init_age,increase:2})
    }
  }
}

@Link装饰器-父子双向同步

子组件中被@Link 装饰的变量与其父组件中对应的数据源建立双向数据绑定。从APIversion 9开始,该装饰器支持在ArkTS卡片中使用。

需要注意:@Link 装饰的变量与其父组件中的数据源共享相同的值。@Link 装饰器不能在@Entry 装饰的自定义组件中使用。

代码示例展示:

复制代码
TypeScript 复制代码
//自定义按钮的信息类型
class ButtonState{
  value:string;
  width:number=0;
​
  constructor(value:string, width:number){
    this.value = value;
    this.width = width;
  }
}
​
​
@Component
​
struct MyChildGreenButton{
  //拥有 绿色按钮的组件,Link装饰器 实现双向同步
  @Link
  buttonState:ButtonState   //自定义对象类型
​
  build(){
      Button(`${this.buttonState.value}`)
        .width(this.buttonState.width)
        .height(150)
        .backgroundColor(Color.Green)
        .onClick(()=>{
          //点击按钮  实现宽度的变化
          if(this.buttonState.width<700){
            this.buttonState.width+=100
          }else {
            //按钮宽度回到初始值
            this.buttonState = new ButtonState('绿色按钮',100)
          }
        })
  }
}
​
​
@Component
struct MyChildRedButton{
  //拥有 红色按钮的组件,Link装饰器 实现双向同步
  @Link
  value: string
  @Link
  buttomWidth: number;
​
  build(){
    Button(`${this.value}`)
      .width(this.buttomWidth)
      .height(150)
      .backgroundColor(Color.Red)
      .onClick(()=>{
        //点击按钮  实现宽度的变化
        if(this.buttomWidth<700){
          this.buttomWidth+=100
        }else {
          //按钮宽度回到初始值
          this.buttomWidth = 100
        }
      })
  }
}
​
@Entry
@Component
struct MYParent{
  @State parentGreenButton:ButtonState=new ButtonState('一号',100)  //状态变量
  @State parentRedValue: string = '二号子组件'  //状态变量
  @State parentRedWidth: number = 200   //状态变量  //绿色按钮的宽度
​
  build(){
     Column(){
       //父组件中调整按钮宽度
       Button(`父组件中修改绿色按钮的宽度:${this.parentGreenButton.width}`)
         .onClick(()=>{
           this.parentGreenButton.width = this.parentGreenButton.width < 700 ? this.parentGreenButton.width+100:100
         })
​
       Button(`父组件中修改红色按钮的宽度:${this.parentRedWidth}`)
         .onClick(()=>{
           this.parentRedWidth = this.parentRedWidth < 700 ? this.parentRedWidth+100:100
         })
​
       Divider()
​
       MyChildGreenButton({buttonState:$parentGreenButton})  //传递Link装饰器的变量时候加 $ 符号
       MyChildRedButton({value:$parentRedValue,buttomWidth:$parentRedWidth})
     }
  }
}

if/else 条件渲染

ArkTS 提供了渲染控制的能力。条件渲染可根据应用的不同状态,使用if、else 和else if 渲染对应状态下的UI内容。从API version 9开始,该接口支持在 ArkTS卡片中使用。

使用规则

  • 支持if、else和else if 语句。

  • if、else if 后跟随的条件语句可以使用状态变量

  • 允许在容器组件内使用,通过条件渲染语句构建不同的子组件。

  • 条件渲染语句在涉及到组件的父子关系时是"透明"的,当父组件和子组件之间存在一个或多个if语句时,必须遵守父组件关于子组件使用的规则。

  • "每个分支内部的构建函数必须遵循构建函数的规则,并创建一个或多个组件。无法创建组件的空构建函数会产生语法错误。

  • 某些容器组件限制子组件的类型或数量,将条件渲染语句用于这些组件内时,这些限制将同样应用于条件渲染语句内创建的组件。例如,Grid容器组件的子组件仅支持GridItem 组件,在Grid 内使用条件渲染语句时,条件渲染语句内仅允许使用GridItem组件

更新机制

当if、else if 后跟随的状态判断中使用的状态变量值变化时,条件渲染语句会进行更新,更新步骤如下:

  1. 评估if和else if的状态判断条件,如果分支没有变化,请无需执行以下步骤。如果分支有变化,则执行2、3步骤:

  2. 删除此前构建的所有子组件。

  3. 执行新分支的构造函数,将获取到的组件添加到if 父容器中。如果缺少适用的 else分支,则不构建任何内容。

    条件可以包括 Typescript 表达式。对于构造函数中的表达式,此类表达式不得更改应用程序状态。

使用if进行条件渲染

if 语句的每个分支都包含一个构建函数。此类构建函数必须创建一个或多个子组件。在初始渲染时,if语句会执行构建函数,并将生成的子组件添加到其父组件中。

"每当if或else if条件语句中使用的状态变量发生变化时,条件语句都会更新并重新评估新的条件值。如果条件值评估发生了变化,这意味着需要构建另一个条件分支。此时ArkUI 框架将:

  1. 删除所有以前渲染的(早期分支的)组件。

  2. 执行新分支的构造函数,将生成的子组件添加到其父组件中

代码演示

分为两种 :第一种 点击"是否真假的按钮的时候" 计数器的值会归零

TypeScript 复制代码
//定义子组件
@Component
struct MyChild{
​
  //计数器
  @State   counter:number=0
  label: string;
​
  build(){
    Row(){
      Text(`${this.label}`)
        .width(100)
        .height(100)
        .fontSize(20)
​
​
      Button(`计数器的值:${this.counter}`)
        .width(200)
        .height(60)
        .onClick(()=>{
           this.counter += 1;
        })
    }
  }
}
​
@Entry
@Component
struct MyParent{
  @State flag:boolean=false;
  build(){
    Column(){
      //根据判断决定子组件
      if(this.flag){
        MyChild({label:'zhenzhen'})
      }else {
        MyChild({label:'jiajia'})
      }
      Divider()
      Button(`是否真假:${this.flag}`)
        .width(300)
        .height(60)
        .fontSize(30)
        .margin(40)
        .onClick(()=>{
          this.flag=!this.flag
        })
    }
  }
}

第二种 点击是否真假的时候计数器的值不会进行更新归零

TypeScript 复制代码
//定义子组件
@Component
struct MyChild{
​
  //计数器
  @Link counter:number;
  label: string;
​
  build(){
    Row(){
      Text(`${this.label}`)
        .width(100)
        .height(100)
        .fontSize(20)
​
​
      Button(`计数器的值:${this.counter}`)
        .width(200)
        .height(60)
        .onClick(()=>{
           this.counter += 1;
        })
    }
  }
}
​
​
@Entry
@Component
struct MyParent{
  @State flag:boolean=false;
  @State parentCount: number=0
  build(){
    Column(){
      //根据判断决定子组件
      if(this.flag){
        MyChild({counter:$parentCount,label:'zhenzhen'})
      }else {
        MyChild({counter:$parentCount,label:'jiajia'})
      }
      Divider()
      Button(`是否真假:${this.flag}`)
        .width(300)
        .height(60)
        .fontSize(30)
        .margin(40)
        .onClick(()=>{
          this.flag=!this.flag
        })
    }
  }
}
相关推荐
dayouziei1 小时前
java的类加载机制的学习
java·学习
SoraLuna4 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
dsywws5 小时前
Linux学习笔记之vim入门
linux·笔记·学习
ClkLog-开源埋点用户分析5 小时前
ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力
华为·开源·开源软件·harmonyos
mg6685 小时前
鸿蒙系统的优势 开发 环境搭建 开发小示例
华为·harmonyos
模拟IC攻城狮5 小时前
华为海思招聘-芯片与器件设计工程师-模拟芯片方向- 机试题-真题套题题目——共8套(每套四十题)
嵌入式硬件·华为·硬件架构·芯片
孙同学要努力5 小时前
全连接神经网络案例——手写数字识别
人工智能·深度学习·神经网络
lqj_本人5 小时前
鸿蒙next选择 Flutter 开发跨平台应用的原因
flutter·华为·harmonyos
lqj_本人6 小时前
使用 Flutter 绘制一个棋盘
harmonyos
晨曦_子画6 小时前
3种最难学习和最容易学习的 3 种编程语言
学习