注意:博主有個鴻蒙專欄,里面從上到下有關于鴻蒙next的教學文檔,大家感興趣可以學習下
如果大家覺得博主文章寫的好的話,可以點下關注,博主會一直更新鴻蒙next相關知識
專欄地址: https://blog.csdn.net/qq_56760790/category_12794123.html
文章所屬類目(HarmonyOS 語言-ArkTS)
目錄
1. 渲染-條件渲染
1.1 基本介紹
1.2 if/else
1.3 visibility屬性控制
1.4 多種條件控制
2. 渲染-循環渲染
2.1 ForEach:循環渲染
2.1.1 基本介紹
2.1.2 語法
2.1.3 代碼示例
2.1.4 key的推薦建議
3. 下拉刷新+上拉加載
3.1 下拉刷新
3.2 上拉加載
1. 渲染-條件渲染
1.1 基本介紹
在ArkTS中 我們要根據某個狀態來控制元素或者組件的顯示隱藏 可以采用條件渲染
- if/else(創建銷毀元素)
- visibility屬性控制
1.2 if/else
- 支持if、else和else if語句。
- if、else if后跟隨的條件語句可以使用狀態變量或者常規變量(狀態變量:值的改變可以實時渲染UI,常規變量:值的改變不會實時渲染UI)。
- 允許在容器組件內使用,通過條件渲染語句構建不同的子組件。
- 條件渲染語句在涉及到組件的父子關系時是“透明”的,當父組件和子組件之間存在一個或多個if語句時,必須遵守父組件關于子組件使用的規則。
- 每個分支內部的構建函數必須遵循構建函數的規則,并創建一個或多個組件。無法創建組件的空構建函數會產生語法錯誤。
- 某些容器組件限制子組件的類型或數量,將條件渲染語句用于這些組件內時,這些限制將同樣應用于條件渲染語句內創建的組件。例如,Grid容器組件的子組件僅支持GridItem組件,在Grid內使用條件渲染語句時,條件渲染語句內僅允許使用GridItem組件。
注意:
當if、else if后跟隨的狀態判斷中使用的狀態變量值變化時,條件渲染語句會進行更新,更新步驟如下:
- 評估if和else if的狀態判斷條件,如果分支沒有變化,無需執行以下步驟。如果分支有變化,則執行2、3步驟。
- 刪除此前構建的所有子組件。
- 執行新分支的構造函數,將獲取到的組件添加到if父容器中。如果缺少適用的else分支,則不構建任何內容。
@Entry
@Component
struct Demo1 {@State count: number = 0;build() {Column() {Text(`count=${this.count}`)if (this.count > 0) {Text(`count is positive`).fontColor(Color.Green)}Button('increase count').onClick(() => {this.count++;})Button('decrease count').onClick(() => {this.count--;})}}
}
顯示隱藏
@Entry@Componentstruct Index {@State isShow:boolean=truebuild() {Column() {Button('顯示/隱藏').width(100).height(30).onClick(()=>{if(this.isShow){this.isShow=false}else{this.isShow=true}})if(this.isShow){Text('我是東林').width(200).height(200).fontSize(40)}}.width('100%').height('100%')}}
1.3 visibility屬性控制
visibility屬性有以下三種:
1、Visible 顯示
2、Hidden 隱藏
3、None 隱藏,但是不占位置
@Entry@Componentstruct Demo2 {@State isShow:boolean=truebuild() {Column() {Button('顯示/隱藏').width(100).height(30).onClick(()=>{if(this.isShow){this.isShow=false}else{this.isShow=true}})Text('我是東林').width(200).height(200).fontSize(40).backgroundColor(Color.Green).visibility(this.isShow?Visibility.Visible:Visibility.Hidden)Text('小頭').width(200).height(200).fontSize(40).backgroundColor(Color.Yellow)}.width('100%').height('100%')}}
1.4 多種條件控制
分析:
1.頁面排版布局樣式實現
2.下拉框的雙向綁定
3.條件渲染
@Entry@ComponentV2struct Demo3 {@Local myVip: number = 0;@Local optionValue: string = '暫不開通'build() {Column({ space: 20 }) {Row() {Text('開通會員:')Select([{ value: '暫不開通' },{ value: 'VIP' },{ value: 'SVIP' }]).width('50%').selected($$this.myVip).value($$this.optionValue)// .onSelect((index, value) => {// this.myVip = index// this.optionValue = value// })}Row({ space: 20 }) {Image($r('app.media.img')).width(30).borderRadius(30)Text('西北吳彥祖')if (this.myVip === 0) {Text('VIP').VIPStyle(this.myVip).backgroundColor('#ccc')} else if (this.myVip === 1) {Text('VIP').VIPStyle(this.myVip).backgroundColor('#ffffb803')} else if (this.myVip === 2) {Text('SVIP').VIPStyle(this.myVip).backgroundColor('#ffb00909')}}.width('100%').justifyContent(FlexAlign.Center)}.width('100%').padding(20)}}@Extend(Text)function VIPStyle(type: number) {.padding({left: 12,right: 12,bottom: 4,top: 4}).fontColor('#fff').borderRadius(20).fontSize(12)}
2. 渲染-循環渲染
- ForEach-最常用的
- LazyForEach-懶加載渲染
2.1 ForEach:循環渲染
2.1.1 基本介紹
ForEach接口基于數組類型數據來進行循環渲染,需要與容器組件配合使用,且接口返回的組件應當是允許包含在ForEach父容器組件中的子組件。例如,ListItem組件要求ForEach的父容器組件必須為List組件。
2.1.2 語法
ForEach(// 數據源arr: Array,// 組件生成函數itemGenerator: (item: 單項, index?: number) => void,// 鍵值生成函數keyGenerator?: (item: 單項, index?: number): string => string
)
ForEach
接口基于數組類型數據來進行循環渲染,需要與容器組件配合使用。
2.1.3 代碼示例
interface PayRecord {OrderName:stringOrderDate:DateOrderAmount:number
}
@Entry@ComponentV2struct ForEachDemo {PayRecordList: PayRecord[] = [{OrderName: '給老婆買口紅',OrderDate: new Date('2024/05/11'),OrderAmount: 399.00},{OrderName: '給老婆買花',OrderDate: new Date('2024/05/12'),OrderAmount: 99.00},{OrderName: '給自己買手機',OrderDate: new Date('2024/05/13'),OrderAmount: 9999.00}]build() {Column() {// 標題Row() {Text('支付記錄').layoutWeight(1).textAlign(TextAlign.Center).margin({left: 30})}.width('100%').padding(16).border({width: {bottom: 1},color: '#f4f5f6'})// 列表Column() {// 要循環的結構體Column({ space: 20 }) {Text('給老婆買了一朵花').fontWeight(FontWeight.Bold).width('100%')Row() {Text('¥43.00').fontColor(Color.Red)Text('2024/5/11')}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.width('100%').padding(20)}.width('100%')}.width('100%').height('100%')}}
頁面中生成數據,用ForEach循環
ForEach(this.PayRecordList, (item: PayRecord) => {// 要循環的結構體Column({ space: 20 }) {Text(item.OrderName).fontWeight(FontWeight.Bold).width('100%')Row() {Text(`¥${item.OrderAmount}`).fontColor(Color.Red)Text(item.OrderDate.toLocaleDateString())}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.width('100%').padding(20)
})
interface PayRecord {OrderName:stringOrderDate:DateOrderAmount:number
}
@Entry
@ComponentV2
struct ForEachDemo2 {PayRecordList: PayRecord[] = [{OrderName: '給老婆買口紅',OrderDate: new Date('2024/05/11'),OrderAmount: 399.00},{OrderName: '給老婆買花',OrderDate: new Date('2024/05/12'),OrderAmount: 99.00},{OrderName: '給自己買手機',OrderDate: new Date('2024/05/13'),OrderAmount: 9999.00}]build() {Column() {// 標題Row() {Text('支付記錄').layoutWeight(1).textAlign(TextAlign.Center).margin({left: 30})}.width('100%').padding(16).border({width: {bottom: 1},color: '#f4f5f6'})// 列表Column() {ForEach(this.PayRecordList, (item: PayRecord) => {// 要循環的結構體Column({ space: 20 }) {Text(item.OrderName).fontWeight(FontWeight.Bold).width('100%')Row() {Text(`¥${item.OrderAmount}`).fontColor(Color.Red)Text(item.OrderDate.toLocaleDateString())}.width('100%').justifyContent(FlexAlign.SpaceBetween)}.width('100%').padding(20)})}.width('100%')}.width('100%').height('100%')}
}
2.1.4 key的推薦建議
在ForEach循環渲染過程中,系統會為每個數組元素生成一個唯一且持久的鍵值,用于標識對應的組件。當這個鍵值變化時,ArkUI框架將視為該數組元素已被替換或修改,并會基于新的鍵值創建一個新的組件。
ForEach提供了一個名為keyGenerator的參數,這是一個函數,開發者可以通過它自定義鍵值的生成規則。如果開發者沒有定義keyGenerator函數,則ArkUI框架會使用默認的鍵值生成函數,即(item: Object, index: number) => { return index + '__' + JSON.stringify(item); }。
ForEach的第三個屬性是一個回調,它是生成唯一key的
不傳的話會幫助我們生成獨一無二的key => index_ + JSON.stringify(item)
鴻蒙更新的原理:循環的比較-比較你的key存在不,如果存在相同的key,則不更新
只改動了某一條數據,可能所有列表都會更新
ForEach的第三個參數 寧可不給 也不要瞎給
- 下面是key的使用案例
interface Person {id: numbername: stringage: number
}@Entry@ComponentV2struct ForEachDemo3 {@Local heroList: Person[] = [{ id: 1, name: '呂布', age: 18 },{ id: 2, name: '張飛', age: 20 },{ id: 3, name: '貂蟬', age: 21 }]build() {Column() {Button() {Text('在第1項后插入新項').fontSize(30)}.onClick(() => {this.heroList.splice(1, 0, {id: Math.random(),name: '呂蒙',age: 20});})ForEach(this.heroList, (item: Person) => {ChildItem({ item: item })})}.justifyContent(FlexAlign.Center).width('100%').height('100%').backgroundColor(0xF1F3F5)}}@ComponentV2struct ChildItem {@Param item: Person = {} as Person;aboutToAppear(): void {console.log('我被渲染了', this.item.name)}build() {Text(this.item.name + this.item.age).fontSize(30)}}
會發現使用index作為key,一旦中間項變化,后面的所有項,都需要重新渲染更新,影響性能
轉而修改成以 id 作為 key,測試發現性能提升不少,只有對應項需要更新
interface Hero {id: numbername: stringage: number
}@Entry@ComponentV2struct ForEachDemo4 {@Local heroList: Hero[] = [{ id: 1, name: '呂布', age: 18 },{ id: 2, name: '張飛', age: 20 },{ id: 3, name: '貂蟬', age: 21 },]build() {Column() {Button() {Text('在第1項后插入新項').fontSize(30)}.onClick(() => {this.heroList.splice(1, 0, {id: Math.random(),name: '呂蒙',age: 20});})ForEach(this.heroList, (item: Hero) => {ChildItem({ item: item })}, (item: Hero) => item.id.toString())}.justifyContent(FlexAlign.Center).width('100%').height('100%').backgroundColor(0xF1F3F5)}}@ComponentV2struct ChildItem {@Param item: Hero = {} as Hero;aboutToAppear(): void {console.log('我被渲染了', this.item.name)}build() {Text(this.item.name + this.item.age).fontSize(30)}}
3. 下拉刷新+上拉加載
3.1 下拉刷新
Refresh組件支持下拉刷新,包裹List組件,下拉事件中更新列表
基礎結構
@Entry
@ComponentV2
struct RefreshCase {@Local list: number[] = Array(20).fill(Date.now())build() {Column() {List() {ForEach(this.list, (item: number) => {ListItem() {Row() {Text(item.toString())}.width('100%').padding(20).border({width: {bottom: 1},color: Color.Gray,})}})}}.height('100%').width('100%')}
}
添加Refresh組件
@Entry
@ComponentV2
struct RefreshCase2 {@Local list: number[] = Array(20).fill(Date.now())@Local refreshing: boolean = false@BuilderrefreshContent() {Text('正在加載中...').width('100%').textAlign(TextAlign.Center)}build() {Column() {Refresh({ refreshing: $$this.refreshing, builder: this.refreshContent }) {List() {ForEach(this.list, (item: number) => {ListItem() {Row() {Text(item.toString())}.width('100%').padding(20).border({width: {bottom: 1},color: Color.Gray,})}})}}.onRefreshing(() => {setTimeout(() => {this.list = Array(20).fill(Date.now())this.refreshing = false}, 1000)})}.height('100%').width('100%')}
}
3.2 上拉加載
滾動至列表尾部(會立刻觸發兩次,滾動到底部+回彈一下)
import { promptAction } from '@kit.ArkUI'@Entry@ComponentV2struct RefreshCase3 {@Local list: number[] = Array(20).fill(Date.now())@Local refreshing: boolean = false@Local isLoading: boolean = false // 標記是否正在加載中scroller: Scroller = new Scroller()@BuilderrefreshContent() {Text('正在加載中...').width('100%').textAlign(TextAlign.Center)}build() {Column() {Refresh({refreshing: $$this.refreshing,builder: this.refreshContent}) {List({ scroller: this.scroller }) {ForEach(this.list, (item: number) => {ListItem() {Row() {Text(item.toString())}.width('100%').padding(20).border({width: {bottom: 1},color: Color.Gray,})}})if (this.isLoading) {ListItem() {Text('正在拼命加載中...').width('100%').textAlign(TextAlign.Center).height(80)}}}.onReachEnd(() => {if (!this.isLoading) {this.isLoading = true // 開始加載, 顯示Loading動畫this.scroller.scrollEdge(Edge.End)setTimeout(() => {this.list.push(...Array(10).fill(Date.now()))promptAction.showToast({message: '10條數據添加成功'})this.isLoading = false}, 2000)}})}.onRefreshing(() => {setTimeout(() => {this.list = Array(20).fill(Date.now())this.refreshing = false}, 1000)})}.height('100%').width('100%')}}