1.Tabs(選項卡)
1.1 概述
Tabs組件的頁面組成包含兩個部分,分別是TabContent和TabBar。TabContent是內容頁,TabBar是導航頁簽欄。
TabBar是導航頁簽欄,頁面結構如下圖所示,根據不同的導航類型,布局會有區別,可以分為底部導航、頂部導航、側邊導航,其導航欄分別位于底部、頂部和側邊。
1.2 基本使用
Tabs使用花括號包裹TabContent,每一個TabContent對應的內容需要有一個頁簽,可以通過TabContent的tabBar屬性進行配置。代碼如下
Tabs() {TabContent() {Text('首頁的內容').fontSize(30)}.tabBar('首頁')TabContent() {Text('推薦的內容').fontSize(30)}.tabBar('推薦')TabContent() {Text('發現的內容').fontSize(30)}.tabBar('發現')TabContent() {Text('我的內容').fontSize(30)}.tabBar("我的")
}
1.3 導航位置
barPosition 屬性用來設置導航的位置,如下圖所示
說明
● vertical為false時,tabbar的寬度默認為撐滿屏幕的寬度,需要設置barWidth為合適值。
● vertical為true時,tabbar的高度默認為實際內容的高度,需要設置barHeight為合適值。
1.4 禁止滑動切換
控制滑動切換的屬性為scrollable,默認值為true,表示可以滑動,若要限制滑動切換頁簽則需要設置為false。
Tabs({barPosition: BarPosition.Start}){...
}.scrollable(false)
1.5 滾動導航欄
當導航頁簽欄選項比較多時,可以讓導航頁簽滾動
Tabs({ barPosition: BarPosition.Start }) {// TabContent的內容:關注、視頻、游戲、數碼、科技、體育、影視、人文、藝術、自然、軍事...
}
.barMode(BarMode.Scrollable)
1.6 自定義導航欄
對于底部導航欄,一般作為應用主頁面功能區分,為了更好的用戶體驗,會以“文字+圖標”表示頁簽內容,這種情況下,需要自定義導航頁簽的樣式。
1.6.1自定義導航頁簽
自定義導航每一個頁簽包含四部分內容
- 頁簽文字
- 頁簽未選中圖片
- 頁簽選中時圖片
- 頁簽的索引
創建一個@Builder tabBuilder(){…}函數,用于構建自定義導航頁簽內容。
/*
title: 頁簽標題
targetIndex: 頁簽的索引
selectedImg: 選中時圖標
normalImg: 未選中時
*/
//當前選中頁簽索引
@State currentIndex: number = 0
@Builder
tabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {Column() {Image(this.currentIndex == targetIndex ? selectedImg : normalImg).size({ width: 25, height: 25 })Text(title).fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')}.width('100%').height(50).justifyContent(FlexAlign.Center).onClick(() => {this.currentIndex = targetIndex})
}
在調用TabContent對應.tabBar(this.tabBuilder(…)),傳入相對應的參數。代碼如下
@Entry
@Component
struct TabsDemo {build() {Tabs({barPosition: BarPosition.End}){TabContent(){Text('首頁')}.tabBar(this.tabBuilder('首頁',0,$r('app.media.home_selected'),$r('app.media.home_normal')))TabContent(){Text('推薦')}.tabBar(this.tabBuilder('推薦',1,$r('app.media.home_selected'),$r('app.media.home_normal')))TabContent(){Text('熱門')}.tabBar(this.tabBuilder('熱門',2,$r('app.media.home_selected'),$r('app.media.home_normal')))TabContent(){Text('文化')}.tabBar(this.tabBuilder('文化',3,$r('app.media.home_selected'),$r('app.media.home_normal')))}})
}
如下圖所示,此時點擊頁簽,能夠實現選中與未選中的變色效果。
但是此時還有一個問題,點擊頁簽時并不能同步切換內容頁。
1.6.2 點擊頁簽同步切換內容頁
想要實現點擊頁簽同步切換頁面,需要讓Tabs與TabsController相關聯,TabsController是用來控制頁面切換的控制器,并調用TabsController的changeIndex(索引)切換內容頁。
- 創建TabController與Tabs關聯
tabsController: TabsController = new TabsController()build() {Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {... }
}
- 給自定義頁簽設置點擊事件,調用TabController的changeIndex(索引)切換頁簽。
1.6.3滑動內容頁同步切換頁簽
在自定義導航欄的情況下,滑動內容頁時,頁簽是不會同步切換的;此時需要監聽內容頁的改變,手動進行切換;
Tabs({ barPosition: BarPosition.End, controller: this.tabController }) {TabContent() {Text('首頁內容')}.tabBar(this.tabBuilder('首頁', 0, $r('app.media.home_selected'), $r('app.media.home_normal')))...
}.onChange((index) => {this.currentIndex = index
})
效果如下
2.List(列表)
2.1 基本概念
List列表是一種復雜容器,當列表項達到一定數量,內容超過屏幕大小時,可以自動提供滾動功能。使用列表可以輕松高效地顯示結構化、可滾動的信息。
2.2 基本使用
List和ListItem是結合起來使用的,List表示列表容器,而ListItem表示列表中的列表項。
如下圖所示,這是一個最簡單的列表,每一個列表項顯示一個文本
@Entry
@Component
struct CityList {build() {List() {ListItem() {Text('北京').fontSize(24)}ListItem() {Text('杭州').fontSize(24)}ListItem() {Text('上海').fontSize(24)}}.backgroundColor('#FFF1F3F5').alignListItem(ListItemAlign.Center)}
}
2.3 循環渲染
如果List種ListItem列表項非常多,肯定不能一個一個列舉,這個時候需要用到ForEach循環渲染。ForEach循環渲染的格式如下
ForEach(數據集;(元素)=>{//需要虛幻渲染的內容
})
比如我現在想要使用List列表顯示中國的所有省會城市。
@Entry
@Component
struct CityList {// 定義一個字符串數組,包含中國所有的省會城市citys: string[] = ["北京","上海","天津","重慶","哈爾濱", // 黑龍江省"長春", // 吉林省"沈陽", // 遼寧省"呼和浩特", // 內蒙古自治區"石家莊", // 河北省"太原", // 山西省"西安", // 陜西省"蘭州", // 甘肅省"西寧", // 青海省"銀川", // 寧夏回族自治區"烏魯木齊", // 新疆維吾爾自治區"南寧", // 廣西壯族自治區"廣州", // 廣東省"海口", // 海南省"成都", // 四川省"貴陽", // 貴州省"昆明", // 云南省"拉薩", // 西藏自治區"鄭州", // 河南省"濟南", // 山東省"南京", // 江蘇省"杭州", // 浙江省"合肥", // 安徽省"福州", // 福建省"臺北", // 臺灣省(注意:政治和實際情況可能有所不同)"南昌", // 江西省"長沙", // 湖南省"武漢",// 湖北省];build() {List() {ForEach(this.citys, (item: string) => {ListItem() {Text(item).fontSize(24)}})}.backgroundColor('#FFF1F3F5').alignListItem(ListItemAlign.Center)}
}
2.4 列表樣式
- 設置列表項間距
List({ space: 10 }) {// ...
}
- 設置列表項分割線
List() {// ...
}
.divider({strokeWidth: 0.5, //分割線的粗細color:Color.Black //分割線的顏色
})
- 設置滾動條
List() {// ...
}
.scrollBar(BarState.Auto) //默認不顯示,滾動時顯示,2秒后消失
修改上面的案例,給列表加入自定義樣式,代碼如下
List({ space: 20 }) {ForEach(this.citys, (item: string) => {ListItem() {Text(item).fontSize(24)}})}.backgroundColor('#FFF1F3F5').alignListItem(ListItemAlign.Center) //交叉軸居中顯示.divider({ //加入分割線strokeWidth:1, //線條粗細startMargin:20, //線條開始外邊距endMargin:20, //線條結尾外邊距color:'#dedede'}).scrollBar(BarState.Auto) //默認不顯示,滾動時顯示,2秒后消失
2.5 復雜列表
案例需求:如下圖所示是一個任務列表
- 每一個列表項包含:任務標題、任務創建時間、任務選中狀態
- 點擊添加時,添加列項到任務列表
- 點擊刪除時,將處于選中狀態的列表項從列表中刪除
- 點擊取消時,列表項處于不可編輯狀態(不顯示復選框);長按列表項時,列表項處于可編輯狀態(顯示復選框)
2.5.1復雜列表的設計思路
當一個列表項顯示的內容比較多時,一般會將列表項的UI和列表項的數據分離開來。如下圖所示,是一個任務列表的列表項;
左圖:用一個類來封裝列表項的數據;
右圖:將列表項的UI用自定義組件來表示;
合并:最后列表項的UI和數據綁定即可
多個列表項:采用ForEach循環渲染列表項就可以了
2.5.2 創建列表項數據模型
分析頁面發現,每一個ListItem擁有三個數據,分別是:標題、創建時間、選中狀態,根據編輯狀態決定是否顯示復選框。
class TargetItemData {title: string //目標標題createTime: string //創建時間checkStatus: boolean //選中狀態 //1未選中,2選中constructor(title: string //目標標題, checkStatus: boolean //選中狀態) {this.title = title;this.createTime = this.getCurrentTime();this.checkStatus = checkStatus;}//獲取當前系統時間getCurrentTime(): string {let date = new Date();let year = date.getFullYear();let month = date.getMonth() + 1;let day = date.getDate();let hours = date.getHours();let minutes = date.getMinutes().toString();if (Number.parseInt(minutes) < 10) {minutes = `0${minutes}`;}let second = date.getSeconds().toString();if (Number.parseInt(second) < 10) {second = `0${second}`;}return `${year}/${month}/${day} ${hours}:${minutes}:${second}`;}
}
2.5.3 創建列表項組件
分析頁面,每一個Item組件包括:一個標題Text、一個時間Text、一個復選框CheckBox;其中CheckBox根據狀態決定顯示或者不顯示。
@Entry
@Component
struct Index {//2.創建Item組件,并與Item數據模型綁定@State isEditModel: number = 1 //是否編輯(1未編輯、2編輯)@BuildertargetItem(data: TargetItemData) {Row() {Column() {Text(data.title).width('100%').fontSize(18)Text(`創建時間:${data.createTime}`).fontSize(12).fontColor('#A5A6AA').margin({ top: 8 })}.alignItems(HorizontalAlign.Start).layoutWeight(9)if (this.isEditModel == 2) {Checkbox().select(data.checkStatus).layoutWeight(1).onChange((status) => {data.checkStatus = status})}}.padding({left: 12, right: 12}).backgroundColor(Color.White).height(68).border({ radius: 15 }).margin({ left: 12, right: 12 })}build() {//...}
}
2.5.4 循環渲染列表項
分析主頁面的布局結構,如下圖所示,我們先把中間列表這部分寫出來,其他的先放一下后面再完成
//1.定義Item數據模型
//...@Entry
@Component
struct Index {//2.創建Item組件與Item數據模型綁定//...//3.創建Item數據集,并循環渲染到List容器中@State targets: Array<TargetItemData> = [new TargetItemData('運動', false),new TargetItemData('讀書', false),new TargetItemData('聽音樂', false),new TargetItemData('看電影', false),new TargetItemData('旅游', false)]build() {Column() {List({ space: 20 }) {ForEach(this.targets, (item: TargetItemData) => {ListItem() {this.targetItem(item)}})}.margin({ top: 20 })}.width('100%').height('100%').backgroundColor('#E9E9EB')}
}
2.5.5 給列表項添加長按事件
給ListItem添加長按事件,檢測到手指長按時改變Item狀態。
ListItem() {this.targetItem(item)
}.gesture(LongPressGesture({ duration: 500 }).onAction((event) => {this.isEditModel = 2 //編輯模式
}))
2.5.6 新增/刪除列表項
當點擊新增按鈕時,新增一個列表項;點擊刪除按鈕時,刪除選中的列表項。
@Entry
@Component
struct Index {//...@State isEditModel: number = 1 //是否編輯(1未編輯、2編輯)build() {Column() {List({ space: 20 }) {//...}.margin({ top: 20 }).layoutWeight(9) //權重9//操作按鈕Column() {if (this.isEditModel == 1) {Button('新增').width('80%').onClick(() => {this.targets.push(new TargetItemData('釣魚',false))})} else {Button('刪除').width('80%').onClick(() => {let leftData = this.targets.filter((item) => item.checkStatus == false) this.targets = leftData;this.isEditModel = 1 //設置為未編輯狀態})}}.width('100%').layoutWeight(1) //權重1.justifyContent(FlexAlign.Center)}.width('100%').height('100%').backgroundColor('#E9E9EB')}
}
3. Swiper(輪播)
3.1 基本概念
Swiper一般用來顯示輪播圖,在很多應用的首頁都會有輪播圖展示廣告或者一些重要的信息。如下圖所示,輪播展示幾張圖片。
3.2 基本使用
Swiper的基本使用也非常簡單,只需要以下兩個步驟即可
● 準備輪播數據(我這里就是4張圖片,如果每一個輪播數據比較多也可以封裝成對象)
● 循環渲染輪播數據
代碼如下
@Entry
@Component
struct Index {//1. 準備輪播數據swiperImages: Resource[] = [$r('app.media.fig1'),$r('app.media.fig2'),$r('app.media.fig3'),$r('app.media.fig4'),]build() {Column() {Swiper() {//2.循環輪播數據ForEach(this.swiperImages, (img: Resource) => {Image(img).borderRadius(16).width('100%')})}.margin({ top: 12 }).autoPlay(true) //自動播放}.margin({left: 12, right: 12})}
}
3.3 導航點指示器
Swiper(){ForEach(this.imageArray,(item:Resource)=>{Image(item).width('100%').height(200).borderRadius(10)})
}
//.indicator(false) true表示有導航點、false表示沒有導航點
.indicator(//Indicator.digit() //數字導航指示效果Indicator.dot() //原點導航點.itemWidth(20) //未選中的寬度.selectedItemWidth(20) //選中的寬度.color(Color.Red) //未選中的顏色.selectedColor(Color.Yellow) //選中的顏色
)