ArkUI学习-小控件集合

202 阅读6分钟

官方文档地址:

参考地址:

1、Blank

空白填充组件,- Blank在父容器RowColumnFlex主轴方向上未设置大小时会自动拉伸、压缩,设置了大小或容器自适应子节点大小时不会自动拉伸、压缩。

4e4b9929ccf71779d694ecc33a7cd01.png

//Blank组件在横竖屏占满空余空间效果。
Column(){
  Row(){
    Text('BlueBooth').fontSize(18)
    Blank()
    Toggle({ type: ToggleType.Switch }).margin({ top: 14, bottom: 14, left: 6, right: 6 })
  }.width('100%').backgroundColor(0xFFFFFF).borderRadius(15).padding({ left: 12 })
}.backgroundColor(0xEFEFEF).padding(20)

2、Divider:分割线

0382634ae5c7c55896b01bdc29c30f6.png

Column(){
  Text('文本1').fontSize(18)
  Divider().strokeWidth(8).color('#f00')
  Text('文本1').fontSize(18)
  //Round:分界线的两端是半圆;
  Divider().vertical(false).strokeWidth(8).height(40).color('#0f0').lineCap(LineCapStyle.Round)
  //Square:在路径末端延伸半个圆,宽度等于线宽的一半,高度等于线宽(也就是边长了一点)。
  Divider().vertical(false).strokeWidth(8).height(40).color('#0f0').lineCap(LineCapStyle.Square)
  //Butt
  Divider().vertical(false).strokeWidth(8).height(40).color('#0f0').lineCap(LineCapStyle.Butt)

}.backgroundColor(0xEFEFEF).padding(20)

3、Toggle

组件提供勾选框样式、状态按钮样式及开关样式。

136906a3006b180cd5bb2ca969ebe0b.png

Column({ space: 10 }) {
  Text('type: Switch').fontSize(12).fontColor(0xcccccc).width('90%')
  Flex({ justifyContent: FlexAlign.SpaceEvenly, alignItems: ItemAlign.Center }) {
    Toggle({ type: ToggleType.Switch, isOn: false })
      .selectedColor('#007DFF')
      .switchPointColor('#FFFFFF')
      .onChange((isOn: boolean) => {
        console.info('Component status:' + isOn)
      })

    Toggle({ type: ToggleType.Switch, isOn: true })
      .selectedColor('#007DFF')
      .switchPointColor('#FFFFFF')
      .onChange((isOn: boolean) => {
        console.info('Component status:' + isOn)
      })
  }

  Text('type: Checkbox').fontSize(12).fontColor(0xcccccc).width('90%')
  Flex({ justifyContent: FlexAlign.SpaceEvenly, alignItems: ItemAlign.Center }) {
    Toggle({ type: ToggleType.Checkbox, isOn: false })
      .size({ width: 20, height: 20 })
      .selectedColor('#007DFF')
      .onChange((isOn: boolean) => {
        console.info('Component status:' + isOn)
      })

    Toggle({ type: ToggleType.Checkbox, isOn: true })
      .size({ width: 20, height: 20 })
      .selectedColor('#007DFF')
      .onChange((isOn: boolean) => {
        console.info('Component status:' + isOn)
      })
  }

  Text('type: Button').fontSize(12).fontColor(0xcccccc).width('90%')
  Flex({ justifyContent: FlexAlign.SpaceEvenly, alignItems: ItemAlign.Center }) {
    Toggle({ type: ToggleType.Button, isOn: false }) {
      Text('status button').fontColor('#182431').fontSize(12)
    }.width(106)
    .selectedColor('rgba(0,125,255,0.20)')
    .onChange((isOn: boolean) => {
      console.info('Component status:' + isOn)
    })

    Toggle({ type: ToggleType.Button, isOn: true }) {
      Text('status button').fontColor('#182431').fontSize(12)
    }.width(106)
    .selectedColor('rgba(0,125,255,0.20)')
    .onChange((isOn: boolean) => {
      console.info('Component status:' + isOn)
    })
  }
}.width('100%').padding(24)

4、Checkbox

提供多选框组件,通常用于某选项的打开或关闭。

d608e9c02749525bf9503c09fff7836.png

Flex({ justifyContent: FlexAlign.SpaceAround }) {
  Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
    .select(true)
    .selectedColor(0xed6f21)
    .shape(CheckBoxShape.CIRCLE)
    .onChange((value: boolean) => {
      console.info('Checkbox1 change is' + value)
    })
  Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
    .select(false)
    .selectedColor(0x39a2db)
    .shape(CheckBoxShape.ROUNDED_SQUARE)
    .onChange((value: boolean) => {
      console.info('Checkbox2 change is' + value)
    })
}

5、DatePicker

日期选择器组件,用于根据指定日期范围创建日期滑动选择器。

@Entry
@Component
struct DatePickerExample {
  @State isLunar: boolean = false
  private selectedDate: Date = new Date('2021-08-08')

