DEV Community

HarmonyOS
HarmonyOS

Posted on

How to get the position of the first visible and last visible ListItem

Read the original article:How to get the position of the first visible and last visible ListItem

How to get the position of the first visible and last visible ListItem

Requirement Description

When scrolling a horizontal List component, how to dynamically track the indices of the first fully visible and last fully visible ListItem

Background Knowledge

  • List: A container for displaying a series of items with uniform width, suitable for presenting homogeneous data (e.g., images and text).
  • ListItem: Must be used within List to render individual items.
  • Events:
    • onAreaChange: Triggered when component dimensions change, useful for obtaining coordinates and size.
    • onScrollIndex: Fired when the first/last visible child index changes during scrolling.
    • onScrollStop: Triggered when scrolling stops.
  • Limitation: onScrollIndex treats ListItemGroup as a single index (ignores inner ListItem indices). Use onVisibleAreaChange for grouped lists.

Implementation Steps

  1. Use onAreaChange to get List width for visibility checks.
  2. Initialize indices via onScrollIndex, then update them in onScrollStop.
  3. Adjust indices for partial visibility:
    • If the first item is partially hidden, use the next item.
    • If the last item is partially hidden, use the previous item.

Code Snippet / Configuration

@Entry
@Component
struct ListExample {
  private arr: number[] = []
  private scrollerForList: Scroller = new Scroller()
  @State startStr: string = ''
  startIndex = 0  // First visible item
  endIndex = 0    // Last visible item
  initState = true
  listWidth: number = 0  // List width

  // Adjust indices for partial visibility (±3vp tolerance)
  private judgeVisible() {
    this.startStr = ""
    let rect = this.scrollerForList.getItemRect(this.startIndex)
    if (rect.x < -3) this.startIndex++  // Left-clipped item
    rect = this.scrollerForList.getItemRect(this.endIndex)
    if (rect.x + rect.width > this.listWidth + 3) this.endIndex--  // Right-clipped item
    this.startStr = (this.startIndex <= this.endIndex) 
      ? `${this.startIndex},${this.endIndex}` 
      : "No fully visible items"
  }

  aboutToAppear() {
    for (let i = 0; i < 20; i++) this.arr.push(i)
  }

  build() {
    Column({ space: 20 }) {
      Text(this.startStr).fontColor(Color.Red).height(100).width("100%")
      Row() {
        List({ space: 20, initialIndex: 0, scroller: this.scrollerForList }) {
          ForEach(this.arr, (item: number) => {
            ListItem() {
              Text('' + item).width('100%').height(100)
            }
            .width(item % 4 == 0 ? 500 : 50).height(200)
          }, (item: number) => item.toString())
        }
        .listDirection(Axis.Horizontal)
        .onAreaChange((_, newValue) => { this.listWidth = newValue.width as number })
        .onScrollIndex((start, end) => {
          this.startIndex = start
          this.endIndex = end
          if (this.initState) { this.judgeVisible(); this.initState = false }
        })
        .onScrollStop(() => {
          const [tmpStart, tmpEnd] = [this.startIndex, this.endIndex]
          this.judgeVisible()
          this.startIndex = tmpStart
          this.endIndex = tmpEnd
        })
      }.width('100%').height('100%')
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Handling ListItemGroup

ListItemGroup({ header: this.ListGroupHead(itemGroup.title) }) {
  ForEach(this.arr, (item: number) => {
    ListItem() {
      Text(item.toString())
    }
    .onVisibleAreaChange([0, 1], (isVisible: boolean, currRatio: number) => {
      // Logic for visibility changes (e.g., show header button for topmost group)
    })
  })
}
Enter fullscreen mode Exit fullscreen mode

Test Results

Correctly identifies fully visible items in horizontal List.

Limitations

onScrollIndex ignores nested ListItem indices within ListItemGroup.

Requires manual adjustment for partial visibility thresholds (e.g., 3vp tolerance).

Related Documents

https://developer.huawei.com/consumer/en/doc/harmonyos-references/ts-container-list

Written by Ahmet Furkan Sevim

Top comments (0)