• 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. 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/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-ifelse.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
112@Entry
113@Component
114struct EgLanes {
115  @State egLanes: LengthConstrain = { minLength: 200, maxLength: 300 }
116  build() {
117    List() {
118      // ...
119    }
120    .lanes(this.egLanes)
121  }
122}
123```
124
125For example, if the **lanes** attribute is set to **{ minLength: 200, maxLength: 300 }** for a vertical list, then:
126
127- When the list width is 300 vp, the list contains one column, because **minLength** is 200 vp.
128
129- When the list width changes to 400 vp, which is twice that of the **minLength** value, the list is automatically adapted to two-column.
130
131With 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.
132
133
134```ts
135List() {
136  // ...
137}
138.alignListItem(ListItemAlign.Center)
139```
140
141
142## Displaying Data in the List
143
144The 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.
145
146  **Figure 7** Example of a city list
147
148![en-us_image_0000001563060761](figures/en-us_image_0000001563060761.png)
149
150```ts
151@Entry
152@Component
153struct CityList {
154  build() {
155    List() {
156      ListItem() {
157        Text('Beijing').fontSize(24)
158      }
159
160      ListItem() {
161        Text('Hangzhou').fontSize(24)
162      }
163
164      ListItem() {
165        Text('Shanghai').fontSize(24)
166      }
167    }
168    .backgroundColor('#FFF1F3F5')
169    .alignListItem(ListItemAlign.Center)
170  }
171}
172```
173
174Each **\<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.
175
176  **Figure 8** Example of a contacts list
177
178![en-us_image_0000001511421328](figures/en-us_image_0000001511421328.png)
179
180As 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.
181
182
183```ts
184List() {
185  ListItem() {
186    Row() {
187      Image($r('app.media.iconE'))
188        .width(40)
189        .height(40)
190        .margin(10)
191
192      Text ('Tom')
193        .fontSize(20)
194    }
195  }
196
197  ListItem() {
198    Row() {
199      Image($r('app.media.iconF'))
200        .width(40)
201        .height(40)
202        .margin(10)
203
204      Text ('Tracy')
205        .fontSize(20)
206    }
207  }
208}
209```
210
211
212## Iterating List Content
213
214Compared 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.
215
216 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.
217
218
219```ts
220import util from '@ohos.util';
221
222class Contact {
223  key: string = util.generateRandomUUID(true);
224  name: string;
225  icon: Resource;
226
227  constructor(name: string, icon: Resource) {
228    this.name = name;
229    this.icon = icon;
230  }
231}
232
233@Entry
234@Component
235struct SimpleContacts {
236  private contacts: Array<object> = [
237    new Contact ('Tom', $r ("app.media.iconA")),
238    new Contact ('Tracy', $r ("app.media.iconB")),
239  ]
240
241  build() {
242    List() {
243      ForEach(this.contacts, (item: Contact) => {
244        ListItem() {
245          Row() {
246            Image(item.icon)
247              .width(40)
248              .height(40)
249              .margin(10)
250            Text(item.name).fontSize(20)
251          }
252          .width('100%')
253          .justifyContent(FlexAlign.Start)
254        }
255      }, (item: Contact) => item.key.toString())
256    }
257    .width('100%')
258  }
259}
260```
261
262In 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).
263
264
265## Customizing the List Style
266
267
268### Setting the Spacing
269
270When 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:
271
272
273```ts
274List({ space: 10 }) {
275  // ...
276}
277```
278
279
280### Adding Dividers
281
282A 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.
283
284  **Figure 9** Using dividers between the setting items
285
286![en-us_image_0000001511580960](figures/en-us_image_0000001511580960.png)
287
288To 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.
289
290**startMargin** and **endMargin**: distance between the divider and the start edge and end edge of the list, respectively.
291
292
293```ts
294class DividerTmp {
295  strokeWidth: Length = 1
296  startMargin: Length = 60
297  endMargin: Length = 10
298  color: ResourceColor = '#ffe9f0f0'
299
300  constructor(strokeWidth: Length, startMargin: Length, endMargin: Length, color: ResourceColor) {
301    this.strokeWidth = strokeWidth
302    this.startMargin = startMargin
303    this.endMargin = endMargin
304    this.color = color
305  }
306}
307@Entry
308@Component
309struct EgDivider {
310  @State egDivider: DividerTmp = new DividerTmp(1, 60, 10, '#ffe9f0f0')
311  build() {
312    List() {
313      // ...
314    }
315    .divider(this.egDivider)
316  }
317}
318```
319
320This 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.
321
322>**NOTE**
323>
324>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.
325>
326>2. When a list contains multiple columns, the **startMargin** and **endMargin** attributes of the divider apply to each column.
327>
328>3. The divider is drawn between list items. No divider is drawn above the first list item and below the last list item.
329
330
331### Adding a Scrollbar
332
333When 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.
334
335  **Figure 10** Scrollbar of a list
336
337![en-us_image_0000001511740544](figures/en-us_image_0000001511740544.gif)
338
339When 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.
340
341The default value of the **scrollBar attribute** is **BarState.Off** in API version 9 and earlier versions and **BarState.Auto** since API version 10.
342```ts
343List() {
344  // ...
345}
346.scrollBar(BarState.Auto)
347```
348
349
350## Adding Grouping Support
351
352By 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.
353
354  **Figure 11** Contacts list with grouping
355
356![en-us_image_0000001511580948](figures/en-us_image_0000001511580948.png)
357
358You can use **\<ListItemGroup>** to group items in the **\<List>** component to build a two-dimensional list.
359
360A **\<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.
361
362
363```ts
364@Entry
365@Component
366struct ContactsList {
367
368  @Builder itemHead(text: string) {
369    // Header of the list group, corresponding to the group A and B locations.
370    Text(text)
371      .fontSize(20)
372      .backgroundColor('#fff1f3f5')
373      .width('100%')
374      .padding(5)
375  }
376
377  build() {
378    List() {
379      ListItemGroup({ header: this.itemHead('A') }) {
380        // Render the repeated list items of group A.
381      }
382
383      ListItemGroup({ header: this.itemHead('B') }) {
384        // Render the repeated list items of group B.
385      }
386    }
387  }
388}
389```
390
391If 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).
392
393## Adding a Sticky Header
394
395The 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.
396
397Sticky 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.
398
399  **Figure 12** Sticky header
400
401![en-us_image_0000001511740552](figures/en-us_image_0000001511740552.gif)
402
403You can set a sticky header or footer for a **\<ListItemGroup>** component by setting the **sticky** attribute of its parent **\<List>** component.
404
405Setting 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**.
406
407
408```ts
409import util from '@ohos.util';
410class Contact {
411  key: string = util.generateRandomUUID(true);
412  name: string;
413  icon: Resource;
414
415  constructor(name: string, icon: Resource) {
416    this.name = name;
417    this.icon = icon;
418  }
419}
420class ContactsGroup {
421  title: string = ''
422  contacts: Array<object> | null = null
423  key: string = ""
424}
425export let contactsGroups: object[] = [
426  {
427    title: 'A',
428    contacts: [
429      new Contact('Alice', $r('app.media.iconA')),
430      new Contact ('Ann', $r ('app.media.iconB')),
431      new Contact('Angela', $r('app.media.iconC')),
432    ],
433    key: util.generateRandomUUID(true)
434  } as ContactsGroup,
435  {
436    title: 'B',
437    contacts: [
438      new Contact ('Ben', $r ('app.media.iconD')),
439      new Contact ('Bryan', $r ('app.media.iconE')),
440    ],
441    key: util.generateRandomUUID(true)
442  } as ContactsGroup,
443  // ...
444]
445@Entry
446@Component
447struct ContactsList {
448  // Define the contactsGroups array.
449  @Builder itemHead(text: string) {
450    // Header of the list group, corresponding to the group A and B locations.
451    Text(text)
452      .fontSize(20)
453      .backgroundColor('#fff1f3f5')
454      .width('100%')
455      .padding(5)
456  }
457  build() {
458    List() {
459      // Render the <ListItemGroup> components cyclically. contactsGroups is the data set of contacts and titles of multiple groups.
460      ForEach(contactsGroups, (itemGroup: ContactsGroup) => {
461        ListItemGroup({ header: this.itemHead(itemGroup.title) }) {
462          // Render <ListItem> components cyclically.
463          if (itemGroup.contacts) {
464            ForEach(itemGroup.contacts, (item: Contact) => {
465              ListItem() {
466                // ...
467              }
468            }, (item: Contact) => item.key.toString())
469          }
470        }
471      }, (itemGroup: ContactsGroup) => itemGroup.key.toString())
472    }.sticky(StickyStyle.Header)  // Set a sticky header.
473  }
474}
475```
476
477
478## Controlling the Scrolling Position
479
480In 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.
481
482  **Figure 13** Returning to the top of the list
483
484![en-us_image_0000001511900520](figures/en-us_image_0000001511900520.gif)
485
486When 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.
487
488To start with, create a **Scroller** object **listScroller**.
489
490
491```ts
492private listScroller: Scroller = new Scroller();
493```
494
495Then, 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.
496
497
498```ts
499Stack({ alignContent: Alignment.Bottom }) {
500  // use listScroller to initialize the scroller parameter to bind it with the <List> component.
501  List({ space: 20, scroller: this.listScroller }) {
502    // ...
503  }
504
505  Button() {
506    // ...
507  }
508  .onClick(() => {
509    // Specify where e to jump when the specific button is clicked, which is the top of the list in this example.
510    this.listScroller.scrollToIndex(0)
511  })
512}
513```
514
515
516## Responding to the Scrolling Position
517
518Many 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.
519
520Another 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.
521
522**Figure 14** Alphabetical index bar's response to contacts list scrolling
523
524![en-us_image_0000001563060769](figures/en-us_image_0000001563060769.gif)
525
526As 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.
527
528When 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.
529
530
531```ts
532const alphabets = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
533  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
534@Entry
535@Component
536struct ContactsList {
537  @State selectedIndex: number = 0;
538  private listScroller: Scroller = new Scroller();
539
540  build() {
541    Stack({ alignContent: Alignment.End }) {
542      List({ scroller: this.listScroller }) {}
543      .onScrollIndex((firstIndex: number) => {
544        // Recalculate the value of this.selectedIndex in the alphabetical index bar based on the index of the item to which the list has scrolled.
545      })
546
547      // <AlphabetIndexer> component
548      AlphabetIndexer({ arrayValue: alphabets, selected: 0 })
549        .selected(this.selectedIndex)
550    }
551  }
552}
553```
554
555>**NOTE**
556>
557>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.
558
559
560## Responding to Swipe on List Items
561
562Swipe 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).
563
564**Figure 15** Swipe-to-delete feature
565
566![en-us_image_0000001563060773](figures/en-us_image_0000001563060773.gif)
567
568The swipeAction attribute (../reference/arkui-ts/ts-container-listitem.md# attribute) of a list item can be used to implement the function of sliding a list item leftwards or rightwards. 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.
569
570In 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.
571
5721. Build the component that appears from the end edge when the list item is swiped left.
573
574    ```ts
575    @Builder itemEnd(index: number) {
576      // Build the component that appears from the end edge when the list item is swiped left.
577      Button({ type: ButtonType.Circle }) {
578        Image($r('app.media.ic_public_delete_filled'))
579          .width(20)
580          .height(20)
581      }
582      .onClick(() => {
583        // 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.
584        this.messages.splice(index, 1);
585      })
586    }
587    ```
588
5892. Binds the **swipeAction** attribute to a list item that can be swiped left.
590
591    ```ts
592    // When constructing a list, use ForEach to render list items based on the data source this.messages.
593    ListItem() {
594      // ...
595    }
596    .swipeAction({
597      end: {
598        // index is the index of the list item.
599        builder: () => { this.itemEnd(index) },
600      }
601    }) // Set the swipe action.
602    ```
603
604## Adding a Badge to a List Item
605
606A 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.
607
608  **Figure 16** Adding a badge to a list item
609
610![en-us_image_0000001511580952](figures/en-us_image_0000001511580952.png)
611
612To add a badge, 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.
613
614In this example, when implementing the **\<Image>** component for presenting the profile picture of a list item, add it to **\<Badge>** as a child component.
615
616In 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.
617
618
619```ts
620ListItem() {
621  Badge({
622    count: 1,
623    position: BadgePosition.RightTop,
624    style: { badgeSize: 16, badgeColor: '#FA2A2D' }
625  }) {
626    // The <Image> component implements the contact profile picture.
627    // ...
628  }
629}
630```
631
632
633## Implementing Pull-Down-to-Refresh and Pull-Up-to-Load
634
635The 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.
636
637The following describes the implementation of the pull-and-refresh feature:
638
6391. Listen for the finger press event and record the value of the initial position.
640
6412. 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.
642
6433. 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.
644
645
646
647
648## Editing a List
649
650The 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.
651
652The following uses to-do list management as an example to describe how to quickly add and delete list items.
653
654
655### Adding a List Item
656
657As 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.
658
659  **Figure 17** Adding a to-do task
660
661![en-us_image_0000001511740556](figures/en-us_image_0000001511740556.gif)
662
663The process of implementing the addition feature is as follows:
664
6651. Define the list item data structure. In this example, a to-do data structure is defined.
666
667   ```ts
668   //ToDo.ets
669   import util from '@ohos.util';
670
671   export class ToDo {
672     key: string = util.generateRandomUUID(true);
673     name: string;
674
675     constructor(name: string) {
676       this.name = name;
677     }
678   }
679   ```
680
6812. Build the overall list layout and list items.
682
683   ```ts
684   //ToDoListItem.ets
685   import { ToDo } from './ToDo';
686   @Component
687   export struct ToDoListItem {
688     @Link isEditMode: boolean
689     @Link selectedItems: ToDo[]
690     private toDoItem: ToDo = new ToDo("");
691
692     build() {
693      Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
694        // ...
695      }
696      .width('100%')
697      .height(80)
698      // .padding(): Set this parameter based on the use case.
699      .borderRadius(24)
700      // .linearGradient(): Set this parameter based on the use case.
701      .gesture(
702        GestureGroup(GestureMode.Exclusive,
703        LongPressGesture()
704          .onAction(() => {
705            // ...
706          })
707        )
708      )
709     }
710   }
711   ```
712
7133. Initialize the to-do list data and available items, and build the list layout and list items.
714
715   ```ts
716   //ToDoList.ets
717   import { ToDo } from './ToDo';
718   import { ToDoListItem } from './ToDoListItem';
719   @Entry
720   @Component
721   struct ToDoList {
722     @State toDoData: ToDo[] = []
723     @Watch('onEditModeChange') @State isEditMode: boolean = false
724     @State selectedItems: ToDo[] = []
725     private availableThings: string[] = ['Reading', 'Fitness', 'Travel','Music','Movie', 'Singing']
726
727     onEditModeChange() {
728       if(!this.isEditMode) {
729         this.selectedItems = []
730       }
731     }
732
733     build() {
734       Column() {
735         Row() {
736           if (this.isEditMode) {
737             Text('X')
738               .fontSize(20)
739               .onClick(() => {
740                 this.isEditMode = false;
741               })
742               .margin({ left: 20, right: 20})
743           } else {
744             Text('To-Do')
745               .fontSize(36)
746               .margin({ left: 40 })
747           Blank()
748           Text('+') // Provide an entry for adding a list item, that is, add a click event for the add button.
749               .onClick(() => {
750                 TextPickerDialog.show({
751                   range: this.availableThings,
752                   onAccept: (value: TextPickerResult) => {
753                   let arr = Array.isArray(value.index) ? value.index : [value.index];
754                   for(let i = 0; i < arr.length; i++) {
755                      this.toDoData.push(new ToDo(this.availableThings[arr[i]])); // Add to-do list items (available items).
756                   }
757                 },
758               })
759             })
760           }
761            List({ space: 10 }) {
762              ForEach(this.toDoData, (toDoItem: ToDo) => {
763                ListItem() {
764                  // Place each item of toDoData into the list item in the form of model.
765                  ToDoListItem({
766                    isEditMode: this.isEditMode,
767                    toDoItem: toDoItem,
768                    selectedItems: this.selectedItems })
769                }
770              }, (toDoItem: ToDo) => toDoItem.key.toString())
771            }
772          }
773        }
774      }
775    }
776   ```
777
778
779
780
781### Deleting a List Item
782
783As 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.
784
785  **Figure 18** Deleting a to-do task
786
787![en-us_image_0000001562820877](figures/en-us_image_0000001562820877.gif)
788
789The process of implementing the deletion feature is as follows:
790
7911. 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.
792   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.
793
794    ```ts
795    // Structure reference
796    export class ToDo {
797      key: string = util.generateRandomUUID(true);
798      name: string;
799      toDoData: ToDo[] = [];
800
801      constructor(name: string) {
802        this.name = name;
803      }
804    }
805    class ToDoTmp {
806      isEditMode: boolean = false
807      selectedItems: Array<object> = []
808      toDoItem: ToDo[] = [];
809      toDoData: ToDo[] = [];
810    }
811    let toDoList: ToDoTmp = new ToDoTmp()
812    // ToDoListItem.ets
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 (!toDoList.isEditMode) {
824            toDoList.isEditMode = true; // Enter the editing mode.
825            toDoList.selectedItems.push(toDoList.toDoItem); // Record the list items selected when long pressed.
826          }
827        })
828      )
829    )
830    ```
831
8322. Respond to the user's selection and record the list items to be deleted.
833   In this to-do list example, the list items are selected or unselected according to the user's selection.
834
835    ```ts
836    // Structure reference
837    import util from '@ohos.util';
838    export class ToDo {
839      key: string = util.generateRandomUUID(true);
840      name: string;
841      toDoData: ToDo[] = [];
842
843      constructor(name: string) {
844        this.name = name;
845      }
846    }
847    class ToDoTmp {
848      isEditMode: boolean = false
849      selectedItems: Array<object> = []
850      toDoItem: ToDo[] = [];
851      toDoData: ToDo[] = [];
852    }
853    let toDoList: ToDoTmp = new ToDoTmp()
854    // ToDoListItem.ets
855    ```
856    ```ts
857    // Implementation reference
858    if (toDoList.isEditMode) {
859      Checkbox()
860        .onChange((isSelected) => {
861          if (isSelected) {
862            toDoList.selectedItems.push(toDoList.toDoItem) // When an item is selected, record the selected item.
863          } else {
864            let index = toDoList.selectedItems.indexOf(toDoList.toDoItem)
865            if (index !== -1) {
866              toDoList.selectedItems.splice(index, 1) // When an item is deselected, delete the item from the selectedItems array.
867            }
868          }
869        })
870    }
871    ```
872
8733. Respond to the user's clicking the delete button and delete the corresponding items from the list.
874
875    ```ts
876    // Structure reference
877    import util from '@ohos.util';
878    export class ToDo {
879      key: string = util.generateRandomUUID(true);
880      name: string;
881      toDoData: ToDo[] = [];
882
883      constructor(name: string) {
884        this.name = name;
885      }
886    }
887    class ToDoTmp {
888      isEditMode: boolean = false
889      selectedItems: Array<object> = []
890      toDoItem: ToDo[] = [];
891      toDoData: ToDo[] = [];
892    }
893    let toDoList: ToDoTmp = new ToDoTmp()
894    ```
895    ```ts
896    // Implementation reference
897    Button ('Delete')
898      .onClick(() => {
899        // Delete the toDoData data corresponding to the selected list items.
900        let leftData = toDoList.toDoData.filter((item) => {
901          return toDoList.selectedItems.find((selectedItem) => selectedItem !== item);
902        })
903
904        toDoList.toDoData = leftData;
905        toDoList.isEditMode = false;
906      })
907    ```
908
909
910## Handling a Long List
911
912[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.
913
914For details about the implementation, see the example in [LazyForEach: Lazy Data Loading](../quick-start/arkts-rendering-control-lazyforeach.md).
915
916When 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**.
917
918
919```ts
920List() {
921  // ...
922}.cachedCount(3)
923```
924
925The following uses a vertical list as an example:
926
927- 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.
928
929- 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.
930
931>**NOTE**
932>
933>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.
934>
935>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.
936