  build() {
    Column() {
      Button('切换公历农历')
        .margin({ top: 30, bottom: 30 })
        .onClick(() => {
          this.isLunar = !this.isLunar
        })
      DatePicker({
        start: new Date('1970-1-1'),
        end: new Date('2100-1-1'),
        selected: this.selectedDate
      })
        .disappearTextStyle({color: Color.Gray, font: {size: '16fp', weight: FontWeight.Bold}})
        .textStyle({color: '#ff182431', font: {size: '18fp', weight: FontWeight.Normal}})
        .selectedTextStyle({color: '#ff0000FF', font: {size: '26fp', weight: FontWeight.Regular}})
        .lunar(this.isLunar)
        .onDateChange((value: Date) => {
          this.selectedDate = value
          console.info('select current date is: ' + value.toString())
        })

    }.width('100%')
  }
}

889e15e9ea303e7ed04efc55e124408.png

6、TextPicker

// xxx.ets
class bottom {
  bottom:number = 50
}
let bott:bottom = new bottom()
@Entry
@Component
struct TextPickerExample {
  private select: number = 1
  private apfruits: string[] = ['apple1', 'apple2', 'apple3', 'apple4']
  private orfruits: string[] = ['orange1', 'orange2', 'orange3', 'orange4']
  private pefruits: string[] = ['peach1', 'peach2', 'peach3', 'peach4']
  private multi: string[][] = [this.apfruits, this.orfruits, this.pefruits]
  private cascade: TextCascadePickerRangeContent[] = [
    {
      text: '辽宁省',
      children: [{ text: '沈阳市', children: [{ text: '沈河区' }, { text: '和平区' }, { text: '浑南区' }] },
        { text: '大连市', children: [{ text: '中山区' }, { text: '金州区' }, { text: '长海县' }] }]
    },
    {
      text: '吉林省',
      children: [{ text: '长春市', children: [{ text: '南关区' }, { text: '宽城区' }, { text: '朝阳区' }] },
        { text: '四平市', children: [{ text: '铁西区' }, { text: '铁东区' }, { text: '梨树县' }] }]
    },
    {
      text: '黑龙江省',
      children: [{ text: '哈尔滨市', children: [{ text: '道里区' }, { text: '道外区' }, { text: '南岗区' }] },
        { text: '牡丹江市', children: [{ text: '东安区' }, { text: '西安区' }, { text: '爱民区' }] }]
    }
  ]

  build() {
    Column() {

      TextPicker({ range: this.apfruits, selected: this.select })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('Picker item changed, value: ' + value + ', index: ' + index)
        }).margin(bott)

      TextPicker({ range: this.multi })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('TextPicker 多列:onChange ' + JSON.stringify(value) + ', ' + 'index: ' + JSON.stringify(index))
        }).margin(bott)

      TextPicker({ range: this.cascade })
        .onChange((value: string | string[], index: number | number[]) => {
          console.info('TextPicker 多列联动:onChange ' + JSON.stringify(value) + ', ' + 'index: ' + JSON.stringify(index))
        })
    }
  }
}

cc1bea9e0419d43c231beebb7748e47.png

7、TimePicker

5f01aec122ddfce4bd3251811bee770.png

// xxx.ets
@Entry
@Component
struct TimePickerExample {
  @State isMilitaryTime: boolean = false
  private selectedTime: Date = new Date('2022-07-22T08:00:00')

  build() {
    Column() {
      Button('切换12小时制/24小时制')
        .margin(30)
        .onClick(() => {
          this.isMilitaryTime = !this.isMilitaryTime
        })
      TimePicker({
        selected: this.selectedTime,
      })
        .useMilitaryTime(this.isMilitaryTime)
        .onChange((value: TimePickerResult) => {
          if(value.hour >= 0) {
            this.selectedTime.setHours(value.hour, value.minute)
            console.info('select current date is: ' + JSON.stringify(value))
          }
        })
        .disappearTextStyle({color: Color.Red, font: {size: 15, weight: FontWeight.Lighter}})
        .textStyle({color: Color.Black, font: {size: 20, weight: FontWeight.Normal}})
        .selectedTextStyle({color: Color.Blue, font: {size: 30, weight: FontWeight.Bolder}})
    }.width('100%')
  }
}

8、Select

0e62dfc2b37140be67554bf28e7491d.png

// xxx.ets
@Entry
@Component
struct SelectExample {
  @State text: string = "TTTTT"
  @State index: number = 2
  @State space: number = 8
  @State arrowPosition: ArrowPosition = ArrowPosition.END
  build() {
    Column() {
      Select([{ value: 'aaa', icon: $r("app.media.startIcon") },
        { value: 'bbb', icon: $r("app.media.startIcon") },
        { value: 'ccc', icon: $r("app.media.startIcon") },
        { value: 'ddd', icon: $r("app.media.startIcon") }])
        .selected(this.index)
        .value(this.text)
        .font({ size: 16, weight: 500 })
        .fontColor('#182431')
        .selectedOptionFont({ size: 16, weight: 400 })
        .optionFont({ size: 16, weight: 400 })
        .space(this.space)
        .arrowPosition(this.arrowPosition)
        .menuAlign(MenuAlignType.START, {dx:0, dy:0})
        .optionWidth(200)
        .optionHeight(300)
        .onSelect((index:number, text?: string | undefined)=>{
          console.info('Select:' + index)
          this.index = index;
          if(text){
            this.text = text;
          }
        })
    }.width('100%')
  }
}

