列表是一種復(fù)雜的容器,當(dāng)列表項(xiàng)達(dá)到一定數(shù)量,內(nèi)容超過屏幕大小時(shí),可以自動(dòng)提供滾動(dòng)功能。它適合用于呈現(xiàn)同類數(shù)據(jù)類型或數(shù)據(jù)類型集,例如圖片和文本。在列表中顯示數(shù)據(jù)集合是許多應(yīng)用程序中的常見要求(如通訊錄、音樂列表、購物清單等)。
使用列表可以輕松高效地顯示結(jié)構(gòu)化、可滾動(dòng)的信息。通過在List組件中按垂直或者水平方向線性排列子組件ListItemGroup或ListItem,為列表中的行或列提供單個(gè)視圖,或使用ForEach迭代一組行或列,或混合任意數(shù)量的單個(gè)視圖和ForEach結(jié)構(gòu),構(gòu)建一個(gè)列表。List組件支持使用條件渲染、循環(huán)渲染、懶加載等渲染控制方式生成子組件。
列表作為一種容器,會(huì)自動(dòng)按其滾動(dòng)方向排列子組件,向列表中添加組件或從列表中移除組件會(huì)重新排列子組件。
如下圖所示,在垂直列表中,List按垂直方向自動(dòng)排列ListItemGroup或ListItem。
ListItemGroup用于列表數(shù)據(jù)的分組展示,其子組件也是ListItem。ListItem表示單個(gè)列表項(xiàng),可以包含單個(gè)子組件。
List的子組件必須是ListItemGroup或ListItem,ListItem和ListItemGroup必須配合List來使用。
List除了提供垂直和水平布局能力、超出屏幕時(shí)可以滾動(dòng)的自適應(yīng)延伸能力之外,還提供了自適應(yīng)交叉軸方向上排列個(gè)數(shù)的布局能力。
利用垂直布局能力可以構(gòu)建單列或者多列垂直滾動(dòng)列表,如下圖所示。
利用水平布局能力可以是構(gòu)建單行或多行水平滾動(dòng)列表,如下圖所示。
列表的主軸方向是指子組件列的排列方向,也是列表的滾動(dòng)方向。垂直于主軸的軸稱為交叉軸,其方向與主軸方向相互垂直。
如下圖所示,垂直列表的主軸是垂直方向,交叉軸是水平方向。水平列表的主軸是水平方向,交叉軸是垂直方向。
如果List組件主軸或交叉軸方向設(shè)置了尺寸,則其對應(yīng)方向上的尺寸為設(shè)置值。
如果List組件主軸方向沒有設(shè)置尺寸,當(dāng)List子組件主軸方向總尺寸小于List的父組件尺寸時(shí),List主軸方向尺寸自動(dòng)適應(yīng)子組件的總尺寸。
如下圖所示,一個(gè)垂直列表B沒有設(shè)置高度時(shí),其父組件A高度為200vp,若其所有子組件C的高度總和為150vp,則此時(shí)列表B的高度為150vp。
如果子組件主軸方向總尺寸超過List父組件尺寸時(shí),List主軸方向尺寸適應(yīng)List的父組件尺寸。
如下圖所示,同樣是沒有設(shè)置高度的垂直列表B,其父組件A高度為200vp,若其所有子組件C的高度總和為300vp,則此時(shí)列表B的高度為200vp。
List組件交叉軸方向在沒有設(shè)置尺寸時(shí),其尺寸默認(rèn)自適應(yīng)父組件尺寸。
List組件主軸默認(rèn)是垂直方向,即默認(rèn)情況下不需要手動(dòng)設(shè)置List方向,就可以構(gòu)建一個(gè)垂直滾動(dòng)列表。
若是水平滾動(dòng)列表場景,將List的listDirection屬性設(shè)置為Axis.Horizontal即可實(shí)現(xiàn)。listDirection默認(rèn)為Axis.Vertical,即主軸默認(rèn)是垂直方向。
- List() {
- ...
- }
- .listDirection(Axis.Horizontal)
List組件的交叉軸布局可以通過lanes和alignListItem屬性進(jìn)行設(shè)置,lanes屬性用于確定交叉軸排列的列表項(xiàng)數(shù)量,alignListItem用于設(shè)置子組件在交叉軸方向的對齊方式。
List組件的lanes屬性通常用于在不同尺寸的設(shè)備自適應(yīng)構(gòu)建不同行數(shù)或列數(shù)的列表。lanes屬性的取值類型是"number | LengthConstrain",即整數(shù)或者LengthConstrain類型。以垂直列表為例,如果將lanes屬性設(shè)為2,表示構(gòu)建的是一個(gè)兩列的垂直列表,如圖2中右圖所示。lanes的默認(rèn)值為1,即默認(rèn)情況下,垂直列表的列數(shù)是1。
- List() {
- ...
- }
- .lanes(2)
當(dāng)其取值為LengthConstrain類型時(shí),表示會(huì)根據(jù)LengthConstrain與List組件的尺寸自適應(yīng)決定行或列數(shù)。
- List() {
- ...
- }
- .lanes({ minLength: 200, maxLength: 300 })
例如,假設(shè)在垂直列表中設(shè)置了lanes的值為{ minLength: 200, maxLength: 300 }。此時(shí),
同樣以垂直列表為例,當(dāng)alignListItem屬性設(shè)置為ListItemAlign.Center表示列表項(xiàng)在水平方向上居中對齊。alignListItem的默認(rèn)值是ListItemAlign.Start,即列表項(xiàng)在列表交叉軸方向上默認(rèn)按首部對齊。
- List() {
- ...
- }
- .alignListItem(ListItemAlign.Center)
列表視圖垂直或水平顯示項(xiàng)目集合,在行或列超出屏幕時(shí)提供滾動(dòng)功能,使其適合顯示大型數(shù)據(jù)集合。在最簡單的列表形式中,List靜態(tài)地創(chuàng)建其列表項(xiàng)ListItem的內(nèi)容。
- @Component
- struct CityList {
- build() {
- List() {
- ListItem() {
- Text('北京').fontSize(24)
- }
- ListItem() {
- Text('杭州').fontSize(24)
- }
- ListItem() {
- Text('上海').fontSize(24)
- }
- }
- .backgroundColor('#FFF1F3F5')
- .alignListItem(ListItemAlign.Center)
- }
- }
由于在ListItem中只能有一個(gè)根節(jié)點(diǎn)組件,不支持以平鋪形式使用多個(gè)組件。因此,若列表項(xiàng)是由多個(gè)組件元素組成的,則需要將這多個(gè)元素組合到一個(gè)容器組件內(nèi)或組成一個(gè)自定義組件。
如上圖所示,聯(lián)系人列表的列表項(xiàng)中,每個(gè)聯(lián)系人都有頭像和名稱。此時(shí),需要將Image和Text封裝到一個(gè)Row容器內(nèi)。
- List() {
- ListItem() {
- Row() {
- Image($r('app.media.iconE'))
- .width(40)
- .height(40)
- .margin(10)
- Text('小明')
- .fontSize(20)
- }
- }
- ListItem() {
- Row() {
- Image($r('app.media.iconF'))
- .width(40)
- .height(40)
- .margin(10)
- Text('小紅')
- .fontSize(20)
- }
- }
- }
通常更常見的是,應(yīng)用通過數(shù)據(jù)集合動(dòng)態(tài)地創(chuàng)建列表。使用循環(huán)渲染可從數(shù)據(jù)源中迭代獲取數(shù)據(jù),并在每次迭代過程中創(chuàng)建相應(yīng)的組件,降低代碼復(fù)雜度。
ArkTS通過ForEach提供了組件的循環(huán)渲染能力。以簡單形式的聯(lián)系人列表為例,將聯(lián)系人名稱和頭像數(shù)據(jù)以Contact類結(jié)構(gòu)存儲(chǔ)到contacts數(shù)組,使用ForEach中嵌套ListItem的形式來代替多個(gè)平鋪的、內(nèi)容相似的ListItem,從而減少重復(fù)代碼。
- import util from '@ohos.util';
- class Contact {
- key: string = util.generateRandomUUID(true);
- name: string;
- icon: Resource;
- constructor(name: string, icon: Resource) {
- this.name = name;
- this.icon = icon;
- }
- }
- @Entry
- @Component
- struct SimpleContacts {
- private contacts = [
- new Contact('小明', $r("app.media.iconA")),
- new Contact('小紅', $r("app.media.iconB")),
- ...
- ]
- build() {
- List() {
- ForEach(this.contacts, (item: Contact) => {
- ListItem() {
- Row() {
- Image(item.icon)
- .width(40)
- .height(40)
- .margin(10)
- Text(item.name).fontSize(20)
- }
- .width('100%')
- .justifyContent(FlexAlign.Start)
- }
- }, item => item.key)
- }
- .width('100%')
- }
- }
在List組件中,F(xiàn)orEach除了可以用來循環(huán)渲染ListItem,也可以用來循環(huán)渲染ListItemGroup。ListItemGroup的循環(huán)渲染詳細(xì)使用請參見支持分組列表。
在初始化列表時(shí),如需在列表項(xiàng)之間添加間距,可以使用space參數(shù)。例如,在每個(gè)列表項(xiàng)之間沿主軸方向添加10vp的間距:
- List({ space: 10 }) {
- ...
- }
分隔線用來將界面元素隔開,使單個(gè)元素更加容易識(shí)別。如下圖所示,當(dāng)列表項(xiàng)左邊有圖標(biāo)(如藍(lán)牙圖標(biāo)),由于圖標(biāo)本身就能很好的區(qū)分,此時(shí)分隔線從圖標(biāo)之后開始顯示即可。
List提供了divider屬性用于給列表項(xiàng)之間添加分隔線。在設(shè)置divider屬性時(shí),可以通過strokeWidth和color屬性設(shè)置分隔線的粗細(xì)和顏色。
startMargin和endMargin屬性分別用于設(shè)置分隔線距離列表側(cè)邊起始端的距離和距離列表側(cè)邊結(jié)束端的距離。
- List() {
- ...
- }
- .divider({
- strokeWidth: 1,
- startMargin: 60,
- endMargin: 10,
- color: '#ffe9f0f0'
- })
此示例表示從距離列表側(cè)邊起始端60vp開始到距離結(jié)束端10vp的位置,畫一條粗細(xì)為1vp的分割線,可以實(shí)現(xiàn)圖8設(shè)置列表分隔線的樣式。
1. 分隔線的寬度會(huì)使ListItem之間存在一定間隔,當(dāng)List設(shè)置的內(nèi)容間距小于分隔線寬度時(shí),ListItem之間的間隔會(huì)使用分隔線的寬度。
2. 當(dāng)List存在多列時(shí),分割線的startMargin和endMargin作用于每一列上。
3. List組件的分隔線畫在兩個(gè)ListItem之間,第一個(gè)ListItem上方和最后一個(gè)ListItem下方不會(huì)繪制分隔線。
當(dāng)列表項(xiàng)高度(寬度)超出屏幕高度(寬度)時(shí),列表可以沿垂直(水平)方向滾動(dòng)。在頁面內(nèi)容很多時(shí),若用戶需快速定位,可拖拽滾動(dòng)條,如下圖所示。
在使用List組件時(shí),可通過scrollBar屬性控制列表滾動(dòng)條的顯示。scrollBar的取值類型為BarState,當(dāng)取值為BarState.Auto表示按需顯示滾動(dòng)條。此時(shí),當(dāng)觸摸到滾動(dòng)條區(qū)域時(shí)顯示控件,可上下拖拽滾動(dòng)條快速瀏覽內(nèi)容,拖拽時(shí)會(huì)變粗。若不進(jìn)行任何操作,2秒后滾動(dòng)條自動(dòng)消失。
- List() {
- ...
- }
- .scrollBar(BarState.Auto)
在列表中支持?jǐn)?shù)據(jù)的分組展示,可以使列表顯示結(jié)構(gòu)清晰,查找方便,從而提高使用效率。分組列表在實(shí)際應(yīng)用中十分常見,如下圖所示聯(lián)系人列表。
在List組件中使用ListItemGroup對項(xiàng)目進(jìn)行分組,可以構(gòu)建二維列表。
在List組件中可以直接使用一個(gè)或者多個(gè)ListItemGroup組件,ListItemGroup的寬度默認(rèn)充滿List組件。在初始化ListItemGroup時(shí),可通過header參數(shù)設(shè)置列表分組的頭部組件。
- @Component
- struct ContactsList {
- ...
- @Builder itemHead(text: string) {
- // 列表分組的頭部組件,對應(yīng)聯(lián)系人分組A、B等位置的組件
- Text(text)
- .fontSize(20)
- .backgroundColor('#fff1f3f5')
- .width('100%')
- .padding(5)
- }
- build() {
- List() {
- ListItemGroup({ header: this.itemHead('A') }) {
- // 循環(huán)渲染分組A的ListItem
- ...
- }
- ...
- ListItemGroup({ header: this.itemHead('B') }) {
- // 循環(huán)渲染分組B的ListItem
- ...
- }
- ...
- }
- }
- }
如果多個(gè)ListItemGroup結(jié)構(gòu)類似,可以將多個(gè)分組的數(shù)據(jù)組成數(shù)組,然后使用ForEach對多個(gè)分組進(jìn)行循環(huán)渲染。例如在聯(lián)系人列表中,將每個(gè)分組的聯(lián)系人數(shù)據(jù)contacts(可參考迭代列表內(nèi)容章節(jié))和對應(yīng)分組的標(biāo)題title數(shù)據(jù)進(jìn)行組合,定義為數(shù)組contactsGroups。
- contactsGroups: object[] = [
- {
- title: 'A',
- contacts: [
- new Contact('艾佳', $r('app.media.iconA')),
- new Contact('安安', $r('app.media.iconB')),
- new Contact('Angela', $r('app.media.iconC')),
- ],
- },
- {
- title: 'B',
- contacts: [
- new Contact('白葉', $r('app.media.iconD')),
- new Contact('伯明', $r('app.media.iconE')),
- ],
- },
- ...
- ]
然后在ForEach中對contactsGroups進(jìn)行循環(huán)渲染,即可實(shí)現(xiàn)多個(gè)分組的聯(lián)系人列表。
- List() {
- // 循環(huán)渲染ListItemGroup,contactsGroups為多個(gè)分組聯(lián)系人contacts和標(biāo)題title的數(shù)據(jù)集合
- ForEach(this.contactsGroups, item => {
- ListItemGroup({ header: this.itemHead(item.title) }) {
- // 循環(huán)渲染ListItem
- ForEach(item.contacts, contact => {
- ListItem() {
- ...
- }
- }, item => item.key)
- }
- ...
- })
- }
粘性標(biāo)題是一種常見的標(biāo)題模式,常用于定位字母列表的頭部元素。如下圖所示,在聯(lián)系人列表中滾動(dòng)A部分時(shí),B部分開始的頭部元素始終處于A的下方。而在開始滾動(dòng)B部分時(shí),B的頭部會(huì)固定在屏幕頂部,直到所有B的項(xiàng)均完成滾動(dòng)后,才被后面的頭部替代。
粘性標(biāo)題不僅有助于闡明列表中數(shù)據(jù)的表示形式和用途,還可以幫助用戶在大量信息中進(jìn)行數(shù)據(jù)定位,從而避免用戶在標(biāo)題所在的表的頂部與感興趣區(qū)域之間反復(fù)滾動(dòng)。
List組件的sticky屬性配合ListItemGroup組件使用,用于設(shè)置ListItemGroup中的頭部組件是否呈現(xiàn)吸頂效果或者尾部組件是否呈現(xiàn)吸底效果。
通過給List組件設(shè)置sticky屬性為StickyStyle.Header,即可實(shí)現(xiàn)列表的粘性標(biāo)題效果。如果需要支持吸底效果,可以通過footer參數(shù)初始化ListItemGroup的底部組件,并將sticky屬性設(shè)置為StickyStyle.Footer。
- @Component
- struct ContactsList {
- // 定義分組聯(lián)系人數(shù)據(jù)集合contactsGroups數(shù)組
- ...
- @Builder itemHead(text: string) {
- // 列表分組的頭部組件,對應(yīng)聯(lián)系人分組A、B等位置的組件
- Text(text)
- .fontSize(20)
- .backgroundColor('#fff1f3f5')
- .width('100%')
- .padding(5)
- }
- build() {
- List() {
- // 循環(huán)渲染ListItemGroup,contactsGroups為多個(gè)分組聯(lián)系人contacts和標(biāo)題title的數(shù)據(jù)集合
- ForEach(this.contactsGroups, item => {
- ListItemGroup({ header: this.itemHead(item.title) }) {
- // 循環(huán)渲染ListItem
- ForEach(item.contacts, contact => {
- ListItem() {
- ...
- }
- }, item => item.key)
- }
- ...
- })
- }
- .sticky(StickyStyle.Header) // 設(shè)置吸頂,實(shí)現(xiàn)粘性標(biāo)題效果
- }
- }
控制滾動(dòng)位置在實(shí)際應(yīng)用中十分常見,例如當(dāng)新聞頁列表項(xiàng)數(shù)量龐大,用戶滾動(dòng)列表到一定位置時(shí),希望快速滾動(dòng)到列表底部或返回列表頂部。此時(shí),可以通過控制滾動(dòng)位置來實(shí)現(xiàn)列表的快速定位,如下圖所示。
List組件初始化時(shí),可以通過scroller參數(shù)綁定一個(gè)Scroller對象,進(jìn)行列表的滾動(dòng)控制。例如,用戶在新聞應(yīng)用中,點(diǎn)擊新聞頁面底部的返回頂部按鈕時(shí),就可以通過Scroller對象的scrollToIndex方法使列表滾動(dòng)到指定的列表項(xiàng)索引位置。
首先,需要?jiǎng)?chuàng)建一個(gè)Scroller的對象listScroller。
- private listScroller: Scroller = new Scroller();
然后,通過將listScroller用于初始化List組件的scroller參數(shù),完成listScroller與列表的綁定。在需要跳轉(zhuǎn)的位置指定scrollToIndex的參數(shù)為0,表示返回列表頂部。
- Stack({ alignContent: Alignment.BottomEnd }) {
- // 將listScroller用于初始化List組件的scroller參數(shù),完成listScroller與列表的綁定。
- List({ space: 20, scroller: this.listScroller }) {
- ...
- }
- ...
- Button() {
- ...
- }
- .onClick(() => {
- // 點(diǎn)擊按鈕時(shí),指定跳轉(zhuǎn)位置,返回列表頂部
- this.listScroller.scrollToIndex(0)
- })
- ...
- }
許多應(yīng)用需要監(jiān)聽列表的滾動(dòng)位置變化并作出響應(yīng)。例如,在聯(lián)系人列表滾動(dòng)時(shí),如果跨越了不同字母開頭的分組,則側(cè)邊字母索引欄也需要更新到對應(yīng)的字母位置。
除了字母索引之外,滾動(dòng)列表結(jié)合多級分類索引在應(yīng)用開發(fā)過程中也很常見,例如購物應(yīng)用的商品分類頁面,多級分類也需要監(jiān)聽列表的滾動(dòng)位置。
如上圖所示,當(dāng)聯(lián)系人列表從A滾動(dòng)到B時(shí),右側(cè)索引欄也需要同步從選中A狀態(tài)變成選中B狀態(tài)。此場景可以通過監(jiān)聽List組件的onScrollIndex事件來實(shí)現(xiàn),右側(cè)索引欄需要使用字母表索引組件AlphabetIndexer。
在列表滾動(dòng)時(shí),根據(jù)列表此時(shí)所在的索引值位置firstIndex,重新計(jì)算字母索引欄對應(yīng)字母的位置selectedIndex。由于AlphabetIndexer組件通過selected屬性設(shè)置了選中項(xiàng)索引值,當(dāng)selectedIndex變化時(shí)會(huì)觸發(fā)AlphabetIndexer組件重新渲染,從而顯示為選中對應(yīng)字母的狀態(tài)。
- ...
- const alphabets = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
- 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
- @Entry
- @Component
- struct ContactsList {
- @State selectedIndex: number = 0;
- private listScroller: Scroller = new Scroller();
- ...
- build() {
- Stack({ alignContent: Alignment.End }) {
- List({ scroller: this.listScroller }) {
- ...
- }
- .onScrollIndex((firstIndex: number) => {
- this.selectedIndex = firstIndex
- // 根據(jù)列表滾動(dòng)到的索引值,重新計(jì)算對應(yīng)聯(lián)系人索引欄的位置this.selectedIndex
- ...
- })
- ...
- // 字母表索引組件
- AlphabetIndexer({ arrayValue: alphabets, selected: 0 })
- .selected(this.selectedIndex)
- ...
- }
- }
- }
計(jì)算索引值時(shí),ListItemGroup作為一個(gè)整體占一個(gè)索引值,不計(jì)算ListItemGroup內(nèi)部ListItem的索引值。
側(cè)滑菜單在許多應(yīng)用中都很常見。例如,通訊類應(yīng)用通常會(huì)給消息列表提供側(cè)滑刪除功能,即用戶可以通過向左側(cè)滑列表的某一項(xiàng),再點(diǎn)擊刪除按鈕刪除消息,如下圖所示。
ListItem的swipeAction屬性可用于實(shí)現(xiàn)列表項(xiàng)的左右滑動(dòng)功能。swipeAction屬性方法初始化時(shí)有必填參數(shù)SwipeActionOptions,其中,start參數(shù)表示設(shè)置列表項(xiàng)右滑時(shí)起始端滑出的組件,end參數(shù)表示設(shè)置列表項(xiàng)左滑時(shí)尾端滑出的組件。
在消息列表中,end參數(shù)表示設(shè)置ListItem左滑時(shí)尾端劃出自定義組件,即刪除按鈕。在初始化end方法時(shí),將滑動(dòng)列表項(xiàng)的索引傳入刪除按鈕組件,當(dāng)用戶點(diǎn)擊刪除按鈕時(shí),可以根據(jù)索引值來刪除列表項(xiàng)對應(yīng)的數(shù)據(jù),從而實(shí)現(xiàn)側(cè)滑刪除功能。
- @Entry
- @Component
- struct MessageList {
- @State messages: object[] = [
- // 初始化消息列表數(shù)據(jù)
- ...
- ];
- @Builder itemEnd(index: number) {
- // 側(cè)滑后尾端出現(xiàn)的組件
- Button({ type: ButtonType.Circle }) {
- Image($r('app.media.ic_public_delete_filled'))
- .width(20)
- .height(20)
- }
- .onClick(() => {
- this.messages.splice(index, 1);
- })
- ...
- }
- build() {
- ...
- List() {
- ForEach(this.messages, (item, index) => {
- ListItem() {
- ...
- }
- .swipeAction({ end: this.itemEnd.bind(this, index) }) // 設(shè)置側(cè)滑屬性
- }, item => item.id.toString())
- }
- ...
- }
- }
添加標(biāo)記是一種無干擾性且直觀的方法,用于顯示通知或?qū)⒆⒁饬械綉?yīng)用內(nèi)的某個(gè)區(qū)域。例如,當(dāng)消息列表接收到新消息時(shí),通常對應(yīng)的聯(lián)系人頭像的右上方會(huì)出現(xiàn)標(biāo)記,提示有若干條未讀消息,如下圖所示。
在ListItem中使用Badge組件可實(shí)現(xiàn)給列表項(xiàng)添加標(biāo)記功能。Badge是可以附加在單個(gè)組件上用于信息標(biāo)記的容器組件。
在消息列表中,若希望在聯(lián)系人頭像右上角添加標(biāo)記,可在實(shí)現(xiàn)消息列表項(xiàng)ListItem的聯(lián)系人頭像時(shí),將頭像Image組件作為Badge的子組件。
在Badge組件中,count和position參數(shù)用于設(shè)置需要展示的消息數(shù)量和提示點(diǎn)顯示位置,還可以通過style參數(shù)靈活設(shè)置標(biāo)記的樣式。
- Badge({
- count: 1,
- position: BadgePosition.RightTop,
- style: { badgeSize: 16, badgeColor: '#FA2A2D' }
- }) {
- // Image組件實(shí)現(xiàn)消息聯(lián)系人頭像
- ...
- }
- ...
頁面的下拉刷新與上拉加載功能在移動(dòng)應(yīng)用中十分常見,例如,新聞頁面的內(nèi)容刷新和加載。這兩種操作的原理都是通過響應(yīng)用戶的觸摸事件,在頂部或者底部顯示一個(gè)刷新或加載視圖,完成后再將此視圖隱藏。
以下拉刷新為例,其實(shí)現(xiàn)主要分成三步:
下拉刷新與上拉加載的具體實(shí)現(xiàn)可參考相關(guān)實(shí)例中新聞數(shù)據(jù)加載。若開發(fā)者希望快速實(shí)現(xiàn)此功能,也可使用三方組件PullToRefresh。
列表的編輯模式用途十分廣泛,常見于待辦事項(xiàng)管理、文件管理、備忘錄的記錄管理等應(yīng)用場景。在列表的編輯模式下,新增和刪除列表項(xiàng)是最基礎(chǔ)的功能,其核心是對列表項(xiàng)對應(yīng)的數(shù)據(jù)集合進(jìn)行數(shù)據(jù)添加和刪除。
下面以待辦事項(xiàng)管理為例,介紹如何快速實(shí)現(xiàn)新增和刪除列表項(xiàng)功能。
如下圖所示,當(dāng)用戶點(diǎn)擊添加按鈕時(shí),提供用戶新增列表項(xiàng)內(nèi)容選擇或填寫的交互界面,用戶點(diǎn)擊確定后,列表中新增對應(yīng)的項(xiàng)目。
添加列表項(xiàng)功能實(shí)現(xiàn)主要流程如下:
- import util from '@ohos.util';
- export class ToDo {
- key: string = util.generateRandomUUID(true);
- name: string;
- constructor(name: string) {
- this.name = name;
- }
- }
- @State toDoData: ToDo[] = [];
- private availableThings: string[] = ['讀書', '運(yùn)動(dòng)', '旅游', '聽音樂', '看電影', '唱歌'];
最后,構(gòu)建列表布局和列表項(xiàng):
- List({ space: 10 }) {
- ForEach(this.toDoData, (toDoItem) => {
- ListItem() {
- ...
- }
- }, toDoItem => toDoItem.key)
- }
- Text('+')
- .onClick(() => {
- TextPickerDialog.show({
- range: this.availableThings,
- onAccept: (value: TextPickerResult) => {
- this.toDoData.push(new ToDo(this.availableThings[value.index])); // 新增列表項(xiàng)數(shù)據(jù)toDoData
- },
- })
- })
如下圖所示,當(dāng)用戶長按列表項(xiàng)進(jìn)入刪除模式時(shí),提供用戶刪除列表項(xiàng)選擇的交互界面,用戶勾選完成后點(diǎn)擊刪除按鈕,列表中刪除對應(yīng)的項(xiàng)目。
刪除列表項(xiàng)功能實(shí)現(xiàn)主要流程如下:
- // ToDoListItem.ets
- Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
- ...
- }
- .gesture(
- GestureGroup(GestureMode.Exclusive,
- LongPressGesture()
- .onAction(() => {
- if (!this.isEditMode) {
- this.isEditMode = true; //進(jìn)入編輯模式
- this.selectedItems.push(this.toDoItem); // 記錄長按時(shí)選中的列表項(xiàng)
- }
- })
- )
- )
- // ToDoListItem.ets
- if (this.isEditMode) {
- Checkbox()
- .onChange((isSelected) => {
- if (isSelected) {
- this.selectedItems.push(this.toDoItem) // 勾選時(shí),記錄選中的列表項(xiàng)
- } else {
- let index = this.selectedItems.indexOf(this.toDoItem)
- if (index !== -1) {
- this.selectedItems.splice(index, 1) // 取消勾選時(shí),則將此項(xiàng)從selectedItems中刪除
- }
- }
- })
- ...
- }
- // ToDoList.ets
- Button('刪除')
- .onClick(() => {
- // 刪除選中的列表項(xiàng)對應(yīng)的toDoData數(shù)據(jù)
- let leftData = this.toDoData.filter((item) => {
- return this.selectedItems.find((selectedItem) => selectedItem !== item);
- })
- this.toDoData = leftData;
- this.isEditMode = false;
- })
- ...
循環(huán)渲染適用于短列表,當(dāng)構(gòu)建具有大量列表項(xiàng)的長列表時(shí),如果直接采用循環(huán)渲染方式,會(huì)一次性加載所有的列表元素,會(huì)導(dǎo)致頁面啟動(dòng)時(shí)間過長,影響用戶體驗(yàn)。因此,推薦使用數(shù)據(jù)懶加載(LazyForEach)方式實(shí)現(xiàn)按需迭代加載數(shù)據(jù),從而提升列表性能。
關(guān)于長列表按需加載優(yōu)化的具體實(shí)現(xiàn)可參考數(shù)據(jù)懶加載章節(jié)中的示例。
當(dāng)使用懶加載方式渲染列表時(shí),為了更好的列表滾動(dòng)體驗(yàn),減少列表滑動(dòng)時(shí)出現(xiàn)白塊,List組件提供了cachedCount參數(shù)用于設(shè)置列表項(xiàng)緩存數(shù),只在懶加載LazyForEach中生效。
- List() {
- LazyForEach(this.dataSource, item => {
- ListItem() {
- ...
- }
- })
- }.cachedCount(3)
以垂直列表為例:
更多建議: