自学内容网 自学内容网

ArkTS中的自定义构建函数、Tab栏和组件状态共享

一、自定义构建函数

1.构建函数 @Builder

1.1 介绍

文档地址:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-builder-V5?catalogVersion=V5

概念:ArkUI提供了一种轻量的UI元素复用机制@Builder,可以将重复使用的UI元素抽象成一个方法。在builder里调用。

1.2 定义的位置

组件内定义
@Builder  MyBuilderFcuntion(){
    
}

//调用
this.MyBuilderFcuntion()
全局定义
@Builder function  MyBuilderFcuntion(){
    
}

//调用
MyBuilderFcuntion()
案例

在这里插入图片描述

鸿蒙页面布局,此处我们会用到栅格布局:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-layout-development-grid-layout-V5

待实现的页面结构
@Entry
@Component
struct BuilderDemo1Page {

  build() {
    Column(){ //垂直方向布局
      //columns:分为几列,columns默认值为12
      //gutter:设置子元素在水平和垂直方向的间距。
      GridRow({columns:3,gutter:15}){
        GridCol(){
          Column(){
            Row(){
              Text('评价(200+)')
                .layoutWeight(1)
                //.fontWeight(FontWeight.Bold)
                .fontWeight(600)
            }
            Row(){

            }
            .height(100)
          }
          .backgroundColor('#fff')
          .borderRadius(12)
          .padding(10)
        }

        GridCol(){
          Column(){
            Row(){
              Text('推荐')
                .layoutWeight(1)
                  //.fontWeight(FontWeight.Bold)
                .fontWeight(600)
            }
            Row(){

            }
            .height(100)
          }
          .backgroundColor('#fff')
          .borderRadius(12)
          .padding(10)
        }

        GridCol(){
          Column(){
            Row(){
              Text('体验')
                .layoutWeight(1)
                  //.fontWeight(FontWeight.Bold)
                .fontWeight(600)
            }
            Row(){

            }
            .height(100)
          }
          .backgroundColor('#fff')
          .borderRadius(12)
          .padding(10)
        }

      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
    .padding(15)
  }
}
使用@Builder提取UI结构
@Entry
@Component
struct BuilderDemo1Page {

  @Builder
  MyBuilder(){
    Row(){
      Text('查看更多')
      Image($r('app.media.chevron_right'))
        .width(16)
        .aspectRatio(1)
    }
  }

  build() {
    Column(){ //垂直方向布局
      //columns:分为几列,columns默认值为12
      //gutter:设置子元素在水平和垂直方向的间距。
      GridRow({columns:2,gutter:15}){
        GridCol({span:2}){  //span:将几个合并成一个
          Column(){
            Row(){
              Text('评价(200+)')
                .layoutWeight(1)
                //.fontWeight(FontWeight.Bold)
                .fontWeight(600)
              // 查看更多
              this.MyBuilder()
            }
            Row(){

            }
            .height(100)
          }
          .backgroundColor('#fff')
          .borderRadius(12)
          .padding(10)
        }

        GridCol(){
          Column(){
            Row(){
              Text('推荐')
                .layoutWeight(1)
                  //.fontWeight(FontWeight.Bold)
                .fontWeight(600)
              // 查看更多
              this.MyBuilder()
            }
            Row(){

            }
            .height(100)
          }
          .backgroundColor('#fff')
          .borderRadius(12)
          .padding(10)
        }

        GridCol(){
          Column(){
            Row(){
              Text('体验')
                .layoutWeight(1)
                  //.fontWeight(FontWeight.Bold)
                .fontWeight(600)

              // 查看更多
              this.MyBuilder()
            }
            Row(){

            }
            .height(100)
          }
          .backgroundColor('#fff')
          .borderRadius(12)
          .padding(10)
        }

      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
    .padding(15)
  }
}
小结

遇到非遍历情况下,一个组件分散着相同的ui结构,可以使用@Builder加上更轻量级的GridRow和GirdCol栅格布局

1.3 参数传递

场景:构建不同的UI

@Builder  MyBuilderFcuntion(title:string){
    
}

//调用
this.MyBuilderFcuntion('Title')

在这里插入图片描述

//两个位置变动
@Builder
MyBuilder(title:string){
    Row(){
        Text(title)
        Image($r('app.media.chevron_right'))
            .width(16)
            .aspectRatio(1)
    }
}
// 查看更多
this.MyBuilder('好频率98%')
this.MyBuilder('查看全部')
this.MyBuilder('4条测评')

1.4 引用传递

场景:当传递的数据更新,需要更新ui

需求:

点击按钮后,模拟加载好评率数据

在这里插入图片描述

import promptAction from '@ohos.promptAction';
class Params{
  title:string = ''
}
@Entry
@Component
struct BuilderDemo1Page {
  @State rate:number = 0;
  @Builder
  MyBuilder(params:Params){
    Row(){
      Text(params.title)
      Image($r('app.media.chevron_right'))
        .width(16)
        .aspectRatio(1)
    }
  }

  build() {
    Column(){ //垂直方向布局
      Button('获取数据')
        .onClick( () => {
            this.rate = 98;
            promptAction.showToast({message:this.rate.toString()})
        })

      //columns:分为几列,columns默认值为12
      //gutter:设置子元素在水平和垂直方向的间距。
      GridRow({columns:2,gutter:15}){
        GridCol({span:2}){  //span:将几个合并成一个
          Column(){
            Row(){
              Text('评价(200+)')
                .layoutWeight(1)
                //.fontWeight(FontWeight.Bold)
                .fontWeight(600)
              // 查看更多
              this.MyBuilder({title:`好频率${this.rate}%`})
            }
            Row(){

            }
            .height(100)
          }
        }
        .backgroundColor('#fff')
        .borderRadius(12)
        .padding(10)

        GridCol(){
          Column(){
            Row(){
              Text('推荐')
                .layoutWeight(1)
                  //.fontWeight(FontWeight.Bold)
                .fontWeight(600)
              // 查看更多
              this.MyBuilder({title:'查看全部'})
            }
            Row(){

            }
            .height(100)
          }
        }
        .backgroundColor('#fff')
        .borderRadius(12)
        .padding(10)

        GridCol(){
          Column(){
            Row(){
              Text('体验')
                .layoutWeight(1)
                  //.fontWeight(FontWeight.Bold)
                .fontWeight(600)

              // 查看更多
              this.MyBuilder({title:'4条测评'})
            }
            Row(){

            }
            .height(100)
          }
        }
        .backgroundColor('#fff')
        .borderRadius(12)
        .padding(10)

      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
    .padding(15)
  }
}

使用Builder复用逻辑的时候,支持参数可以更灵活渲染UI

参数可以使用状态数据,不过建议通过对象的方式传递@Builder

2.构建函数 @BuilderParam

@BuilderParam该装饰器用于声明任意UI描述一个元素,类似于slot占位符

前置知识:
组件属性初始化
1.定义组件声明属性: title:string
2.使用组件的初始化属性:Comp({title:string})
  • 尾随闭包初始化组件
    • 组件内有且仅有一个@BuilderParam装饰器的属性
  • 参数初始化组件
    • 组件内有多个@BuilderParam装饰器属性

2.1 尾随闭包初始化组件

需求:

  • 标题文件和更多文件通过属性传入
  • 内容结构需要传入

在这里插入图片描述

@Entry
@Component
struct BuilderParamPage {

  build() {
    Column(){
      GridRow({columns:2,gutter:15}){
        GridCol({span:2}){
          PanelComp({title:'评价(200+)',more:'好评率98%'});
        }
        GridCol(){
          PanelComp({title:'推荐',more:'查看全部'}){
            Text('推荐内容')
          };
        }
        GridCol(){
          PanelComp({title:'体验',more:'4 条测评'}){
            Text('体验内容')
          };
        }
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
    .padding(15)
  }
}

@Component
struct PanelComp {
  title:string = ''
  more:string = ''

  @Builder
  DefaultPanelContent(){
    Text('默认内容')
  }

  @BuilderParam
  panelContent: () => void = this.DefaultPanelContent

  build() {
    Column(){
      Row(){
        Text(this.title)
          .layoutWeight(1)
          .fontWeight(600)
        Row(){
          Text(this.more)
            .fontSize(14)
            .fontColor('#666')
          Image($r('app.media.chevron_right'))
            .width(16)
            .aspectRatio(1)
            .fillColor('#666')
        }
      }
      Row(){
        this.panelContent();
      }
      .height(100)
    }
    .backgroundColor('#fff')
    .padding(10)
    .borderRadius(12)

  }
}

2.2 参数初始化组件 (具名插槽)

需求:

​ 需要传入内容结构和底部结构

在这里插入图片描述

@Entry
@Component
struct BuilderParam2Page {

  @Builder
  ContentBuilderA(){
    Text('评论内容1111')
  }
  @Builder
  ContentBuilderB(){
    Text('评论底部')
  }
  build() {
    Column(){
      GridRow({columns:2,gutter:15}){
        GridCol({span:2}){
          PanelComp2({
            title:'评价(2000+)',
            more:'好评率98%',
            panelContent: this.ContentBuilderA,
            panelFooter:this.ContentBuilderB
          })
        }
      }
    }
    .width('100%')
    .height('100%')
    .padding(15)
    .backgroundColor('#f5f5f5')
  }
}

@Component
struct PanelComp2 {
  title:string = '';
  more:string = '';
  @Builder
  DefaultPanelContent(){
    Text('默认内容')
  }
  @BuilderParam
  panelContent: () => void = this.DefaultPanelContent
  @BuilderParam
  panelFooter: () => void = this.DefaultPanelContent

  build() {
    Column(){
      Row(){
        Text(this.title)
          .layoutWeight(1)
          .fontWeight(600)
          .fontSize(14)
          .fontColor('#666')
        Row(){
          Text(this.more)
          Image($r('app.media.chevron_right'))
            .width(16)
            .aspectRatio(1)
            .fillColor('#666')
        }
      }
      Row(){
        this.panelContent()
      }
      .height(100)
      Row(){
        this.panelFooter()
      }
      .height(100)
    }
    .backgroundColor('#fff')
    .padding(10)
    .borderRadius(16)


  }
}

3.总结

  • 当子组件使用一个@BuilderParam的时候,使用组件的时候尾随 {},插入UI结构
  • 当子组件有多个@BuilderParam的时候,使用组件的时候Comp({xxx:xxxxx})传入
  • 子组件本身提供一个默认的@Builder函数作为@BuilderParam备用函数,当作准用内容使用(默认内容)

4.系统组件自定义UI

在一些系统组件中,根据配置无法达到预期的UI,就可以使用@Builder构建函数自定义UI,前提是该组件要支持自定义UI

Tabs:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-navigation-tabs-V5

4.1 基本Tab结构

@Entry
@Component
struct TabsPage {

  build() {
    Column(){
      //barPosition:控制显示的位置
      Tabs({barPosition:BarPosition.End}){
        TabContent(){
          Text('首页')
        }.tabBar('首页')
        TabContent(){
          Text('项目')
        }
        .tabBar('项目')
        TabContent(){
          Text('面试')
        }.tabBar('面试')
        TabContent() {
          Text('我的')
        }.tabBar('我的')
      }
    }
    .width('100%')
    .height('100%')
  }
}

4.2 自定义Tab结构

在这里插入图片描述

import promptAction from '@ohos.promptAction'

@Entry
@Component
struct TabsPage {
  @State activeIndex:number = 0; //默认激活的是第一个索引
  //定义个对象数组,用来存储图标相关信息
  toolBarItems:ToolBarItem[] = [
    {defaultIcon:$r('app.media.home'),activeIcon:$r('app.media.home_select'),label:'首页'},
    {defaultIcon:$r('app.media.project'),activeIcon:$r('app.media.project_select'),label:'项目'},
    {defaultIcon:$r('app.media.interview'),activeIcon:$r('app.media.interview_select'),label:'面试'},
    {defaultIcon:$r('app.media.mine'),activeIcon:$r('app.media.mine_select'),label:'我的'},
  ]
  @Builder
  TabBarBuilder(item:ToolBarItem,index:number){
    Column(){
      Image(this.activeIndex === index?item.activeIcon:item.defaultIcon)
        .width(24)
        .aspectRatio(1)
      Text(item.label)
        .lineHeight(12)
        .margin({top:4})
        .fontColor(this.activeIndex === index?'#000':'#aaa')
    }
  }
  build() {
    Column(){
      //barPosition:控制显示的位置
      Tabs({barPosition:BarPosition.End}){
        //循环遍历
        ForEach(this.toolBarItems,(item:ToolBarItem,index:number)=>{
          TabContent(){
            Text(item.label)
          }.tabBar(this.TabBarBuilder(item,index))

        })

      }
      .onChange( (index:number) => {
        //获取到点击的索引
        // promptAction.showToast({message:index.toString()})
        this.activeIndex = index;//获取到当前激活的索引
      })
    }
    .width('100%')
    .height('100%')
  }
}

//类:描述ToolBar
class ToolBarItem{
  defaultIcon:string | Resource = '' //默认图标
  activeIcon:string | Resource = ''  //激活图标
  label: string = ''                //文字
}

二、组件状态共享

1.父子单向

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

@Prop装饰的变量是可变的,但是变化不同同步回到父组件

@Entry
@Component
struct PropPage {
  @State money:number = 0;
  build() {
    Column({space:20}){
      Text('父组件:' + this.money)
        .fontSize(20)
        .onClick(() => {
          this.money++
        })

      //引入子组件
      Child1({money: this.money});
    }
  }
}

@Component
struct Child1 {
  //@Prop是父子单向
  @Prop money:number;
  // @Prop money:number = 10;
  build() {
    Text('子组件:' + this.money)
      .fontSize(20)
      .onClick(() => {
        this.money++
      })
  }
}

支持的类型:

  • string
  • number
  • boolean
  • enum

子组件可以修改Prop的数据值,父组件更新后覆盖子组件Prop的数据

子组件可以有初始化的默认值,注意:目前的编译器是已经修复bug,不会报错

2.父子双向

子组件中用@Link装饰的变量与父组件中对应的数据建立双向数据绑定

2.1 简单类型

string、number、boolean、enum

@Entry
@Component
struct Link1Page {
  @State money:number = 0;
  build() {
    Column({space:20}){
      Text('父组件:' + this.money)
        .fontSize(20)
        .onClick(() => {
          this.money++
        })

      //引入子组件
      Child2({money: this.money});
    }
  }
}

@Component
struct Child2 {
  //@Link父子双向
  @Link money:number;
  build() {
    Text('子组件:' + this.money)
      .fontSize(20)
      .onClick(() => {
        this.money++
      })
  }
}

2.2 复杂类型

object、class

@Entry
@Component
struct Link2Page {
  @State person:Person = {name:'kunkun',age:26};
  build() {
    Column({space:20}){
      Text(`父组件: + ${this.person.name},今年${this.person.age}岁了`)
        .fontSize(20)
        .onClick(() => {
          this.person.age++
        })

      //引入子组件
      Child3({person:$person});
      // Child3({person:this.person});
    }
  }
}

@Component
struct Child3 {
  //@Link父子双向
  @Link person:Person;
  build() {
    Text(`子组件: + ${this.person.name},今年${this.person.age}岁了`)
      .fontSize(20)
      .onClick(() => {
        this.person.age++
      })
  }
}
//自定义类型
class Person{
  name:string = ''
  age:number = 0
}

父组件传值的时候,需要this. 改写成$

子组件@Link装饰数据

3.后代组件

@Provideconsume ,应用于与后代组件的双向数据同步,应用于状态的数据的多个层级之间传递场景

3.1 通过相同的变量名绑定

必须保证变量名是相同

@Entry
@Component
struct ProvidePage {
  @Provide money:number = 0;

  build() {
    Column({space:25}){
      Text('父组件:' + this.money)
        .onClick(() => {
          this.money++
        })
      Child4()
    }
  }
}

@Component
struct Child4 {
  //不可以给初始值
  @Consume money:number;
  build() {
    Text('子组件:' + this.money)
      .onClick(() => {
        this.money++
      })
  }
}

Tip ⏲

  • Object、class、string、number、boolean、enum类型都支持
  • 通过相同的变量名绑定@Provide@Consume的变量名一致

3.2 状态监听

如果开发者需要关注某个变量的值是否发生变化,可以使用@watch为状态变量设置回调函数

@State 、@Prop 和@Link等装饰器在@Watch装饰之前

import promptAction from '@ohos.promptAction'

@Entry
@Component
struct WatchPage {
  @State activeIndex:number = 0;

  build() {
    Column(){

      Button('按钮1')
        .onClick(()=>{
          this.activeIndex = 1;
          promptAction.showToast({message:this.activeIndex.toString()})
        })
      Button('按钮2')
        .onClick(()=>{
          this.activeIndex = 2;
          promptAction.showToast({message:this.activeIndex.toString()})
        })


      Child5({activeIndex:this.activeIndex});
    }
  }
}


@Component
struct Child5 {
  @Prop @Watch('onActionIndex') activeIndex:number = 0

  onActionIndex(){
    promptAction.showToast({message:'监听到了变化 -->' + this.activeIndex})
  }

  build() {
    Text('Child5---->'  + this.activeIndex)
  }
}

TIP:

在第一次初始化的时候,@Watch装饰器的方法是不会被调用的

3.2 @Observed 与@ObjectLink

之前我们通过 赋值的方式 修改嵌套对象或对象数组这类复杂数据来更新UI的

使用步骤:

  • 类class数据模拟需要定义通过构造函数,使用@Observed修改这个类
  • 初始化数据:需要通过初始化构造方式添加
  • 通过@ObjectLink关联对象,可以直接修改被关联对象来更新UI

在这里插入图片描述

//创建接口
interface ReplyItemInterface{
  id?:number
  avatar?:string | Resource
  author?:string
  content?:string
  time?:string
  area?:string
  linkNum?:number
  likeFlag?:boolean
}
@Observed
export class ReplyItem{
  id?:number
  avatar?:string | Resource
  author?:string
  content?:string
  time?:string
  area?:string
  linkNum?:number
  likeFlag?:boolean

  //构造函数中要求使用接口类型
  constructor(item:ReplyItemInterface) {
    this.id = item.id
    this.avatar = item.avatar
    this.author = item.author
    this.content = item.content
    this.time = item.time
    this.area = item.area
    this.linkNum = item.linkNum
    this.likeFlag = item.likeFlag
  }
}
//创建对象数组
export const replyList:ReplyItem[] = [
  new ReplyItem({id:1,avatar:$r('app.media.kun'),author:'金庸',content:'倚天屠龙记',time:'16:54',area:'武汉',linkNum:30,likeFlag:true}),
  new ReplyItem({id:1,avatar:$r('app.media.kun'),author:'金庸',content:'倚天屠龙记',time:'16:54',area:'武汉',linkNum:30,likeFlag:true}),
  new ReplyItem({id:1,avatar:$r('app.media.kun'),author:'金庸',content:'倚天屠龙记',time:'16:54',area:'武汉',linkNum:30,likeFlag:true})
]
import {ReplyItem,replyList} from '../model/common'
import promptAction from '@ohos.promptAction'

@Entry
@Component
struct ZhihuPage {
  @State content:string = ''
  //评论对象数组
  @State replyList:ReplyItem[] = replyList
  //回复消息
  onReply(){
    const reply:ReplyItem = new ReplyItem({id:1,avatar:$r('app.media.kun'),author:'蔡徐坤',content:this.content,time:'16:54',area:'武汉',linkNum:30,likeFlag:true})
    //添加到对象数组中
    // this.replyList.push(reply) //添加到数组的最后一个
    this.replyList.unshift(reply) //添加到对象数组中的第一个
    this.content = ''
    promptAction.showToast({message:'回复成功'})
  }
  build() {
    Column(){
      TextInput({placeholder:'回复'})
        .layoutWeight(1)
        .onChange((value:string) => {
          this.content = value; //获取到输入框中输入的值
        })
      
      ForEach(this.replyList,(item:ReplyItem) => {
        ReplyComp({item:item})
      })
      Button('发布')
        .onClick(()=> {
          this.onReply();//发布消息
        })
    }
  }
}

@Component
struct ReplyComp {
  @ObjectLink item:ReplyItem
  build() {
    Column(){
      Image(this.item.avatar)
        .width(32)
        .aspectRatio(1)
        .borderRadius(16)
      Column(){
        Text(this.item.author)
          .fontSize(15)
          .fontWeight(100)
          .margin({top:5})
        Text(this.item.content)
          .margin({top:5})
          .fontColor('blue')
          .lineHeight(20)
      }
    }
  }
}

原文地址:https://blog.csdn.net/qq_63946637/article/details/143635083

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!