9、Slider

设置滑轨的背景颜色。

// xxx.ets
@Entry
@Component
struct SliderExample {
  @State outSetValueOne: number = 40
  @State inSetValueOne: number = 40
  @State noneValueOne: number = 40
  @State outSetValueTwo: number = 40
  @State inSetValueTwo: number = 40
  @State vOutSetValueOne: number = 40
  @State vInSetValueOne: number = 40
  @State vOutSetValueTwo: number = 40
  @State vInSetValueTwo: number = 40

  build() {
    Column({ space: 8 }) {
      Text('outset slider').fontSize(9).fontColor(0xCCCCCC).width('90%').margin(15)
      Row() {
        Slider({
          value: this.outSetValueOne,
          min: 0,
          max: 100,
          style: SliderStyle.OutSet
        })
          .showTips(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            this.outSetValueOne = value
            console.info('value:' + value + 'mode:' + mode.toString())
          })
        // toFixed(0)将滑动条返回值处理为整数精度
        Text(this.outSetValueOne.toFixed(0)).fontSize(12)
      }
      .width('80%')
      Row() {
        Slider({
          value: this.outSetValueTwo,
          step: 10,
          style: SliderStyle.OutSet
        })
          .showSteps(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            this.outSetValueTwo = value
            console.info('value:' + value + 'mode:' + mode.toString())
          })
        Text(this.outSetValueTwo.toFixed(0)).fontSize(12)
      }
      .width('80%')

      Text('inset slider').fontSize(9).fontColor(0xCCCCCC).width('90%').margin(15)
      Row() {
        Slider({
          value: this.inSetValueOne,
          min: 0,
          max: 100,
          style: SliderStyle.InSet
        })
          .blockColor('#191970')
          .trackColor('#ADD8E6')
          .selectedColor('#4169E1')
          .showTips(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            this.inSetValueOne = value
            console.info('value:' + value + 'mode:' + mode.toString())
          })
        Text(this.inSetValueOne.toFixed(0)).fontSize(12)
      }
      .width('80%')
      Row() {
        Slider({
          value: this.inSetValueTwo,
          step: 10,
          style: SliderStyle.InSet
        })
          .blockColor('#191970')
          .trackColor('#ADD8E6')
          .selectedColor('#4169E1')
          .showSteps(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            this.inSetValueTwo = value
            console.info('value:' + value + 'mode:' + mode.toString())
          })
        Text(this.inSetValueTwo.toFixed(0)).fontSize(12)
      }
      .width('80%')

      Text('none slider').fontSize(9).fontColor(0xCCCCCC).width('90%').margin(15)
      Row() {
        Slider({
          value: this.noneValueOne,
          min: 0,
          max: 100,
          style: SliderStyle.NONE
        })
          .blockColor('#191970')
          .trackColor('#ADD8E6')
          .selectedColor('#4169E1')
          .showTips(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            this.noneValueOne = value
            console.info('value:' + value + 'mode:' + mode.toString())
          })
        Text(this.noneValueOne.toFixed(0)).fontSize(12)
      }
      .width('80%')

      Row() {
        Column() {
          Text('vertical outset slider').fontSize(9).fontColor(0xCCCCCC).width('50%').margin(15)
          Row() {
            Text().width('10%')
            Slider({
              value: this.vOutSetValueOne,
              style: SliderStyle.OutSet,
              direction: Axis.Vertical
            })
              .blockColor('#191970')
              .trackColor('#ADD8E6')
              .selectedColor('#4169E1')
              .showTips(true)
              .onChange((value: number, mode: SliderChangeMode) => {
                this.vOutSetValueOne = value
                console.info('value:' + value + 'mode:' + mode.toString())
              })
            Slider({
              value: this.vOutSetValueTwo,
              step: 10,
              style: SliderStyle.OutSet,
              direction: Axis.Vertical
            })
              .blockColor('#191970')
              .trackColor('#ADD8E6')
              .selectedColor('#4169E1')
              .showSteps(true)
              .onChange((value: number, mode: SliderChangeMode) => {
                this.vOutSetValueTwo = value
                console.info('value:' + value + 'mode:' + mode.toString())
              })
          }
        }.width('50%').height(300)

        Column() {
          Text('vertical inset slider').fontSize(9).fontColor(0xCCCCCC).width('50%').margin(15)
          Row() {
            Slider({
              value: this.vInSetValueOne,
              style: SliderStyle.InSet,
              direction: Axis.Vertical,
              reverse: true // 竖向的Slider默认是上端是min值,下端是max值,因此想要从下往上滑动,需要设置reverse为true
            })
              .showTips(true)
              .onChange((value: number, mode: SliderChangeMode) => {
                this.vInSetValueOne = value
                console.info('value:' + value + 'mode:' + mode.toString())
              })
            Slider({
              value: this.vInSetValueTwo,
              step: 10,
              style: SliderStyle.InSet,
              direction: Axis.Vertical,
              reverse: true
            })
              .showSteps(true)
              .onChange((value: number, mode: SliderChangeMode) => {
                this.vInSetValueTwo = value
                console.info('value:' + value + 'mode:' + mode.toString())
              })
          }
        }.width('50%').height(300)
      }
    }.width('100%')
  }
}

