OpenHarmony如何實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)
列表的二級(jí)聯(lián)動(dòng)(Cascading List)是指根據(jù)一個(gè)列表(一級(jí)列表)的選擇結(jié)果,來(lái)更新另一個(gè)列表(二級(jí)列表)的選項(xiàng)。這種聯(lián)動(dòng)可以使用戶根據(jù)實(shí)際需求,快速定位到想要的選項(xiàng),提高交互體驗(yàn)。例如,短視頻中拍攝風(fēng)格的選擇、照片編輯時(shí)的場(chǎng)景的選擇,本文即為大家介紹如何開發(fā)二級(jí)聯(lián)動(dòng)。
效果呈現(xiàn)
本例最終效果如下:
運(yùn)行環(huán)境
本例基于以下環(huán)境開發(fā),開發(fā)者也可以基于其他適配的版本進(jìn)行開發(fā):
- IDE: DevEco Studio 3.1 Beta2
- SDK: Ohos_sdk_public 3.2.11.9 (API Version 9 Release)
實(shí)現(xiàn)思路
- 數(shù)字標(biāo)題(titles)以及下方的數(shù)字列表(contents)分組展示:通過(guò)兩個(gè)List組件分別承載數(shù)字標(biāo)題和數(shù)字項(xiàng)。
- 滾動(dòng)數(shù)字列表,上方數(shù)字標(biāo)題也隨之變動(dòng):通過(guò)List組件的onScrollIndex事件獲取到當(dāng)前滾動(dòng)數(shù)字的索引,根據(jù)該索引計(jì)算出對(duì)應(yīng)標(biāo)題數(shù)字的索引,然后通過(guò)Scroller的scrollToIndex方法跳轉(zhuǎn)到對(duì)應(yīng)的數(shù)字標(biāo)題,且通過(guò)Line組件為選中的標(biāo)題添加下劃線。
- 點(diǎn)擊數(shù)字標(biāo)題,下方的數(shù)字列表也隨之變化:首先獲取到點(diǎn)擊數(shù)字標(biāo)題的索引,通過(guò)該索引計(jì)算出下方對(duì)應(yīng)數(shù)字的起始項(xiàng)索引,然后通過(guò)scroller的scrollToIndex方法跳轉(zhuǎn)到對(duì)應(yīng)索引的數(shù)字項(xiàng)。
開發(fā)步驟
根據(jù)實(shí)現(xiàn)思路,具體實(shí)現(xiàn)步驟如下:
首先構(gòu)建列表數(shù)據(jù),在records中記錄數(shù)字列表中各個(gè)數(shù)字的首項(xiàng)索引值,具體代碼塊如下:
...
@State typeIndex: number = 0
private tmp: number = 0
private titles: Array<string> = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
private contents: Array<string> = ["1", "1", "1", "1", "1", "1", "1", "1", "1", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "3"
, "3", "3", "3", "3", "4", "4", "4", "5", "5", "5", "5", "5", "6", "7", "7", "7", "7", "7", "7", "7", "7", "7", "7", "7", "7",
"8", "8", "8", "8", "8", "9", "9", "9", "9", "9", "9", "9", "9", "9", "9", "9"]
private records: Array<number> = [0, 9, 21, 26, 29, 34, 35, 47, 52, 63]
private classifyScroller: Scroller = new Scroller();
private scroller: Scroller = new Scroller();
...
數(shù)字標(biāo)題列表:具體代碼塊如下:
...
build() {
Column({ space: 0 }) {
List ({ space: 50, scroller: this.classifyScroller, initialIndex: 0 }) {
ForEach(this.titles, (item, index) => {
ListItem() {
Column() {
Text(item)
.fontSize(14)
...
}
}
}
...
}
.listDirection(Axis.Horizontal)
.height(50)
}
}
數(shù)字列表,具體代碼塊如下:
List({ space: 20, scroller: this.scroller }) {
ForEach(this.contents, (item, index) => {
ListItem() {
Column({ space: 5 }) {
Image($r("app.media.app_icon"))
.width(40)
.height(40)
Text(item)
.fontSize(12)
}
...
}
}
}
.listDirection(Axis.Horizontal) //列表排列方向水平
.edgeEffect(EdgeEffect.None) //不支持滑動(dòng)效果
數(shù)字標(biāo)題的索引值判斷,根據(jù)當(dāng)前滾動(dòng)數(shù)字的首項(xiàng)索引值計(jì)算出數(shù)字標(biāo)題的索引,具體代碼塊如下:
...
findClassIndex(ind: number) { // 當(dāng)前界面最左邊圖的索引值ind
let ans = 0
// 定義一個(gè)i 并進(jìn)行遍歷 this.records.length = 10
for (let i = 0; i < this.records.length; i++) {
// 判斷ind在this.records中那兩個(gè)臨近索引值之間
if (ind >= this.records[i] && ind < this.records[i + 1]) {
ans = i
break
}
}
return ans
}
findItemIndex(ind: number) {
// 將ind重新賦值給類型標(biāo)題列表的索引值
return this.records[ind]
}
...
通過(guò)Line組件構(gòu)成標(biāo)題下滑線,具體代碼塊如下:
...
if (this.typeIndex == index) {
Line()
//根據(jù)長(zhǎng)短判斷下劃線
.width(item.length === 2 ? 25 : item.length === 3 ? 35 : 50)
.height(3)
.strokeWidth(20)
.strokeLineCap(LineCapStyle.Round)
.backgroundColor('#ffcf9861')
}
...
點(diǎn)擊數(shù)字標(biāo)題,數(shù)字列表隨之滑動(dòng):首先獲取到點(diǎn)擊數(shù)字標(biāo)題的索引,通過(guò)該索引計(jì)算出下方對(duì)應(yīng)數(shù)字的起始項(xiàng)索引,然后通過(guò)scroller的scrollToIndex方法跳轉(zhuǎn)到對(duì)應(yīng)索引的數(shù)字項(xiàng),具體代碼塊如下:
...
.onClick(() => {
this.typeIndex = index
this.classifyScroller.scrollToIndex(index)
let itemIndex = this.findItemIndex(index)
console.log("移動(dòng)元素:" + itemIndex)
this.scroller.scrollToIndex(itemIndex)
})
...
數(shù)字列表的滑動(dòng)或點(diǎn)擊導(dǎo)致數(shù)字標(biāo)題的變動(dòng):通過(guò)List組件中onScrollIndex事件獲取的到屏幕中最左邊數(shù)字的索引值start,然后通過(guò)該索引值計(jì)算出對(duì)應(yīng)的數(shù)字標(biāo)題的索引currentClassIndex,然后通過(guò)scrollToIndex控制數(shù)字標(biāo)題跳轉(zhuǎn)到對(duì)應(yīng)索引處,具體代碼塊如下:
...
.onScrollIndex((start) => {
let currentClassIndex = this.findClassIndex(start)
console.log("找到的類索引為: " + currentClassIndex)
if (currentClassIndex != this.tmp) {
this.tmp = currentClassIndex
console.log("類別移動(dòng)到索引: " + currentClassIndex)
this.typeIndex = currentClassIndex
this.classifyScroller.scrollToIndex(currentClassIndex)
}
})
...
完整代碼
@Entry
@Component
struct TwoLevelLink {
@State typeIndex: number = 0
private tmp: number = 0
private titles: Array<string> = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
private contents: Array<string> = ["1", "1", "1", "1", "1", "1", "1", "1", "1", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "3"
, "3", "3", "3", "3", "4", "4", "4", "5", "5", "5", "5", "5", "6", "7", "7", "7", "7", "7", "7", "7", "7", "7", "7", "7", "7",
"8", "8", "8", "8", "8", "9", "9", "9", "9", "9", "9", "9", "9", "9", "9", "9"]
private colors: Array<string> = ["#18183C", "#E8A027", "#D4C3B3", "#A4AE77", "#A55D51", "#1F3B54", "#002EA6", "#FFE78F", "#FF770F"]
private records: Array<number> = [0, 9, 21, 26, 29, 34, 35, 47, 52, 63]
private classifyScroller: Scroller = new Scroller();
private scroller: Scroller = new Scroller();
// 根據(jù)數(shù)字列表索引計(jì)算對(duì)應(yīng)數(shù)字標(biāo)題的索引
findClassIndex(ind: number) {
let ans = 0
for (let i = 0; i < this.records.length; i++) {
if (ind >= this.records[i] && ind < this.records[i + 1]) {
ans = i
break
}
}
return ans
}
// 根據(jù)數(shù)字標(biāo)題索引計(jì)算對(duì)應(yīng)數(shù)字列表的索引
findItemIndex(ind: number) {
return this.records[ind]
}
build() {
Column({ space: 0 }) {
List ({ space: 50, scroller: this.classifyScroller, initialIndex: 0 }) {
ForEach(this.titles, (item, index) => {
ListItem() {
Column() {
Text(item)
.fontSize(24)
if (this.typeIndex == index) {
Line()
.width(item.length === 2 ? 25 : item.length === 3 ? 35 : 50)
.height(3)
.strokeWidth(20)
.strokeLineCap(LineCapStyle.Round)
.backgroundColor('#ffcf9861')
}
}
.onClick(() => {
this.typeIndex = index
this.classifyScroller.scrollToIndex(index)
let itemIndex = this.findItemIndex(index)
console.log("移動(dòng)元素:" + itemIndex)
this.scroller.scrollToIndex(itemIndex)
})
}
})
}
.listDirection(Axis.Horizontal)
.height(50)
List({ space: 20, scroller: this.scroller }) {
ForEach(this.contents, (item, index) => {
ListItem() {
Column({ space: 5 }) {
Text(item)
.fontSize(30)
.fontColor(Color.White)
}
.width(60)
.height(60)
.backgroundColor(this.colors[item-1])
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.scroller.scrollToIndex(index)
})
}
})
}
.listDirection(Axis.Horizontal) //列表排列方向水平
.edgeEffect(EdgeEffect.None) //不支持滑動(dòng)效果
.onScrollIndex((start) => {
let currentClassIndex = this.findClassIndex(start)
console.log("找到的類索引為: " + currentClassIndex)
if (currentClassIndex != this.tmp) {
this.tmp = currentClassIndex
console.log("類別移動(dòng)到索引: " + currentClassIndex)
this.typeIndex = currentClassIndex
this.classifyScroller.scrollToIndex(currentClassIndex)
}
})
}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })
}
}