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. The list is applicable for presenting similar data types or data type sets, such as images and text. Some common lists seen in applications are the contacts list, playlist, and shopping list.
7
8You can use lists to easily and efficiently display structured, scrollable information. Specifically, you can provide a single view of rows or columns by arranging the [ListItemGroup](../reference/apis-arkui/arkui-ts/ts-container-listitemgroup.md) or [ListItem](../reference/apis-arkui/arkui-ts/ts-container-listitem.md) child components linearly in a vertical or horizontal direction in the [List](../reference/apis-arkui/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
45While **Grid** and **WaterFlow** can also create single-column and multi-column layouts, there are scenarios where the **List** is the more suitable choice. Specifically, if your layout design requires columns of equal width and items do not need to span rows or columns, opt for the **List**.
46
47### Constraints
48
49The 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.
50
51As 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.
52
53  **Figure 4** Main axis and cross axis of the list
54
55![en-us_image_0000001562940581](figures/en-us_image_0000001562940581.png)
56
57If 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.
58
59If 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**.
60
61In 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.
62
63  **Figure 5** Main axis height constraint example 1 (A: parent component of List; B: List component; C: all child components of List)
64
65![en-us_image_0000001511580956](figures/en-us_image_0000001511580956.png)
66
67If 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.
68
69In 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.
70
71  **Figure 6** Main axis height constraint example 2 (A: parent component of List; B: List component; C: all child components of List)
72
73![en-us_image_0000001511740548](figures/en-us_image_0000001511740548.png)
74
75If 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.
76
77
78## Developing the Layout
79
80
81### Setting the Main Axis Direction
82
83By 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.
84
85To create a horizontal scrolling list, set the **listDirection** attribute to **Axis.Horizontal**. The default value of **listDirection** is **Axis.Vertical**.
86
87
88```ts
89List() {
90  // ...
91}
92.listDirection(Axis.Horizontal)
93```
94
95
96### Setting the Cross Axis Layout
97
98The 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.
99
100The 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/apis-arkui/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**.
101
102
103```ts
104List() {
105  // ...
106}
107.lanes(2)
108```
109
110If 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.
111
112
113```ts
114@Entry
115@Component
116struct EgLanes {
117  @State egLanes: LengthConstrain = { minLength: 200, maxLength: 300 }
118  build() {
119    List() {
120      // ...
121    }
122    .lanes(this.egLanes)
123  }
124}
125```
126
127For example, if the **lanes** attribute is set to **{ minLength: 200, maxLength: 300 }** for a vertical list, then:
128
129- When the list width is 300 vp, the list contains one column, because **minLength** is 200 vp.
130
131- When the list width changes to 400 vp, which is twice that of the **minLength** value, the list is automatically adapted to two-column.
132
133With 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.
134
135
136```ts
137List() {
138  // ...
139}
140.alignListItem(ListItemAlign.Center)
141```
142
143
144## Displaying Data in the List
145
146The 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.
147
148  **Figure 7** Example of a city list
149
150![en-us_image_0000001563060761](figures/en-us_image_0000001563060761.png)
151
152```ts
153@Entry
154@Component
155struct CityList {
156  build() {
157    List() {
158      ListItem() {
159        Text('Beijing').fontSize(24)
160      }
161
162      ListItem() {
163        Text('Hangzhou').fontSize(24)
164      }
165
166      ListItem() {
167        Text('Shanghai').fontSize(24)
168      }
169    }
170    .backgroundColor('#FFF1F3F5')
171    .alignListItem(ListItemAlign.Center)
172  }
173}
174```
175
176Each **ListItem** component can contain only one root child component. Therefore, it does not allow for child components in tile mode. If tile mode is required, encapsulate the child components into a container or create a custom component.
177
178  **Figure 8** Example of a contacts list
179
180![en-us_image_0000001511421328](figures/en-us_image_0000001511421328.png)
181
182As 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.
183
184
185```ts
186List() {
187  ListItem() {
188    Row() {
189      Image($r('app.media.iconE'))
190        .width(40)
191        .height(40)
192        .margin(10)
193
194      Text('Tom')
195        .fontSize(20)
196    }
197  }
198
199  ListItem() {
200    Row() {
201      Image($r('app.media.iconF'))
202        .width(40)
203        .height(40)
204        .margin(10)
205
206      Text('Tracy')
207        .fontSize(20)
208    }
209  }
210}
211```
212
213
214## Iterating List Content
215
216Compared 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.
217
218 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.
219
220
221```ts
222import { util } from '@kit.ArkTS'
223
224class Contact {
225  key: string = util.generateRandomUUID(true);
226  name: string;
227  icon: Resource;
228
229  constructor(name: string, icon: Resource) {
230    this.name = name;
231    this.icon = icon;
232  }
233}
234
235@Entry
236@Component
237struct SimpleContacts {
238  private contacts: Array<object> = [
239    new Contact('Tom', $r ("app.media.iconA")),
240    new Contact('Tracy', $r ("app.media.iconB")),
241  ]
242
243  build() {
244    List() {
245      ForEach(this.contacts, (item: Contact) => {
246        ListItem() {
247          Row() {
248            Image(item.icon)
249              .width(40)
250              .height(40)
251              .margin(10)
252            Text(item.name).fontSize(20)
253          }
254          .width('100%')
255          .justifyContent(FlexAlign.Start)
256        }
257      }, (item: Contact) => JSON.stringify(item))
258    }
259    .width('100%')
260  }
261}
262```
263
264In 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).
265
266
267## Customizing the List Style
268
269
270### Setting the Spacing
271
272When 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:
273
274
275```ts
276List({ space: 10 }) {
277  // ...
278}
279```
280
281
282### Adding Dividers
283
284A 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.
285
286  **Figure 9** Using dividers between the setting items
287
288![en-us_image_0000001511580960](figures/en-us_image_0000001511580960.png)
289
290To add dividers between list items, you can use the **divider** attribute together with the following style attributes:<br> **strokeWidth** and **color**: stroke width and color of the diver, respectively.
291
292**startMargin** and **endMargin**: distance between the divider and the start edge and end edge of the list, respectively.
293
294
295```ts
296class DividerTmp {
297  strokeWidth: Length = 1
298  startMargin: Length = 60
299  endMargin: Length = 10
300  color: ResourceColor = '#ffe9f0f0'
301
302  constructor(strokeWidth: Length, startMargin: Length, endMargin: Length, color: ResourceColor) {
303    this.strokeWidth = strokeWidth
304    this.startMargin = startMargin
305    this.endMargin = endMargin
306    this.color = color
307  }
308}
309@Entry
310@Component
311struct EgDivider {
312  @State egDivider: DividerTmp = new DividerTmp(1, 60, 10, '#ffe9f0f0')
313  build() {
314    List() {
315      // ...
316    }
317    .divider(this.egDivider)
318  }
319}
320```
321
322This 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.
323
324>**NOTE**
325>
326>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.
327>
328>2. When a list contains multiple columns, the **startMargin** and **endMargin** attributes of the divider apply to each column.
329>
330>3. The divider is drawn between list items. No divider is drawn above the first list item and below the last list item.
331
332
333### Adding a Scrollbar
334
335When 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.
336
337  **Figure 10** Scrollbar of a list
338
339![en-us_image_0000001511740544](figures/en-us_image_0000001511740544.gif)
340
341When 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/apis-arkui/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.
342
343The default value of the **scrollBar attribute** is **BarState.Off** in API version 9 and earlier versions and **BarState.Auto** since API version 10.
344```ts
345List() {
346  // ...
347}
348.scrollBar(BarState.Auto)
349```
350
351
352## Adding Grouping Support
353
354By 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.
355
356  **Figure 11** Contacts list with grouping
357
358![en-us_image_0000001511580948](figures/en-us_image_0000001511580948.png)
359
360You can use **ListItemGroup** to group items in the **List** component to build a two-dimensional list.
361
362A **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.
363
364
365```ts
366@Entry
367@Component
368struct ContactsList {
369
370  @Builder itemHead(text: string) {
371    // Header of the list group, corresponding to the group A and B locations.
372    Text(text)
373      .fontSize(20)
374      .backgroundColor('#fff1f3f5')
375      .width('100%')
376      .padding(5)
377  }
378
379  build() {
380    List() {
381      ListItemGroup({ header: this.itemHead('A') }) {
382        // Render the repeated list items of group A.
383      }
384
385      ListItemGroup({ header: this.itemHead('B') }) {
386        // Render the repeated list items of group B.
387      }
388    }
389  }
390}
391```
392
393If 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. Then, with rendering of **contactsGroups** in **ForEach**, a contact list with multiple groups is implemented. For details, see the example in [Adding a Sticky Header](#adding-a sticky-header).
394
395## Adding a Sticky Header
396
397The 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.
398
399Sticky 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.
400
401  **Figure 12** Sticky header
402
403![en-us_image_0000001511740552](figures/en-us_image_0000001511740552.gif)
404
405You can set a sticky header or footer for a **ListItemGroup** component by setting the **sticky** attribute of its parent **List** component.
406
407Setting 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**.
408
409
410```ts
411import { util } from '@kit.ArkTS'
412class Contact {
413  key: string = util.generateRandomUUID(true);
414  name: string;
415  icon: Resource;
416
417  constructor(name: string, icon: Resource) {
418    this.name = name;
419    this.icon = icon;
420  }
421}
422class ContactsGroup {
423  title: string = ''
424  contacts: Array<object> | null = null
425  key: string = ""
426}
427export let contactsGroups: object[] = [
428  {
429    title: 'A',
430    contacts: [
431      new Contact('Alice', $r('app.media.iconA')),
432      new Contact('Ann', $r('app.media.iconB')),
433      new Contact('Angela', $r('app.media.iconC')),
434    ],
435    key: util.generateRandomUUID(true)
436  } as ContactsGroup,
437  {
438    title: 'B',
439    contacts: [
440      new Contact('Ben', $r('app.media.iconD')),
441      new Contact('Bryan', $r('app.media.iconE')),
442    ],
443    key: util.generateRandomUUID(true)
444  } as ContactsGroup,
445  // ...
446]
447@Entry
448@Component
449struct ContactsList {
450  // Define the contactsGroups array.
451  @Builder itemHead(text: string) {
452    // Header of the list group, corresponding to the group A and B locations.
453    Text(text)
454      .fontSize(20)
455      .backgroundColor('#fff1f3f5')
456      .width('100%')
457      .padding(5)
458  }
459  build() {
460    List() {
461      // Render the ListItemGroup components cyclically. contactsGroups is the data set of contacts and titles of multiple groups.
462      ForEach(contactsGroups, (itemGroup: ContactsGroup) => {
463        ListItemGroup({ header: this.itemHead(itemGroup.title) }) {
464          // Render ListItem components cyclically.
465          if (itemGroup.contacts) {
466            ForEach(itemGroup.contacts, (item: Contact) => {
467              ListItem() {
468                // ...
469              }
470            }, (item: Contact) => JSON.stringify(item))
471          }
472        }
473      }, (itemGroup: ContactsGroup) => JSON.stringify(itemGroup))
474    }.sticky(StickyStyle.Header)  // Set a sticky header.
475  }
476}
477```
478
479
480## Controlling the Scrolling Position
481
482In 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.
483
484  **Figure 13** Returning to the top of the list
485
486![en-us_image_0000001511900520](figures/en-us_image_0000001511900520.gif)
487
488When the **List** component is initialized, you can use the **scroller** parameter to bind a [Scroller](../reference/apis-arkui/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.
489
490To start with, create a **Scroller** object **listScroller**.
491
492
493```ts
494private listScroller: Scroller = new Scroller();
495```
496
497Then, 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.
498
499
500```ts
501Stack({ alignContent: Alignment.Bottom }) {
502  // Use listScroller to initialize the scroller parameter to bind it with the List component.
503  List({ space: 20, scroller: this.listScroller }) {
504    // ...
505  }
506
507  Button() {
508    // ...
509  }
510  .onClick(() => {
511    // Specify where e to jump when the specific button is clicked, which is the top of the list in this example.
512    this.listScroller.scrollToIndex(0)
513  })
514}
515```
516
517
518## Responding to the Scrolling Position
519
520Many 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.
521
522Another 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.
523
524**Figure 14** Alphabetical index bar's response to contacts list scrolling
525
526![en-us_image_0000001563060769](figures/en-us_image_0000001563060769.gif)
527
528As 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/apis-arkui/arkui-ts/ts-container-alphabet-indexer.md) component.
529
530When 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.
531
532
533```ts
534const alphabets = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
535  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
536@Entry
537@Component
538struct ContactsList {
539  @State selectedIndex: number = 0;
540  private listScroller: Scroller = new Scroller();
541
542  build() {
543    Stack({ alignContent: Alignment.End }) {
544      List({ scroller: this.listScroller }) {}
545      .onScrollIndex((firstIndex: number) => {
546        // Recalculate the value of this.selectedIndex in the alphabetical index bar based on the index of the item to which the list has scrolled.
547      })
548
549      // AlphabetIndexer component
550      AlphabetIndexer({ arrayValue: alphabets, selected: 0 })
551        .selected(this.selectedIndex)
552    }
553  }
554}
555```
556
557>**NOTE**
558>
559>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.
560
561
562## Responding to Swipe on List Items
563
564Swipe 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 and touching the delete button, as shown in the following figure. For details about how to add a badge to the profile picture of a list item, see [Adding a Badge to a List Item](#adding-a-badge-to-a-list-item).
565
566**Figure 15** Swipe-to-delete feature
567
568![en-us_image_0000001563060773](figures/en-us_image_0000001563060773.gif)
569
570Swiping left or right on a list item can be implemented through the [swipeAction](../reference/apis-arkui/arkui-ts/ts-container-listitem.md#swipeaction9) attribute. 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 is swiped right, and the **end** parameter indicates the component that appears from the end edge when the list item is swiped left.
571
572In 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.
573
5741. Build the component that appears from the end edge when the list item is swiped left.
575
576    ```ts
577    @Builder itemEnd(index: number) {
578      // Build the component that appears from the end edge when the list item is swiped left.
579      Button({ type: ButtonType.Circle }) {
580        Image($r('app.media.ic_public_delete_filled'))
581          .width(20)
582          .height(20)
583      }
584      .onClick(() => {
585        // this.messages is the list data source, which can be constructed as required. A specified data item can be deleted from the data source upon click.
586        this.messages.splice(index, 1);
587      })
588    }
589    ```
590
5912. Binds the **swipeAction** attribute to a list item that can be swiped left.
592
593    ```ts
594    // When constructing a list, use ForEach to render list items based on the data source this.messages.
595    ListItem() {
596      // ...
597    }
598    .swipeAction({
599      end: {
600        // index is the index of the list item.
601        builder: () => { this.itemEnd(index) },
602      }
603    }) // Set the swipe action.
604    ```
605
606## Adding a Badge to a List Item
607
608A badge is an intuitive, unobtrusive visual indicator to draw attention and convey a specific message. For example, a badge can be displayed in the upper right corner of the contact's profile picture to indicate that there is a new message from that contact, as shown in the following figure.
609
610  **Figure 16** Adding a badge to a list item
611
612![en-us_image_0000001511580952](figures/en-us_image_0000001511580952.png)
613
614To add a badge, use the [Badge](../reference/apis-arkui/arkui-ts/ts-container-badge.md) component in **ListItem**. The **Badge** component is a container that can be attached to another component for tagging.
615
616In this example, when implementing the **Image** component for presenting the profile picture of a list item, add it to **Badge** as a child component.
617
618In 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 badge.
619
620
621```ts
622ListItem() {
623  Badge({
624    count: 1,
625    position: BadgePosition.RightTop,
626    style: { badgeSize: 16, badgeColor: '#FA2A2D' }
627  }) {
628    // The Image component implements the contact profile picture.
629    // ...
630  }
631}
632```
633
634
635## Implementing Pull-Down-to-Refresh and Pull-Up-to-Load
636
637The 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/apis-arkui/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.
638
639The following describes the implementation of the pull-and-refresh feature:
640
6411. Listen for the finger press event and record the value of the initial position.
642
6432. 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.
644
6453. 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.
646
647> **NOTE**
648>
649> To implement the pull-down-to-refresh feature, you are advised to use the [Refresh](../reference/apis-arkui/arkui-ts/ts-container-refresh.md) component.
650
651<!--RP1--><!--RP1End-->
652
653<!--Del-->
654  <!--DelEnd-->
655
656
657## Editing a List
658
659The 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.
660
661The following uses to-do list management as an example to describe how to quickly add and delete list items.
662
663
664### Adding a List Item
665
666As 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.
667
668  **Figure 17** Adding a to-do task
669
670![en-us_image_0000001511740556](figures/en-us_image_0000001511740556.gif)
671
672The process of implementing the addition feature is as follows:
673
6741. Define the list item data structure. In this example, a to-do data structure is defined.
675
676   ```ts
677   //ToDo.ets
678   import { util } from '@kit.ArkTS'
679
680   export class ToDo {
681     key: string = util.generateRandomUUID(true);
682     name: string;
683
684     constructor(name: string) {
685       this.name = name;
686     }
687   }
688   ```
689
6902. Build the overall list layout and list items.
691
692   ```ts
693   //ToDoListItem.ets
694   import { ToDo } from './ToDo';
695   @Component
696   export struct ToDoListItem {
697     @Link isEditMode: boolean
698     @Link selectedItems: ToDo[]
699     private toDoItem: ToDo = new ToDo("");
700
701     build() {
702      Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
703        // ...
704      }
705      .width('100%')
706      .height(80)
707      // .padding(): Set this parameter based on the use case.
708      .borderRadius(24)
709      // .linearGradient(): Set this parameter based on the use case.
710      .gesture(
711        GestureGroup(GestureMode.Exclusive,
712        LongPressGesture()
713          .onAction(() => {
714            // ...
715          })
716        )
717      )
718     }
719   }
720   ```
721
7223. Initialize the to-do list data and available items, and build the list layout and list items.
723
724   ```ts
725   //ToDoList.ets
726   import { ToDo } from './ToDo';
727   import { ToDoListItem } from './ToDoListItem';
728
729   @Entry
730   @Component
731   struct ToDoList {
732     @State toDoData: ToDo[] = []
733     @Watch('onEditModeChange') @State isEditMode: boolean = false
734     @State selectedItems: ToDo[] = []
735    private availableThings: string[] = ['Reading', 'Fitness', 'Travel','Music','Movie', 'Singing']
736
737     onEditModeChange() {
738       if (!this.isEditMode) {
739         this.selectedItems = []
740       }
741    }
742
743     build() {
744       Column() {
745         Row() {
746           if (this.isEditMode) {
747             Text('X')
748               .fontSize(20)
749               .onClick(() => {
750                 this.isEditMode = false;
751               })
752               .margin({ left: 20, right: 20 })
753           } else {
754             Text('To-Do')
755               .fontSize(36)
756               .margin({ left: 40 })
757             Blank()
758             Text('+') // Provide an entry for adding a list item, that is, add a click event for the add button.
759               .onClick(() => {
760                 this.getUIContext().showTextPickerDialog({
761                   range: this.availableThings,
762                   onAccept: (value: TextPickerResult) => {
763                     let arr = Array.isArray(value.index) ? value.index : [value.index];
764                     for (let i = 0; i < arr.length; i++) {
765                       this.toDoData.push(new ToDo(this.availableThings[arr[i]])); // Add to-do list items (available items).
766                     }
767                   },
768                 })
769               })
770           }
771           List({ space: 10 }) {
772             ForEach(this.toDoData, (toDoItem: ToDo) => {
773               ListItem() {
774                 // Place each item of toDoData into the list item in the form of model.
775                 ToDoListItem({
776                   isEditMode: this.isEditMode,
777                   toDoItem: toDoItem,
778                   selectedItems: this.selectedItems })
779               }
780             }, (toDoItem: ToDo) => toDoItem.key.toString())
781           }
782         }
783       }
784     }
785   }
786   ```
787
788
789### Deleting a List Item
790
791As 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.
792
793  **Figure 18** Deleting a to-do task
794
795![en-us_image_0000001562820877](figures/en-us_image_0000001562820877.gif)
796
797The process of implementing the deletion feature is as follows:
798
7991. 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.
800   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.
801
802    ```ts
803    // Structure reference
804    export class ToDo {
805      key: string = util.generateRandomUUID(true);
806      name: string;
807      toDoData: ToDo[] = [];
808
809      constructor(name: string) {
810        this.name = name;
811      }
812    }
813    ```
814    ```ts
815    // Implementation reference
816    Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
817      // ...
818    }
819    .gesture(
820    GestureGroup(GestureMode.Exclusive,
821      LongPressGesture()
822        .onAction(() => {
823          if (!this.isEditMode) {
824            this.isEditMode = true; // Enter the editing mode.
825          }
826        })
827      )
828    )
829    ```
830
8312. Respond to the user's selection and record the list items to be deleted.
832   In this to-do list example, the list items are selected or unselected according to the user's selection.
833
834    ```ts
835   // Structure reference
836   import { util } from '@kit.ArkTS'
837   export class ToDo {
838     key: string = util.generateRandomUUID(true);
839     name: string;
840     toDoData: ToDo[] = [];
841
842     constructor(name: string) {
843       this.name = name;
844     }
845   }
846    ```
847    ```ts
848    // Implementation reference
849    if (this.isEditMode) {
850      Checkbox()
851        .onChange((isSelected) => {
852          if (isSelected) {
853            When this.selectedItems.push(toDoList.toDoItem) // this.selectedItems is selected, the selected list items are recorded. You can construct the list items based on the site requirements.
854          } else {
855            let index = this.selectedItems.indexOf(toDoList.toDoItem)
856            if (index !== -1) {
857              this.selectedItems.splice(index, 1) // When an item is deselected, it is deleted from the selectedItems array.
858            }
859          }
860        })
861    }
862    ```
863
8643. Respond to the user's clicking the delete button and delete the corresponding items from the list.
865
866    ```ts
867    // Structure reference
868    import { util } from '@kit.ArkTS'
869    export class ToDo {
870      key: string = util.generateRandomUUID(true);
871      name: string;
872      toDoData: ToDo[] = [];
873
874      constructor(name: string) {
875        this.name = name;
876      }
877    }
878    ```
879    ```ts
880    // Implementation reference
881    Button('Delete')
882      .onClick(() => {
883        // this.toDoData is the to-do list item, which can be constructed based on service requirements. After an item is clicked, the corresponding data is removed.
884        let leftData = this.toDoData.filter((item) => {
885          return !this.selectedItems.find((selectedItem) => selectedItem == item);
886        })
887        this.toDoData = leftData;
888        this.isEditMode = false;
889      })
890    ```
891
892
893## Handling a Long List
894
895[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 once. Therefore, for better list performance, use [LazyForEach](../quick-start/arkts-rendering-control-lazyforeach.md) instead to implement on-demand iterative data loading.
896
897For details about the implementation, see the example in [LazyForEach: Lazy Data Loading](../quick-start/arkts-rendering-control-lazyforeach.md).
898
899When 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**.
900
901
902```ts
903List() {
904  // ...
905}.cachedCount(3)
906```
907
908The following uses a vertical list as an example:
909
910- 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.
911
912- 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.
913
914>**NOTE**
915>
916>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.
917>
918>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.
919
920
921## Collapsing and Expanding
922
923The collapsing and expanding of list items are widely used, often applied in scenarios such as displaying information lists and filling out forms.
924
925  **Figure 19** Collapsing and expanding of list items
926
927![en-us_image_0000001949866104](figures/en-us_image_0000001949866104.gif)
928
929The process of implementing the collapsing and expanding effect of list items is as follows:
930
9311. Define the list item data structure.
932
933    ```ts
934    interface ItemInfo {
935      index: number,
936      name: string,
937      label: ResourceStr,
938      type?: string,
939    }
940
941    interface ItemGroupInfo extends ItemInfo {
942      children: ItemInfo[]
943    }
944    ```
945
9462. Construct a list structure.
947
948    ```ts
949    @State routes: ItemGroupInfo[] = [
950      {
951        index: 0,
952        name: 'basicInfo',
953        label: 'Basic personal information',
954        children: [
955          {
956            index: 0,
957            name: 'Nickname',
958            label: 'xxxx',
959            type: 'Text'
960          },
961          {
962            index: 1,
963            name: 'Profile picture',
964            label: $r('sys.media.ohos_user_auth_icon_face'),
965            type: 'Image'
966          },
967          {
968            index: 2,
969            name: 'Age',
970            label: 'xxxx',
971            type: 'Text'
972          },
973          {
974            index: 3,
975            name: 'Birthday',
976            label: 'xxxxxxxxx',
977            type: 'Text'
978          },
979          {
980            index: 4,
981            name: 'Gender',
982            label: 'xxxxxxxx',
983            type: 'Text'
984          },
985        ]
986      },
987      {
988        index: 1,
989        name: 'equipInfo',
990        label: 'Device information',
991        children: []
992      },
993      {
994        index: 2,
995        name: 'appInfo',
996        label: 'App usage',
997        children: []
998      },
999      {
1000        index: 3,
1001        name: 'uploadInfo',
1002        label: 'Data you actively upload',
1003        children: []
1004      },
1005      {
1006        index: 4,
1007        name: 'tradeInfo',
1008        label: 'Transactions & assets',
1009        children: []
1010      },
1011      {
1012        index: 5,
1013        name: 'otherInfo',
1014        label: 'Other materials',
1015        children: []
1016      },
1017    ];
1018    @State expandedItems: boolean[] = Array(this.routes.length).fill(false);
1019    @State selection: string | null = null;
1020    build() {
1021      Column() {
1022        // ...
1023
1024        List({ space: 10 }) {
1025          ForEach(this.routes, (itemGroup: ItemGroupInfo) => {
1026            ListItemGroup({
1027              header: this.ListItemGroupHeader(itemGroup),
1028              style: ListItemGroupStyle.CARD,
1029            }) {
1030              if (this.expandedItems[itemGroup.index] && itemGroup.children) {
1031                ForEach(itemGroup.children, (item: ItemInfo) => {
1032                  ListItem({ style: ListItemStyle.CARD }) {
1033                    Row() {
1034                      Text(item.name)
1035                      Blank()
1036                      if (item.type === 'Image') {
1037                        Image(item.label)
1038                          .height(20)
1039                          .width(20)
1040                      } else {
1041                        Text(item.label)
1042                      }
1043                      Image($r('sys.media.ohos_ic_public_arrow_right'))
1044                        .fillColor($r('sys.color.ohos_id_color_fourth'))
1045                        .height(30)
1046                        .width(30)
1047                    }
1048                    .width("100%")
1049                  }
1050                  .width("100%")
1051                  .animation({ curve: curves.interpolatingSpring(0, 1, 528, 39) })
1052                })
1053              }
1054            }.clip(true)
1055          })
1056        }
1057        .width("100%")
1058      }
1059      .width('100%')
1060      .height('100%')
1061      .justifyContent(FlexAlign.Start)
1062      .backgroundColor($r('sys.color.ohos_id_color_sub_background'))
1063    }
1064    ```
1065
10663. Control whether each list item is expanded by changing the state of **ListItem**, and achieve the animation effects during the expanding and collapsing process through **animation** and **animateTo**.
1067
1068    ```ts
1069    @Builder
1070    ListItemGroupHeader(itemGroup: ItemGroupInfo) {
1071      Row() {
1072        Text(itemGroup.label)
1073        Blank()
1074        Image($r('sys.media.ohos_ic_public_arrow_down'))
1075          .fillColor($r('sys.color.ohos_id_color_fourth'))
1076          .height(30)
1077          .width(30)
1078          .rotate({ angle: !!itemGroup.children.length ? (this.expandedItems[itemGroup.index] ? 180 : 0) : 180 })
1079          .animation({ curve: curves.interpolatingSpring(0, 1, 228, 22) })
1080      }
1081      .width("100%")
1082      .padding(10)
1083      .animation({ curve: curves.interpolatingSpring(0, 1, 528, 39) })
1084      .onClick(() => {
1085        if (itemGroup.children.length) {
1086          this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(0, 1, 528, 39) }, () => {
1087            this.expandedItems[itemGroup.index] = !this.expandedItems[itemGroup.index]
1088          })
1089        }
1090      })
1091    }
1092    ```
1093