969e42686f67898911fef1eb25914ee.png

10、Search

搜索框组件,适用于浏览器的搜索内容输入框等应用场景。

// xxx.ets
@Entry
@Component
struct SearchExample {
  @State changeValue: string = ''
  @State submitValue: string = ''
  @State positionInfo: CaretOffset = { index: 0, x: 0, y: 0 }
  controller: SearchController = new SearchController()

  build() {
    Column({space: 10}) {
      Text('onSubmit:' + this.submitValue).fontSize(18).margin(15)
      Text('onChange:' + this.changeValue).fontSize(18).margin(15)
      Search({ value: this.changeValue, placeholder: 'Type to search...', controller: this.controller })
        .searchButton('SEARCH')
        .width('95%')
        .height(40)
        .backgroundColor('#F5F5F5')
        .placeholderColor(Color.Grey)
        .placeholderFont({ size: 14, weight: 400 })
        .textFont({ size: 14, weight: 400 })
        .onSubmit((value: string) => {
          this.submitValue = value
        })
        .onChange((value: string) => {
          this.changeValue = value
        })
        .margin(20)
      Button('Set caretPosition 1')
        .onClick(() => {
          // 设置光标位置到输入的第一个字符后
          this.controller.caretPosition(1)
        })
      Button('Get CaretOffset')
        .onClick(() => {
          this.positionInfo = this.controller.getCaretOffset()
        })
    }.width('100%')
  }
}

261213937a2d90e065e0f8ab1143b2e.png

11、web

展示网页内容的组件。

// xxx.ets
import webview from '@ohos.web.webview';
@Entry
@Component
struct Index {
  webCtr : webview.WebviewController = new webview.WebviewController();


  build() {
    Column() {

      Web({
        src:'https://www.baidu.com/'
      , controller : this.webCtr
      })
  }
}

}

12、路由使用

//1.引入路由
import router from '@ohos.router'
 
//2.使用跳转
 
   router.pushUrl({
        url: "pages/Show"
      })
      
      
      
//延迟跳转  
setTimeout(() => {
      //跳转到数据展示页面
      router.pushUrl({
        url: "pages/Show"
      })
    },2000)

13、布局-grid布局

f982d4a88d6c23286b657c241f44748.png

// Grid布局的基本使用: 规则的行列布局中非常常见, 3行4列
Grid() {
  ForEach([1,2,3,4,5,6,7,8,9,10,11,12], () => {
    GridItem() {
      Column() {

      }
      .width('100%')
      .height('100%')
      .backgroundColor(Color.Green)
      .border({ width: 1 })
    }
  })
}
//设置多少列
.columnsTemplate('1fr 1fr 1fr 1fr')
//设置多少行
.rowsTemplate('1fr 1fr 1fr')
.columnsGap(6)
.rowsGap(6)
.width('100%')
.height(500)
.backgroundColor(Color.Pink)

14、层叠布局

// 层叠布局
Stack({
  alignContent: Alignment.Bottom
}) {
  Text('大儿子')
    .width(250)
    .height(250)
    .backgroundColor(Color.Green)
    .zIndex(3)
  Text('二儿子')
    .width(150)
    .height(150)
    .backgroundColor(Color.Orange)
    .zIndex(4)
  Text('三儿子')
    .width(50)
    .height(50)
    .backgroundColor(Color.Yellow)
    .zIndex(5)
}
.width(300)
.height(600)
.backgroundColor(Color.Pink)

15、动画-动画特效

图片.png

16、模块化-导入,导出模块

// 路径: 查找文件时, 从起点到终点的路线
// 相对路径: 从当前文件出发查找目标文件
// → 找上一级 ../
// → 同级目录 ./
 
// 1. 默认导入
// import result from '../tools/module1'
// import fn from './module2'
// console.log('module1中的数据', JSON.stringify(result))
// fn()
 
// 2. 按需导入
// import { name1, price, sayHi as sayHello } from '../tools/module3'
// console.log('module3中的数据', name1, price)
// sayHello()
 
// 3. 全部导入
import * as Module3 from '../tools/module3'
console.log('全部的数据', Module3.name1)
console.log('全部的数据', Module3.price2)
Module3.sayHi()
 
 
 
 
--------------------------默认导出------------------
interface Person {
  name: string
  age: number
}
 
// 一个ets文件, 就是一个模块, 每个模块之间独立
let num: number = 10
let person: Person = {
  name: 'jack',
  age: 18
}
 
// 默认导出 (导出一个值)
export default person
 
--------------------------按需导出------------------------
// 按需导出
// 多个特性, 逐个 export 按需导出
// export let name1: string = '刘备'
// export let price: number = 9.98
// export let sayHi = () => {
//   console.log('打招呼')
// }
 
let name1: string = '刘备'
let name2: string = '张飞'
let name3: string = '关羽'
 
let price: number = 9.98
let price2: number = 10.1
 
let sayHi = () => {
  console.log('打招呼')
}
let run = () => {
  console.log('跑步')
}
 
// 一次性将多个特性, 进行导出
export {
  name1, name2, name3,
  price, price2,
  sayHi, run
}
 

