• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Creating a List (List)
2
3
4## Overview
5
6A list is a container that displays a collection of items. If the list items go beyond the screen, the list can scroll to reveal the content off the screen. A list is applicable for presenting similar data types or data type sets, such as images and text. For example, it can be used to present a collection of contacts, songs, and items to shop.
7
8Use lists to easily and efficiently display structured, scrollable information. You can provide a single view of rows or columns by arranging the [\<ListItemGroup>](../reference/arkui-ts/ts-container-listitemgroup.md) or [\<ListItem>](../reference/arkui-ts/ts-container-listitem.md) child components linearly in a vertical or horizontal direction in the [\<List>](../reference/arkui-ts/ts-container-list.md) component, or use [ForEach](../quick-start/arkts-rendering-control-foreach.md) to iterate over a group of rows or columns, or mix any number of single views and **ForEach** structures to build a list. The **\<List>** component supports the generation of child components in various [rendering](../quick-start/arkts-rendering-control-overview.md) modes, including conditional rendering, rendering of repeated content, and lazy data loading.
9
10
11## Layout and Constraints
12
13A list automatically arranges child components in the direction it scrolls. Adding or removing child components from the list will trigger re-arrangement of the child components.
14
15As shown in the following figure, in a vertical list, **\<ListItemGroup>** or **\<ListItem>** components are automatically arranged vertically.
16
17**\<ListItemGroup>** is used to display list data by group. Its child component is also **\<ListItem>**. **\<ListItem>** represents a list item, which can contain a single child component.
18
19  **Figure 1** Relationships between \<List>, \<ListItemGroup>, and \<ListItem>
20
21![en-us_image_0000001562940589](figures/en-us_image_0000001562940589.png)
22
23>**NOTE**
24>
25>A **\<List>** component can contain only **\<ListItemGroup>** or **\<ListItem>** as its child components. **\<ListItemGroup>** and **\<ListItem>** must be used together with **\<List>**.
26
27
28### Layout
29
30Apart from the aforementioned features, the list is also able to adapt to the number of elements in the cross axis direction.
31
32When used in vertical layout, the list can contain one or more scrollable columns, as shown below.
33
34  **Figure 2** Vertical scrolling list (left: one column; right: multiple columns)
35
36![en-us_image_0000001511580940](figures/en-us_image_0000001511580940.png)
37
38When used in horizontal layout, the list can contain one or more scrollable rows, as shown below.
39
40  **Figure 3** Horizontal scrolling list (left: one column; right: multiple columns)
41
42![en-us_image_0000001511421344](figures/en-us_image_0000001511421344.png)
43
44
45### Constraints
46
47The main axis direction of a list refers to the direction in which the child component columns are laid out and in which the list scrolls. An axis perpendicular to the main axis is referred to as a cross axis, and the direction of the cross axis is perpendicular to a direction of the main axis.
48
49As shown below, the main axis of a vertical list is in the vertical direction, and the cross axis is in the horizontal direction. The main axis of a horizontal list is in the horizontal direction, and the cross axis is in the vertical direction.
50
51  **Figure 4** Main axis and cross axis of the list
52
53![en-us_image_0000001562940581](figures/en-us_image_0000001562940581.png)
54
55If a size is set for the main axis or cross axis of the **\<List>** component, it is used as the size of the component in the corresponding direction.
56
57If no size is set for the main axis of the **\<List>** component, the size of the **\<List>** component in the main axis direction automatically adapts to the total size of its child components, as long as the total size of the child components in the main axis direction does not exceed the size of the parent component of **\<List>**.
58
59In the example shown below, no height is set for vertical list B, and the height of its parent component A is 200 vp. If the total height of all child components C is 150 vp, the height of list B is 150 vp.
60
61  **Figure 5** Main axis height constraint example 1 (A: parent component of \<List>; B: \<List> component; C: all child components of \<List>)
62
63![en-us_image_0000001511580956](figures/en-us_image_0000001511580956.png)
64
65If the total size of the child components in the main axis direction is greater than the size of the parent component of **\<List>**, the size of the **\<List>** component in the main axis direction automatically adapts to the size of its parent component.
66
67In the example shown below, still no height is set for vertical list B, and the height of its parent component A is 200 vp. If the total height of all child components C is 300 vp, the height of list B is 200 vp.
68
69  **Figure 6** Main axis height constraint example 2 (A: parent component of \<List>; B: \<List> component; C: all child components of \<List>)
70
71![en-us_image_0000001511740548](figures/en-us_image_0000001511740548.png)
72
73If no size is set for the cross axis of the **\<List>** component, the size of the **\<List>** component in the cross axis direction automatically adapts to the size of its parent component.
74
75
76## Developing the Layout
77
78
79### Setting the Main Axis Direction
80
81By default, the main axis of the **\<List>** component runs in the vertical direction. This means that you can create a vertical scrolling list without the need to manually set the list direction.
82
83To create a horizontal scrolling list, set the **listDirection** attribute to **Axis.Horizontal**. The default value of **listDirection** is **Axis.Vertical**.
84
85
86```ts
87List() {
88  ...
89}
90.listDirection(Axis.Horizontal)
91```
92
93
94### Setting the Cross Axis Layout
95
96The cross axis layout of the **\<List>** component can be set using the **lanes** and **alignListItem** attributes. The **lanes** attribute controls the number of list items along the cross axis, and the **alignListItem** attribute controls the alignment mode of child components along the cross axis.
97
98The lanes attribute of the **\<List>** component is useful in building a list that auto-adapts the numbers of rows or columns on devices of different sizes. Its value type is number or [LengthConstrain](../reference/arkui-ts/ts-types.md#lengthconstrain). If you are building a two-column vertical list shown on the right in Figure 2, set the **lanes** attribute to **2**. The default value of **lanes** is **1**.
99
100
101```ts
102List() {
103  ...
104}
105.lanes(2)
106```
107
108If set to a value of the LengthConstrain type, the **lanes** attribute determines the number of rows or columns based on the LengthConstrain settings and the size of the **\<List>** component.
109
110
111```ts
112let mn:LengthConstrain = { 'minLength': 200,'maxLength': 300}
113List() {
114  ...
115}
116.lanes(mn)
117```
118
119For example, if the **lanes** attribute is set to **{ minLength: 200, maxLength: 300 }** for a vertical list, then:
120
121- When the list width is 300 vp, the list contains one column, because **minLength** is 200 vp.
122
123- When the list width changes to 400 vp, which is twice that of the **minLength** value, the list is automatically adapted to two-column.
124
125>**NOTE**
126>
127>When the **lanes** attribute is set to a value of the LengthConstrain type, the value is used only to calculate the number of rows or columns in the list and does not affect the size of the list items.
128
129With regard to a vertical list, when the **alignListItem** attribute is set to **ListItemAlign.Center**, list items are center-aligned horizontally; when the **alignListItem** attribute is at its default value **ListItemAlign.Start**, list items are aligned toward the start edge of the cross axis in the list.
130
131
132```ts
133List() {
134  ...
135}
136.alignListItem(ListItemAlign.Center)
137```
138
139
140## Displaying Data in the List
141
142The list displays a collection of items horizontally or vertically and can scroll to reveal content off the screen. In the simplest case, a **\<List>** component is statically made up of **\<ListItem>** components.
143
144  **Figure 7** Example of a city list
145
146![en-us_image_0000001563060761](figures/en-us_image_0000001563060761.png)
147
148```ts
149@Entry
150@Component
151struct CityList {
152  build() {
153    List() {
154      ListItem() {
155        Text('Beijing').fontSize(24)
156      }
157
158      ListItem() {
159        Text('Hangzhou').fontSize(24)
160      }
161
162      ListItem() {
163        Text('Shanghai').fontSize(24)
164      }
165    }
166    .backgroundColor('#FFF1F3F5')
167    .alignListItem(ListItemAlign.Center)
168  }
169}
170```
171
172Each **\<ListItem>** component can contain only one root child component. Therefore, it does not allow use of child components in tile mode. If tile mode is required, you need to encapsulate the child components into a container or create a custom component.
173
174  **Figure 8** Example of a contacts list
175
176![en-us_image_0000001511421328](figures/en-us_image_0000001511421328.png)
177
178As shown above, as a list item, each contact has a profile picture and a name. To present it, you can encapsulate **\<Image>** and **\<Text>** components into a **\<Row>** container.
179
180
181```ts
182List() {
183  ListItem() {
184    Row() {
185      Image($r('app.media.iconE'))
186        .width(40)
187        .height(40)
188        .margin(10)
189
190      Text ('Tom')
191        .fontSize(20)
192    }
193  }
194
195  ListItem() {
196    Row() {
197      Image($r('app.media.iconF'))
198        .width(40)
199        .height(40)
200        .margin(10)
201
202      Text ('Tracy')
203        .fontSize(20)
204    }
205  }
206}
207```
208
209
210## Iterating List Content
211
212Compared with a static list, a dynamic list is more common in applications. You can use [ForEach](../quick-start/arkts-rendering-control-foreach.md) to obtain data from the data source and create components for each data item.
213
214 For example, when creating a contacts list, you can store the contact name and profile picture data in a **Contact** class structure to the **contacts** array, and nest **ListItem**s in **ForEach**, thereby reducing repeated code needed for tiling similar list items.
215
216
217```ts
218import util from '@ohos.util';
219
220class Contact {
221  key: string = util.generateRandomUUID(true);
222  name: string;
223  icon: Resource;
224
225  constructor(name: string, icon: Resource) {
226    this.name = name;
227    this.icon = icon;
228  }
229}
230
231@Entry
232@Component
233struct SimpleContacts {
234  private contacts:Array<object> = [
235    new Contact ('Tom', $r ("app.media.iconA")),
236    new Contact ('Tracy', $r ("app.media.iconB")),
237  ]
238
239  build() {
240    List() {
241      ForEach(this.contacts, (item: Contact) => {
242        ListItem() {
243          Row() {
244            Image(item.icon)
245              .width(40)
246              .height(40)
247              .margin(10)
248            Text(item.name).fontSize(20)
249          }
250          .width('100%')
251          .justifyContent(FlexAlign.Start)
252        }
253      }, (item:Contact) => item.key.toString())
254    }
255    .width('100%')
256  }
257}
258```
259
260In the **\<List>** component, **ForEach** can be used to render **\<ListItemGroup>** items as well as **\<ListItem>** items. For details, see [Adding Grouping Support](#adding-grouping-support).
261
262
263## Customizing the List Style
264
265
266### Setting the Spacing
267
268When initializing a list, you can use the **space** parameter to add spacing between list items. In the following example, a 10vp spacing is added between list items along the main axis:
269
270
271```ts
272List({ space: 10 }) {
273  ...
274}
275```
276
277
278### Adding Dividers
279
280A divider separates UI items to make them easier to identify. In the following figure, a divider is added between the setting items. Note that since the icons are easy to identify in their own right, the divers do not extend below the icons.
281
282  **Figure 9** Using dividers between the setting items
283
284![en-us_image_0000001511580960](figures/en-us_image_0000001511580960.png)
285
286To add dividers between items in a **\<List>** component, you can use its **divider** attribute, sprucing up the dividers with the following style attributes:<br> **strokeWidth** and **color**: indicate the stroke width and color of the diver, respectively.
287
288**startMargin** and **endMargin**: indicate the distance between the divider and the start edge and end edge of the list, respectively.
289
290
291```ts
292class dividerTmp{
293  strokeWidth: Length = 1
294  startMargin: Length = 60
295  endMargin: Length = 10
296  color: ResourceColor ='#ffe9f0f0'
297
298  constructor(strokeWidth: Length,startMargin: Length,endMargin: Length,color: ResourceColor) {
299    this.strokeWidth = strokeWidth
300    this.startMargin = startMargin
301    this.endMargin = endMargin
302    this.color = color
303  }
304}
305let opt:dividerTmp = new dividerTmp(1,60,10,'#ffe9f0f0')
306List() {
307  ...
308}
309.divider(opt)
310```
311
312This example draws a divider with a stroke thickness of 1 vp from a position 60 vp away from the start edge of the list to a position 10 vp away from the end edge of the list. The effect is shown in Figure 9.
313
314>**NOTE**
315>
316>1. The stroke width of the divider causes some space between list items. If the content spacing set for the list is smaller than the stroke width of the divider, the latter is used instead.
317>
318>2. When a list contains multiple columns, the **startMargin** and **endMargin** attributes of the divider apply to each column.
319>
320>3. The divider is drawn between list items. No divider is drawn above the first list item and below the last list item.
321
322
323### Adding a Scrollbar
324
325When the total height (width) of list items exceeds the screen height (width), the list can scroll vertically (horizontally). The scrollbar of a list enables users to quickly navigate the list content, as shown below.
326
327  **Figure 10** Scrollbar of a list
328
329![en-us_image_0000001511740544](figures/en-us_image_0000001511740544.gif)
330
331When using the **\<List>** component, you can use the **scrollBar** attribute to control the display of the list scrollbar. The value type of **scrollBar** is [BarState](../reference/arkui-ts/ts-appendix-enums.md#barstate). When the value is **BarState.Auto**, the scrollbar is displayed as required: It is displayed when the scrollbar area is touched and becomes thicker when being dragged; it automatically disappears after 2 seconds of inactivity.
332
333
334```ts
335List() {
336  ...
337}
338.scrollBar(BarState.Auto)
339```
340
341
342## Adding Grouping Support
343
344By allowing data to be displayed in groups in the list, you make the list easier to scan and navigate. Grouping is common in real-world applications. For example, the contacts list below use grouping.
345
346  **Figure 11** Contacts list with grouping
347
348![en-us_image_0000001511580948](figures/en-us_image_0000001511580948.png)
349
350You can use **\<ListItemGroup>** to group items in the **\<List>** component to build a two-dimensional list.
351
352A **\<List>** component allows one or more **\<ListItemGroup>** child components. By default, the width of **\<ListItemGroup>** is equal to that of **\<List>**. When initializing **\<ListItemGroup>**, you can use the **header** parameter to set its header.
353
354
355```ts
356@Entry
357@Component
358struct ContactsList {
359
360  @Builder itemHead(text: string) {
361    // Header of the list group, corresponding to the group A and B locations.
362    Text(text)
363      .fontSize(20)
364      .backgroundColor('#fff1f3f5')
365      .width('100%')
366      .padding(5)
367  }
368
369  build() {
370    List() {
371      ListItemGroup({ header: this.itemHead('A') }) {
372        // Render the repeated list items of group A.
373      }
374
375      ListItemGroup({ header: this.itemHead('B') }) {
376        // Render the repeated list items of group B.
377      }
378    }
379  }
380}
381```
382
383If the structures of multiple **\<ListItemGroup>** components are similar, you can combine the data of these components into an array and use **ForEach** to render them cyclically. For example, in the contacts list, the **contacts** data of each group (for details, see [Iterating List Content](#iterating-list-content)) and the **title** data of the corresponding group are combined and defined as the **contactsGroups** array.
384
385
386```ts
387class cgtmp{
388  title:string = ''
389  contacts:Array<object>|null = null
390}
391export let contactsGroups: object[] = [
392  {
393    title: 'A',
394    contacts: [
395      new Contact('Alice', $r('app.media.iconA')),
396      new Contact ('Ann', $r ('app.media.iconB')),
397      new Contact('Angela', $r('app.media.iconC')),
398    ],
399  } as cgtmp,
400  {
401    title: 'B',
402    contacts: [
403      new Contact ('Ben', $r ('app.media.iconD')),
404      new Contact ('Bryan', $r ('app.media.iconE')),
405    ],
406  } as cgtmp,
407]
408```
409
410Then, with rendering of **contactsGroups** in **ForEach**, a contact list with multiple groups is implemented.
411
412
413```ts
414class cgtmpf{
415  title:string = ''
416  contacts:Array<object>|null = null
417  key:string = ''
418}
419class heF{
420  itemHead:Function = (text: string) => {}
421  foo(val:string){
422    this.itemHead(val)
423  }
424}
425let fff:heF = this.heF()
426List() {
427  // Render the <ListItemGroup> components cyclically. contactsGroups is the data set of contacts and titles of multiple groups.
428  ForEach(contactsGroups, (item: cgtmpf) => {
429    ListItemGroup({ header: fff(item.title) }) {
430      // Render <ListItem> components cyclically.
431      if (item.contacts) {
432        ForEach(item.contacts, () => {
433          ListItem() {
434          }
435        }, (item: cgtmpf) => item.key.toString())
436      }
437    }
438  })
439}
440```
441
442
443## Adding a Sticky Header
444
445The sticky header is a common pattern for keeping the header in the same place on the screen while the user scrolls down the list. As shown in the following figure, when you scroll through group A in the contacts list, the header of group B is always below group A. When you start scrolling through group B, the header of group B is fixed at the top of the screen. After group B has been scrolled to the bottom, the header of group B is replaced by the header of next group.
446
447Sticky headers not only signify the representation and usage of data in the respective groups, but also help users navigate through a large amount of information, thereby avoiding unnecessary scrolling between the top of the area where the header is located and the area of interest.
448
449  **Figure 12** Sticky header
450
451![en-us_image_0000001511740552](figures/en-us_image_0000001511740552.gif)
452
453You can set a sticky header or footer for a **\<ListItemGroup>** component by setting the **sticky** attribute of its parent **\<List>** component.
454
455Setting the **sticky** attribute to **StickyStyle.Header** implements a sticky header. To implement a sticky footer, use the **footer** parameter to initialize the footer of **\<ListItemGroup>** and set the **sticky** attribute to **StickyStyle.Footer**.
456
457
458```ts
459import util from '@ohos.util';
460class cgtmpf{
461  title:string = ''
462  contacts:Array<object>|null = null
463  key:string = ''
464}
465class Contact {
466  key: string = util.generateRandomUUID(true);
467  name: string;
468  icon: Resource;
469
470  constructor(name: string, icon: Resource) {
471    this.name = name;
472    this.icon = icon;
473  }
474}
475export let contactsGroups: object[] = [
476  {
477    title: 'A',
478    contacts: [
479      new Contact('Alice', $r('app.media.iconA')),
480      new Contact ('Ann', $r ('app.media.iconB')),
481      new Contact('Angela', $r('app.media.iconC')),
482    ],
483  } as cgtmpf,
484  {
485    title: 'B',
486    contacts: [
487      new Contact ('Ben', $r ('app.media.iconD')),
488      new Contact ('Bryan', $r ('app.media.iconE')),
489    ],
490  } as cgtmpf,
491]
492@Entry
493@Component
494struct ContactsList {
495  // Define the contactsGroups array.
496
497  @Builder itemHead(text: string) {
498    // Header of the list group, corresponding to the group A and B locations.
499    Text(text)
500      .fontSize(20)
501      .backgroundColor('#fff1f3f5')
502      .width('100%')
503      .padding(5)
504  }
505
506  build() {
507    List() {
508      // Render the <ListItemGroup> components cyclically. contactsGroups is the data set of contacts and titles of multiple groups.
509      ForEach(contactsGroups, (item:cgtmpf) => {
510        ListItemGroup({ header: this.itemHead(item.title) }) {
511          // Render <ListItem> components cyclically.
512          if(item.contacts){
513            ForEach(item.contacts, () => {
514              ListItem() {
515              }
516            }, (item:cgtmpf) => item.key.toString())
517          }
518        }
519      })
520    }
521    .sticky(StickyStyle.Header) // Set a sticky header.
522  }
523}
524```
525
526
527## Controlling the Scrolling Position
528
529In some cases you may want to control the scrolling position of a list. For example, when there are a huge number of items in the news page list, you may want to allow users to quickly jump to the top or bottom of the list after they have scrolled to a certain point. Below is an example.
530
531  **Figure 13** Returning to the top of the list
532
533![en-us_image_0000001511900520](figures/en-us_image_0000001511900520.gif)
534
535When the **\<List>** component is initialized, you can use the **scroller** parameter to bind a [Scroller](../reference/arkui-ts/ts-container-scroll.md#scroller) object to control the scrolling of the list. In this example of a news page list, the **scrollToIndex** API of the **Scroller** object is used to scroll the list to the list item with the specified index. This allows the user to return to the top of the list by clicking a specific button.
536
537First, you need to create a **Scroller** object **listScroller**.
538
539
540```ts
541export let listScroller: Scroller = new Scroller();
542```
543
544Then, use **listScroller** to initialize the **scroller** parameter to bind it with the **\<List>** component. Set **scrollToIndex** to **0**, meaning to return to the top of the list.
545
546
547```ts
548let sttmo:Record<string,Alignment> = { 'alignContent': Alignment.BottomEnd }
549Stack(sttmo) {
550  // use listScroller to initialize the scroller parameter to bind it with the <List> component.
551  List({ space: 20, scroller: this.listScroller }) {
552    ...
553  }
554  ...
555
556  Button() {
557    ...
558  }
559  .onClick(() => {
560    // Specify where e to jump when the specific button is clicked, which is the top of the list in this example.
561    listScroller.scrollToIndex(0)
562  })
563  ...
564}
565```
566
567
568## Responding to the Scrolling Position
569
570Many applications need to listen for the scrolling position change of the list and respond. For example, with regard to a contacts list, if scrolling spans more than one group, the alphabetical index bar at one side of the list also needs to be updated to highlight the letter corresponding to the current group.
571
572Another common example is a scrolling list working with a multi-level index bar, as in the case of a product category page in a shopping application.
573
574**Figure 14** Alphabetical index bar's response to contacts list scrolling
575
576![en-us_image_0000001563060769](figures/en-us_image_0000001563060769.gif)
577
578As shown above, when the contacts list scrolls from group A to B, the alphabetical index bar on the right also changes from A to B. This scenario can be implemented by listening for the **onScrollIndex** event of the **\<List>** component. The alphabet index bar is implemented using the [\<AlphabetIndexer>](../reference/arkui-ts/ts-container-alphabet-indexer.md) component.
579
580When the list scrolls, the **selectedIndex** value of the letter to highlight in the alphabet index bar is recalculated based on the **firstIndex** value of the item to which the list has scrolled. In the **\<AlphabetIndexer>** component, the index of the highlighted item is set through the **selected** attribute. When the value of **selectedIndex** changes, the **\<AlphabetIndexer>** component is re-rendered to highlight the corresponding letter.
581
582
583```ts
584const alphabets = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
585  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
586@Entry
587@Component
588struct ContactsList {
589  @State selectedIndex: number = 0;
590  private listScroller: Scroller = new Scroller();
591
592  build() {
593    Stack({ alignContent: Alignment.End }) {
594      List({ scroller: this.listScroller }) {}
595      .onScrollIndex((firstIndex: number) => {
596        // Recalculate the value of this.selectedIndex in the alphabetical index bar based on the index of the item to which the list has scrolled.
597      })
598
599      // <AlphabetIndexer> component
600      AlphabetIndexer({ arrayValue: alphabets, selected: 0 })
601        .selected(this.selectedIndex)
602    }
603  }
604}
605```
606
607>**NOTE**
608>
609>During index calculation, each **\<ListItemGroup>** component is taken as a whole and assigned an index, and the indexes of the list items within are not included in the calculation.
610
611
612## Responding to Swipe on List Items
613
614Swipe menus are common in many applications. For example, a messaging application generally provides a swipe-to-delete feature for its message list. This feature allows users to delete a message by swiping left on it in the list and touching the delete button, as shown in the following figure.
615
616**Figure 15** Swipe-to-delete feature
617
618![en-us_image_0000001563060773](figures/en-us_image_0000001563060773.gif)
619
620To implement the swipe feature, you can use the **swipeAction** attribute of **\<ListItem>**. In initialization of the **swipeAction** attribute, the **SwipeActionOptions** parameter is mandatory, wherein the **start** parameter indicates the component that appears from the start edge when the list item slides right, and the **end** parameter indicates the component that appears from the end edge when the list item slides left.
621
622In the example of the message list, the **end** parameter is set to a custom delete button. In initialization of the **end** attribute, the index of the sliding list item is passed to the delete button. When the user touches the delete button, the data corresponding to the list item is deleted based on the index.
623
624
625```ts
626@Entry
627@Component
628struct MessageList {
629  @State messages: object[] = [
630    // Initialize the message list data.
631  ];
632
633  @Builder itemEnd(index: number) {
634    // Set the component that appears from the end edge when the list item slides left.
635    Button({ type: ButtonType.Circle }) {
636      Image($r('app.media.ic_public_delete_filled'))
637        .width(20)
638        .height(20)
639    }
640    .onClick(() => {
641      this.messages.splice(index, 1);
642    })
643  }
644
645  build() {
646      List() {
647        ForEach(this.messages, (item:MessageList, index:number|undefined) => {
648          if(index){
649            ListItem() {
650            }
651            .swipeAction({ end: ()=>{this.itemEnd(index)} }) // Set the swipe action.
652          }
653        }, (item:MessageList) => item.id.toString())
654      }
655  }
656}
657```
658
659
660## Adding a Mark to a List Item
661
662A mark is an intuitive, unobtrusive visual indicator to draw attention and convey a specific message. For example, when a new message is received in the message list, a mark is displayed in the upper right corner of the contact's profile picture, indicating that there is a new message from that contact, as shown in the following figure.
663
664  **Figure 16** Adding a mark to a list item
665
666![en-us_image_0000001511580952](figures/en-us_image_0000001511580952.png)
667
668To add a mark, you can use the [\<Badge>](../reference/arkui-ts/ts-container-badge.md) component in **\<ListItem>**. The **\<Badge>** component is a container that can be attached to another component for tagging.
669
670In this example, when implementing the **\<Image>** component for presenting the profile picture of a list item, add it to **\<Badge>** as a child component.
671
672In the **\<Badge>** component, the **count** and **position** parameters are used to set the number of notifications and the position to display the badge, respectively. You can also use the **style** parameter to spruce up the mark.
673
674
675```ts
676Badge({
677  count: 1,
678  position: BadgePosition.RightTop,
679  style: { badgeSize: 16, badgeColor: '#FA2A2D' }
680}) {
681  // The <Image> component implements the contact profile picture.
682  ...
683}
684...
685```
686
687
688## Implementing Pull-Down-to-Refresh and Pull-Up-to-Load
689
690The pull-down-to-refresh and pull-up-to-load features are widely used in mobile applications, such as news applications. In effect, the implementation of these two features follows the same process: (1) As response to a [touch event](../reference/arkui-ts/ts-universal-events-touch.md), a refresh or load view is displayed at the top or bottom of the page; (2) when the refresh or load is complete, the refresh or load view is hidden.
691
692The following describes the implementation of the pull-and-refresh feature:
693
6941. Listen for the finger press event and record the value of the initial position.
695
6962. Listen for the finger movement event, and record and calculate the difference between the value of the current position and the initial value. If the difference is greater than 0, the finger moves downward. Set the maximum value for the movement.
697
6983. Listen for the finger lift event. If the movement reaches the maximum value, trigger data loading and display the refresh view. After the loading is complete, hide the view.
699
700
701## Editing a List
702
703The list editing mode is frequently used in various scenarios, such as to-do list management, file management, and note management. In editing mode, adding and deleting list items are the most basic functions. The core is to add and delete data in the data set corresponding to the list items.
704
705The following uses to-do list management as an example to describe how to quickly add and delete list items.
706
707
708### Adding a List Item
709
710As shown below, when a user touches **Add**, a page is displayed for the user to set options for the new list item. After the user touches **OK**, the corresponding item is added to the list.
711
712  **Figure 17** Adding a to-do task
713
714![en-us_image_0000001511740556](figures/en-us_image_0000001511740556.gif)
715
716The process of implementing the addition feature is as follows:
717
7181. Define the list item data structure and initialize the list data to build the overall list layout and list items.
719   In this example, first define the to-do data structure.
720
721   ```ts
722   import util from '@ohos.util';
723
724   export class ToDo {
725     key: string = util.generateRandomUUID(true);
726     name: string;
727
728     constructor(name: string) {
729       this.name = name;
730     }
731   }
732   ```
733
734    Then, initialize the to-do list data and options:
735
736  ```ts
737  @State toDoData: ToDo[] = [];
738  export let availableThings: string[] = ['Reading', 'Fitness', 'Travel','Music','Movie', 'Singing'];
739  ```
740
741   Finally, build the list layout and list items:
742
743  ```ts
744  export class ToDo {
745    key: string = util.generateRandomUUID(true);
746    name: string;
747    toDoData:ToDo[] = [];
748
749    constructor(name: string) {
750      this.name = name;
751    }
752  }
753  let todo:ToDo = new ToDo()
754  List({ space: 10 }) {
755    ForEach(todo.toDoData, (toDoItem:ToDo) => {
756      ListItem() {
757      }
758    }, (toDoItem:ToDo) => toDoItem.key.toString())
759  }
760  ```
761
7622. Provide the entry for adding a list item, that is, add a click event to the add button.
763
7643. Respond to the user's confirmation of adding and update the list data.
765   The code snippet for steps 2 and 3 is as follows:
766
767   ```ts
768   Text('+')
769     .onClick(() => {
770       TextPickerDialog.show({
771         range: availableThings,
772         onAccept: (value: TextPickerResult) => {
773            todo.toDoData.push(new ToDo(availableThings[value.index])); // Add a list item data.
774         },
775       })
776     })
777   ```
778
779
780### Deleting a List Item
781
782As shown below, when the user long presses a list item to enter the deletion mode, a page is displayed for the user to delete the list item. After the user selects the list item and touches the delete button, the list item is deleted.
783
784  **Figure 18** Deleting a to-do task
785
786![en-us_image_0000001562820877](figures/en-us_image_0000001562820877.gif)
787
788The process of implementing the deletion feature is as follows:
789
7901. Generally, the deletion feature is available only after the list enters the editing mode. Therefore, the entry to the editing mode needs to be provided.
791   In this example, by listening for the long press event of a list item, the list enters the editing mode when the user long presses a list item.
792
793  ```ts
794  class todoTmp{
795    isEditMode:boolean = false
796    selectedItems:Array<object> = []
797    toDoItem:ToDo[] = [];
798    toDoData:ToDo[] = [];
799  }
800  let todolist:todoTmp = new todoTmp()
801   // ToDoListItem.ets
802
803  Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
804    ...
805  }
806  .gesture(
807  GestureGroup(GestureMode.Exclusive,
808    LongPressGesture()
809      .onAction(() => {
810        if (!todolist.isEditMode) {
811          todolist.isEditMode = true; // Enter the editing mode.
812          todolist.selectedItems.push(todolist.toDoItem); // Record the list item selected when the user long presses the button.
813        }
814      })
815    )
816  )
817   ```
818
8192. Respond to the selection by the user and record the list items to be deleted.
820   In this example of the to-do list, respond to the selection by correctly displaying the check mark and record all the selected list items.
821
822  ```ts
823  import util from '@ohos.util';
824  export class ToDo {
825    key: string = util.generateRandomUUID(true);
826    name: string;
827    toDoData:ToDo[] = [];
828
829    constructor(name: string) {
830      this.name = name;
831    }
832  }
833  class todoTmp{
834    isEditMode:boolean = false
835    selectedItems:Array<object> = []
836    toDoItem:ToDo[] = [];
837    toDoData:ToDo[] = [];
838  }
839  let todolist:todoTmp = new todoTmp()
840   // ToDoListItem.ets
841
842  if (todolist.isEditMode) {
843    Checkbox()
844      .onChange((isSelected) => {
845        if (isSelected) {
846          todolist.selectedItems.push(todolist.toDoItem) // When an item is selected, record the selected item.
847        } else {
848          let index = todolist.selectedItems.indexOf(todolist.toDoItem)
849          if (index !== -1) {
850            todolist.selectedItems.splice(index, 1) // When an item is deselected, delete the item from the selectedItems array.
851          }
852        }
853      })
854  }
855   ```
856
8573. Respond to the user's clicking the delete button and delete the corresponding items from the list.
858
859  ```ts
860  import util from '@ohos.util';
861  export class ToDo {
862    key: string = util.generateRandomUUID(true);
863    name: string;
864    toDoData:ToDo[] = [];
865
866    constructor(name: string) {
867      this.name = name;
868    }
869  }
870  class todoTmp{
871    isEditMode:boolean = false
872    selectedItems:Array<object> = []
873    toDoItem:ToDo[] = [];
874    toDoData:ToDo[] = [];
875  }
876  let todolist:todoTmp = new todoTmp()
877   // ToDoList.ets
878
879  Button ('Delete')
880    .onClick(() => {
881      // Delete the toDoData data corresponding to the selected list items.
882      let leftData = todolist.toDoData.filter((item) => {
883        return todolist.selectedItems.find((selectedItem) => selectedItem !== item);
884      })
885
886      todolist.toDoData = leftData;
887      todolist.isEditMode = false;
888    })
889  ```
890
891
892## Handling a Long List
893
894[ForEach](../quick-start/arkts-rendering-control-foreach.md) is applicable to short lists. With regard to a long list with a large number of list items, using **ForEach** will greatly slow down page loading, as it loads all list items at a time. Therefore, for better list performance, use [LazyForEach](../quick-start/arkts-rendering-control-lazyforeach.md) instead to implement on-demand iterative data loading.
895
896For details about the implementation, see the example in [LazyForEach: Lazy Data Loading](../quick-start/arkts-rendering-control-lazyforeach.md).
897
898When the list is rendered in lazy loading mode, to improve the list scrolling experience and minimize white blocks during list scrolling, you can use the **cachedCount** parameter of the **\<List>** component. This parameter sets the number of list items preloaded outside of the screen and is valid only in **LazyForEach**.
899
900
901```ts
902class dataTmp{
903  dataSource:IDataSource|undefined = undefined
904}
905let ds:dataTmp = new dataTmp()
906List() {
907  if(ds.dataSource){
908    LazyForEach(ds.dataSource, () => {
909      ListItem() {
910      }
911    })
912  }
913}.cachedCount(3)
914```
915
916The following uses a vertical list as an example:
917
918- If lazy loading is used for list items and the list contains only one column, the number of the list items to cache before and after the currently displayed one equals the value of **cachedCount**. If the list contains multiple columns, the number of the list items to cache is the value of **cachedCount** multiplied by the number of columns.
919
920- If lazy loading is used for list item groups, the number of the list item groups to cache before and after the currently displayed one equals the value of **cachedCount**, regardless of the number of columns.
921
922>**NOTE**
923>
924>1. A greater **cachedCount** value may result in higher CPU and memory overhead of the UI. Adjust the value by taking into account both the comprehensive performance and user experience.
925>
926>2. When a list uses data lazy loading, all list items except the list items in the display area and the cached list items are destroyed.
927
928