17、注解-@Entry

在ArkUI 中,@Entry和@Component是两个非常重要的装饰器,@Entry装饰器用于指定应用程序的入口,应用程序启动时将首先加载该组件,而@Component装饰器则用于创建自定义的UI组件。

在ArkUI的示例代码中,通常使用struct来声明一个自定义UI组件,并在build()函数中描述该组件的内容。

@Entry
@Component

struct TextView {
  build() {
    // 视图代码
  }
}

18、注解-@Extend 扩展特定组件样式

// @Extend(组件名)
// function 函数名 (参数, 参数2) {
//
// }
 
@Extend(Text)
function textFn () {
  .fontSize(20)
  .fontWeight(FontWeight.Bold)
}
 
@Extend(Text)
function bannerItem (bgColor: ResourceColor, msg: string) {
  .textAlign(TextAlign.Center)
  .backgroundColor(bgColor)
  .fontColor(Color.White)
  .fontSize(30)
  .onClick(() => {
    AlertDialog.show({
      message: msg
    })
  })
}
 
 
----------------------------调用------------------------------
      Text(this.message)
        .textFn()
 
      Swiper() {
        Text('1')
          .bannerItem(Color.Orange, '轮播图1号')
        Text('2')
          .bannerItem(Color.Brown, '轮播图2号')
        Text('3')
          .bannerItem(Color.Green, '轮播图3号')
      }
      .width('100%')
      .height(160)

18、注解-@Styles 通用组件设置值

// 1. 全局定义
@Styles function commonStyles () {
  .width(100)
  .height(100)
}

@Entry
@Component
struct StylesDemo {
  @State message: string = '@styles';
  @State bgColor: ResourceColor = Color.Gray

  // 2. 组件内定义(才能通过this访问到自己的状态)
  @Styles setBg() {
    .backgroundColor(this.bgColor)
    .onClick(() => {
      this.bgColor = Color.Pink
    })
  }

  build() {
    Column({ space: 10 }) {
      Text(this.message)
        .fontColor(Color.White)
        .commonStyles()
        .setBg()

      Column() {}
      .commonStyles()
      .setBg()

      Button('按钮')
        .commonStyles()
        .setBg()
    }
    .width('100%')
    .height('100%')
  }


}

19、注解-@Builder 构建一个全局组件

93009dad5c60a4c0535166a1ef873ea.png

// 全局 Builder
@Builder
function navItem(icon: ResourceStr, txt: string) {
  Column({ space: 10 }) {
    Image(icon)
      .width('80%')
    Text(txt)
  }
  .width('25%')
  .onClick(() => {
    AlertDialog.show({
      message: '点了' + txt
    })
  })
}


@Entry
@Component
struct BuilderDemo {
  @State message: string = '@Builder';

  //定义一个全局的组件
  @Builder
  navItem(icon: ResourceStr, txt: string) {
    Column({ space: 10 }) {
      Image(icon)
        .width('80%')
      Text(txt)
    }
    .width('25%')
    .onClick(() => {
      AlertDialog.show({
        message: '点了' + txt + this.message
      })
    })
  }

  //文本的开始
  build() {
    Column({ space: 20 }) {
      Text(this.message)
        .fontSize(30)

      Row() {
        Row() {
          navItem($r('app.media.bg_001'), '阿里拍卖')
          navItem($r('app.media.bg_002'), '菜鸟')
          this.navItem($r('app.media.bg_003'), '巴巴农场')
          this.navItem($r('app.media.bg_004'), '阿里药房')
        }
      }
    }
    .width('100%')
    .height('100%')
  }

}

20、注解-@Component 模块复用1

5866a9b59c7bdbe51f9a15e00ea127a.png

@Component
struct MyHeader {
  build() {
    Row() {
      Text('我是头部')
        .fontColor(Color.White)
    }
    .width('100%')
    .height(50)
    .backgroundColor(Color.Brown)
  }
}


@Component
struct MyCom {
  @State count: number = 1
  build() {
    Row() {
      Text(this.count.toString())
        .fontColor(Color.White)
        .margin(10)
      Button('按钮')
        .onClick(() => {
          this.count++
        })
    }
  }
}

@Component
struct MyMain {
  build() {
    Column() {
      // 将相同的业务逻辑, 封装成一个通用的组件
      MyCom()
      MyCom()
      MyCom()
    }
    .layoutWeight(1)
    .width('100%')
    .backgroundColor(Color.Gray)
  }
}

@Component
struct MyFooter {
  build() {
    Row() {
      Text('我是底部')
    }
    .width('100%')
    .height(50)
    .backgroundColor(Color.Green)
  }
}

@Entry
@Component
struct Index {
  build() {
    Column() {
      MyHeader()
      MyMain()
      MyFooter()
    }
  }
}

21、注解-@Component 模块复用2

图片.png

  • 1、在components文件下创建HelloCom

0e693904525316159fc9dad934c6cf6.png

//---------------------------------------1.导出组件------------------
//加入这个@Preview可以预览页面
@Preview
@Component
export struct HelloCom {
  build() {
    Row() {
      Text('自定义组件')
      Button('按钮')
    }
    .width(200)
    .height(50)
    .backgroundColor(Color.Orange)
  }
}
  • 2、在components文件下创建Index 并导入HelloCom

155585ff0c99a6500b3b8972f77c1d5.png

//-----------------------------------2.导入组件----------------------------
import { HelloCom } from '../components/HelloCom'

@Entry
@Component
struct Index {
  build() {
    Column() {
      HelloCom()
        .width(250)
        .height(60)
        .backgroundColor(Color.Gray)
        .onClick(() => {
          AlertDialog.show({
            message: '测试点击'
          })
        })
    }
  }
}

22、注解-@Component 模块复用3

9670a3f8317da3f851cd355c74d1a84.png

//公共模块
@Component
struct MyPanel {
  // 成员变量 - 数据
  title: string = '默认的大标题'
  extra: string = '查看更多 >'
  // 成员变量 - 函数 - 可以外部传入覆盖的
  getMore = () => {
    AlertDialog.show({
      message: '查看更多'
    })
  }

  // 成员函数 - 不可以外部传入覆盖
  sayHi() {
    AlertDialog.show({
      message: '打招呼, 你好'
    })
  }

  build() {
    Column() {
      Row() {
        Text(this.title).fontSize(18)
        Text(this.extra).fontSize(18)
          .onClick(() => {
            this.getMore()
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)

      Row() {
        Text('内容部分').fontSize(18)
        Button('按钮')
          .onClick(() => {
            this.sayHi()
          })
      }
      .padding(20)
    }
    .padding(10)
    .width('100%')
    .height(200)
    .margin({ bottom: 20 })
    .borderRadius(10)
    .backgroundColor(Color.White)
  }
}


//-------------------------------分割线------------------------------------
//调用公共方法
@Entry
@Component
struct Index {
  build() {
    Column() {
      MyPanel({
        title: '我的订单',
        extra: '全部订单 > ',
        getMore() {
          AlertDialog.show({
            message: '点击了全部订单'
          })
        }
      })
      MyPanel({
        title: '小米有品重酬',
        extra: '7款重酬中 >',
        getMore() {
          AlertDialog.show({
            message: '查看7款重酬'
          })
        }
      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ccc')
    .padding(20)
  }
}

23、注解-@BuilderParam 可以让自定义组件外部传递UI,相当于vue的插槽(单个参数)

4c4f9ab83566ad0fc9f2dc6c1bb1a93.png

@Component
struct MyPanel {
  // 成员变量 - 数据
  title: string = '默认的大标题'
  extra: string = '查看更多 >'
  // 成员变量 - 函数 - 可以外部传入覆盖的
  getMore = () => {
    AlertDialog.show({
      message: '查看更多'
    })
  }

  // 成员函数 - 不可以外部传入覆盖
  sayHi() {
    AlertDialog.show({
      message: '打招呼, 你好'
    })
  }

  //定义BuilderParam的方法
  @BuilderParam ContentBuilder: () => void = this.defaultBuilder
  @Builder defaultBuilder () {
    Text('默认文本')
  }

  build() {
    Column() {
      Row() {
        Text(this.title).fontSize(18)
        Text(this.extra).fontSize(18)
          .onClick(() => {
            this.getMore()
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
      Row() {
        // 这里的结构不能写死, 需要通过 BuilderParam 来进行构建
        this.ContentBuilder()
      }
      .padding(20)
    }
    .padding(10)
    .width('100%')
    .height(200)
    .margin({ bottom: 20 })
    .borderRadius(10)
    .backgroundColor(Color.White)
  }
}

@Entry
@Component
struct Index {
  build() {
    Column() {
      MyPanel({
        title: '我的订单',
        extra: '全部订单 > ',
        getMore() {
          AlertDialog.show({
            message: '点击了全部订单'
          })
        }
      }) {
        Column() {
          Text('我是订单 - 相关的文本')
          Text('我是订单 - 相关的文本').margin(5)
          Text('我是订单 - 相关的文本')
        }
      }
      MyPanel({
        title: '小米有品重酬',
        extra: '7款重酬中 >',
        getMore() {
          AlertDialog.show({
            message: '查看7款重酬'
          })
        }
      }) {
        Column() {
          Button('我是小米重酬的按钮')
          Button('我是小米重酬的按钮').margin(5)
          Button('我是小米重酬的按钮')
        }
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ccc')
    .padding(20)
  }
}

24、注解-@BuilderParam 可以让自定义组件外部传递UI,相当于vue的插槽(多个参数)

1f2718a0af5146ada5a2ff755f7b9a5.png

//定义MyCard的卡片
@Component
struct MyCard {
  @BuilderParam tBuilder: () => void = this.tDefaultBuilder
  @BuilderParam cBuilder: () => void = this.cDefaultBuilder
  @Builder tDefaultBuilder () {
    Text('我是默认的大标题')
  }
  @Builder cDefaultBuilder () {
    Text('我是默认的内容')
  }

  build() {
    // 卡片组件
    Column() {
      // 标题部分
      Row() {
        this.tBuilder()
      }
      .height(30)
      .width('100%')
      .border({ color: '#ccc', width: { bottom: 1 }})
      .padding({ left: 10 })
      // 内容部分
      Row() {
        this.cBuilder()
      }
      .width('100%')
      .padding(10)
    }
    .width('100%')
    .height(100)
    .backgroundColor(Color.White)
    .borderRadius(10)
    .justifyContent(FlexAlign.Start)
  }
}


//方法调用
@Entry
@Component
struct Index {
  @Builder ftBuilder () {
    Text('我是传入的大标题结构').fontSize(20).fontColor(Color.Red)
  }
  @Builder fcBuilder () {
    Text('我是内容部分')
    Text('我是内容部分')
    Text('我是内容部分')
  }

  build() {
    Column({ space: 10 }) {
      MyCard()
      MyCard({
        tBuilder: this.ftBuilder,
        cBuilder: this.fcBuilder
      })
    }
    .width('100%')
    .height('100%')
    .padding(20)
    .backgroundColor('#ccc')
  }
}

25、注解-@State 状态变量

  • 1、简单的变量赋值:
// 注意点:
// 1. 普通变量, 只能在初始化时渲染, 后续变化了, 也不会引起更新
// 2. 状态变量, 被装饰器修饰, 值的改变, 会 [自动] 引起 界面的刷新

// 组件外的[普通变量] 不需要this即可访问
let myName: string = '吕布'

@Entry
@Component
struct Index {
  // 组件内的[普通变量] this.xxx
  myAge: number = 18
  // 组件内的[状态变量] this.xxx
  @State myMsg: string = 'hello 黑马'

  build() {
    Column() {
      Button(myName).onClick(() => {
        myName = '貂蝉'
        console.log('myName', myName)
      })
      Button(this.myAge.toString()).onClick(() => {
        this.myAge = 200
        console.log('myAge', this.myAge)
      }).margin(5)
      Button(this.myMsg).onClick(() => {
        this.myMsg = '我已经变成白马了'
        console.log('myMsg', this.myMsg)
      })
    }
  }
}
  • 2、复杂的变量赋值:

aba5df18a8154962e567a1a282d54a8.png

interface Car {
  name: string
}

interface Person {
  name: string
  car: Car
}

const obj: Person = {
  name: 'zs',
  car: {
    name: '小黄车'
  }
}
console.log('查看第一层属性', Object.keys(obj))



//调用
@Entry
@Component
struct Index {
  // 状态变量
  // 1. string number boolean 可以直接监视到变化
  @State message: string = 'hello world'
  // 2. 复杂类型 object class, 第一层随便改, 嵌套需要进行整个嵌套对象的替换
  @State person: Person = {
    name: 'jack',
    car: {
      name: '宝马车'
    }
  }

  build() {
    Column() {
      
      Text(this.message).fontSize(20)
      Button('改message').onClick(() => {
        this.message = '你好'
      })
      
      
      Text(JSON.stringify(this.person)).margin({top:20})
      Button('改person').onClick(() => {
        // this.person = {
        //   name: 'amy',
        //   car: {
        //     name: '保时捷'
        //   }
        // }

        this.person.name = 'tony'

        // 如果不是对象的第一层属性, 修改时, 需要修改整个嵌套的对象
        this.person.car.name = '小火车'

        // console.log('car name', this.person.car.name)
        this.person.car = {
          name: '老爷车'
        }
      })
    }
  }
}

26、注解-@Prop父传子,让值变化

e294bfc55143297398d5c9ebaeffe22.png

//@Prop加了这个后,父组件更改这个值,子组件也会跟着变化
@Component
struct SonCom {
  // 保证父组件的数据变化了, 能够往下响应式的更新
  @Prop sCar: string = ''
  changeCar = (newCar: string) => {}

  build() {
    Column() {
      Text(`子组件 ${this.sCar}`)
      Button('换车').onClick((event: ClickEvent) => {
        // 1. prop传值 → 单向传递
        // 子组件, 可以修改到 prop 传值, 但是修改的更新不会同步到父组件
        // 通常不太会直接修改 prop 传值, 父组件的状态一旦变化, 会自动向下同步
        // 修改就被覆盖了
        // this.sCar = '小黄车'

        // 2. 如果实在想更新, 希望保证父子同步 => 调用父组件传递过来的方法
        // 如果没有写箭头函数, 意味着, this 指向 调用者, 而此处执行环境 this → 子组件
        this.changeCar('蹦蹦车')
      })
    }
    .padding(20)
    .backgroundColor(Color.Orange)
  }
}


@Entry
@Component
struct FatherCom {
  @State fCar:string = '劳斯莱斯'
  build() {
    Column() {
      Text(`父组件 - ${this.fCar}`)
      Button('换车').onClick(() => {
        this.fCar = '三轮车'
      })

      SonCom({
        sCar: this.fCar,
        // 这里必须要用箭头函数, 否则会有 this 指向的问题
        // 使用箭头函数的好处, 可以使用外部环境的 this, 不受传递过去后的执行环境影响
        // 希望此处 this 指向 父组件
        changeCar: (newCar: string) => {
          this.fCar = newCar
        }
      })
    }
    .padding(50)
    .backgroundColor(Color.Pink)
  }
}

27、注解-@Link 父传子 子传父,数据双向同步

f5bd941aeda59b18b3c6a3dccbb77db.png

interface Person {
  name: string
  age: number
}

@Entry
@Component
  // 父组件
struct KnowledgePage {
  @State count: number = 0
  @State person: Person = {
    name: 'zs',
    age: 18
  }

  build() {
    Column() {
      Text('父组件')
        .fontSize(30)
      Text(this.count.toString())
      Text(JSON.stringify(this.person))
      Button('修改数据')
        .onClick(() => {
          this.count++
        })
      SonComponent({
        count: this.count,
        person: this.person
      })
    }
    .padding(10)
    .height('100%')
    .backgroundColor('#eee')
    .width('100%')
    .alignItems(HorizontalAlign.Center)
    .padding({ top: 100 })
  }
}


@Component
  // 子组件
struct SonComponent {
  @Link count: number
  @Link person: Person

  // 编写 UI
  build() {
    Column({ space: 20 }) {
      Text('我是子组件')
        .fontSize(20)
      Text(this.count.toString())
      Text(JSON.stringify(this.person))

      Column() {
        Button('修改数据')
          .onClick(() => {
            // this.count++
            this.person.age++
          })

      }

    }
    .backgroundColor('#a6c398')
    .alignItems(HorizontalAlign.Center)
    .width('80%')
    .margin({ top: 100 })
    .padding(10)
    .borderRadius(10)

  }
}

28、注解- @Consume和@Provide父与子孙后代简单数据同步

2f746953a39d7d3215683bc95518239.png

interface Car {
  name: string
  brand: string
}

@Entry
@Component
  // 顶级组件
struct RootComponent {
  @Provide themeColor: string = 'yellow'
  @Provide car: Car = {
    name: '小黄',
    brand: '美团'
  }
  build() {
    Column() {
      Text('顶级组件')
        .fontSize(30)
        .fontWeight(900)
      Text(this.themeColor)
      Text(JSON.stringify(this.car))

      // 二级组件
      ParentComponent()
      ParentComponent()
    }
    .padding(10)
    .height('100%')
    .backgroundColor('#ccc')
    .width('100%')
    .alignItems(HorizontalAlign.Center)
    .padding({ top: 30 })
  }
}


@Component
  // 二级组件
struct ParentComponent {
  @Consume themeColor: string
  // 编写 UI
  build() {
    Column({ space: 20 }) {
      Text('我是二级组件')
        .fontSize(22)
        .fontWeight(900)
      Text(this.themeColor)

      // 内层子组件
      SonComponent()
    }
    .backgroundColor('#a6c398')
    .alignItems(HorizontalAlign.Center)
    .width('90%')
    .margin({ top: 50 })
    .padding(10)
    .borderRadius(10)

  }
}

@Component
  // 内层组件
struct SonComponent {
  @Consume themeColor: string
  @Consume car: Car
  // 编写 UI
  build() {
    Column({ space: 20 }) {
      Text('我是内层组件' + this.themeColor)
        .fontSize(20)
        .fontWeight(900)
        .onClick(() => {
          // this.themeColor = 'orange'
          this.car.name = '小绿'
        })
      Text(JSON.stringify(this.car))
    }
    .backgroundColor('#bf94e4')
    .alignItems(HorizontalAlign.Center)
    .width('90%')
    .margin({ top: 50 })
    .padding(10)
    .borderRadius(10)

  }
}

29、注解- @Observed和@ObjectLink父与子孙后代复杂数据同步(多层数据)

81b1a51610952d6424bae2bb2f5d2c4.png

interface IPerson {
  id: number
  name: string
  age: number
}

@Observed
class Person {
  id: number
  name: string
  age: number

  constructor(obj: IPerson) {
    this.id = obj.id
    this.name = obj.name
    this.age = obj.age
  }
}


@Entry
@Component
struct ObservedAndLink {
  @State personList: Person[] = [
    new Person({
      id: 1,
      name: '张三',
      age: 18
    }),
    new Person({
      id: 2,
      name: '李四',
      age: 19
    }),
    new Person({
      id: 3,
      name: '王五',
      age: 20
    })
  ]

  build() {
    Column({ space: 20 }) {
      Text('父组件')
        .fontSize(30)
      List({ space: 10 }) {
        ForEach(this.personList, (item: Person, index: number) => {
          ItemCom({
            info: item,
            addAge: () => {
              // 修改嵌套的数据 => 普通的情况, 监视不到更新
              item.age++ // 如果能监视到
              AlertDialog.show({
                message: JSON.stringify(this.personList)
              })
              // this.personList.splice(index, 1, item) // 无需手动替换更新
            }
          })
        })
      }

    }
    .backgroundColor('#cbe69b')
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

@Component
struct ItemCom {
  @ObjectLink info: Person
  addAge = () => {

  }

  build() {
    ListItem() {
      Row({ space: 10 }) {
        Text('姓名:' + this.info.name)
        Text('年龄:' + this.info.age)
        Blank()
        Button('修改数据')
          .onClick(() => {
            // this.addAge()
            this.info.age++
          })
      }
      .backgroundColor(Color.Pink)
      .padding(10)
      .width('100%')
    }
  }
}