DEV Community

HarmonyOS
HarmonyOS

Posted on

How to make ListItem texts in the same row display with the same height

Read the original article:Node-API Part-3: Wrapping a Native C++ Object in a Sendable ArkTS ClassHow to make ListItem texts in the same row display with the same height

Requirement Description

When there are multiple ListItems in the same row, the text heights displayed inside each ListItem are inconsistent, resulting in different heights for each ListItem. Since the text length is uncertain, it is not possible to fix the ListItem height using the height property.

Problem preview:

cke_2950.png

Expected effect preview:

cke_5083.png

Background Knowledge

  • The List Component is a list component containing list items of equal width, suitable for displaying similar data in continuous rows and columns. It can be set to multiple columns through properties. You can use onAreaChange to get the area change values of each ListItem, such as component height.
  • The Flex Component is a flexible container component. By modifying the wrap and alignItems parameters in FlexOptions and adjusting the width of child components, you can display multiple rows and columns to achieve a layout similar to List or Grid.

Implementation Steps

Solution 1: Use the Flex container component. Modify the wrap and alignItems parameters of FlexOptions to achieve a multi-row, multi-column display, with child components stretched and filled along the cross axis.

Solution 2: Use onAreaChange to get the height of each ListItem and determine the maximum height in each row. Then, using state variable changes and if/else conditional statements, refresh the layout and set each component’s height to the row’s maximum height.

Code Snippet

Solution 1:

import { LengthUnit } from '@kit.ArkUI'

export class Area {
  name: string = ''
  heightSize: number = 0

  constructor(name: string) {
    this.name = name
  }
}

const ARR: Area[] = [
  new Area('Tongliang District'), new Area('Tongnan District'), new Area('Rongchang District'), new Area('Kaizhou District'), new Area('Liangping District'), new Area('Shizhu Tujia Autonomous County'),
  new Area('Xiushan Tujia and Miao Autonomous County kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'),
  new Area('Youyang Tujia and Miao Autonomous County'), new Area('Pengshui Miao and Tujia Autonomous County'),
  // ... (repeated for example)
]

// Child component
@Component
struct TextComponentOne {
  content: string = ''

  build() {
    Column() {
      Text(this.content)
        .fontSize(14)
        .padding(9)
        .width('100%')
        .textAlign(TextAlign.Center)
        .fontColor('#000')
    }
  }
}

@Entry
@Component
struct FlexItemHeight {
  private arr: Area[] = ARR

  build() {
    Scroll() {
      Flex({
        justifyContent: FlexAlign.Center,
        wrap: FlexWrap.Wrap, // Flex container items arranged in multiple rows/columns
        alignItems: ItemAlign.Stretch, // Stretch and fill items along the cross axis
        space: {
          cross: { value: 14, unit: LengthUnit.VP }
        }
      }) {
        ForEach(this.arr, (item: Area) => {
          Column() {
            TextComponentOne({ content: item.name })
          }
          // Formatting, width, border radius, etc., should be set in the Flex item; if set in the child component, stretching may not take effect
          .justifyContent(FlexAlign.Center)
          .width('33%')
          .border({ width: 1, radius: 7 })
        })
      }
    }
    .width('100%')
    .background('#fff')
  }
}
Enter fullscreen mode Exit fullscreen mode

Solution 2:

Step 1: Calculate the height of each ListItem and determine the maximum height in the same row.

// Bubble sort and return the maximum value
sortReturnMax(nums: number[]): number {
  const len: number = nums.length;
  for (let i: number = 0; i < len; i++) {
    for (let j: number = 0; j < len - 1 - i; j++) {
      if (nums[j] > nums[j + 1]) {
        const tmp = nums[j];
        nums[j] = nums[j + 1];
        nums[j + 1] = tmp;
      }
    }
  }
  return nums[nums.length-1]
}

// Determine the maximum height in a row and set each element in arr to that height
reSetHeightSize() {
  for (let i = 0; i < this.arr.length; i += 3) {
    let arrTmp: number[] = []
    arrTmp.push(this.arr[i].heightSize)
    arrTmp.push(this.arr[i+1].heightSize)
    arrTmp.push(this.arr[i+2].heightSize)
    let maxHeight = this.sortReturnMax(arrTmp)
    this.arr[i].heightSize = maxHeight
    this.arr[i + 1].heightSize = maxHeight
    this.arr[i + 2].heightSize = maxHeight
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: During the first layout, use onAreaChange to collect and calculate ListItem heights.

@Builder
ListItemChangeRegion() {
  List({ space: 14 }) {
    ForEach(this.arr, (item: Region, index: number) => {
      ListItem() {
        TextComponentTwo({ content: item.name })
          .onAreaChange((oldValue: Area, newValue: Area) => {
             // After traversing all ListItems and collecting heights, call reSetHeightSize to calculate the maximum height per row, then change isBoolean to trigger UI re-rendering
            this.arr[index].heightSize = newValue.height as number
            if (this.flags === this.arr.length - 1) {
              this.isBoolean = false
              this.reSetHeightSize()
            }
            this.flags += 1
          })
      }
    })
  }
  .lanes(3)
}
Enter fullscreen mode Exit fullscreen mode

Step 3: In the second layout, set the component height to the maximum height of the row.

@Builder
ListItemSameHeight() {
  List({ space: 14 }) {
    ForEach(this.arr, (item: Region, index: number) => {
      ListItem() {
        TextComponentTwo({ content: item.name, hgt: item.heightSize })
      }
    }, (item: Region) => JSON.stringify(item))
  }
  .lanes(3)
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Use an if conditional statement and state variable isBoolean to re-render the layout when the variable value changes.

if (this.isBoolean) {
  // First layout: onRegionChange callback triggered to collect and calculate ListItem heights
  this.ListItemChangeRegion()
} else {
  // When isBoolean changes, re-layout enters else branch; now each ListItem height equals the maximum height in the row
  this.ListItemSameHeight()
}
Enter fullscreen mode Exit fullscreen mode

Complete Example Code:

export class Region {
  name: string = ''
  heightSize: number = 0

  constructor(name: string) {
    this.name = name
  }
}

const ARR: Region[] = [
  new Region('Tongliang District'), new Region('Tongnan District'), new Region('Rongchang District'), new Region('Kaizhou District'), new Region('Liangping District'),
  new Region('Shizhu Tujia Autonomous County'),
  new Region('Xiushan Tujia and Miao Autonomous County kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'),
  new Region('Youyang Tujia and Miao Autonomous County'), new Region('Pengshui Miao and Tujia Autonomous County')
]

// Child component
@Component
struct TextComponentTwo {
  content: string = ''
  hgt: Length = 'auto'

  build() {
    Column() {
      Text(this.content)
        .fontSize(14)
        .padding(9)
        .width('100%')
        .textAlign(TextAlign.Center)
        .fontColor("#000")
    }
    .height(this.hgt)
    .justifyContent(FlexAlign.Center)
    .border({
      width: 1,
      radius: 7
    })
  }
}

@Entry
@Component
struct ListItemHeight {
  private arr: Region[] = ARR
  private flags: number = 0
  @State private isBoolean: boolean = true

  // Bubble sort and return the maximum value
  sortReturnMax(nums: number[]): number {
    const len: number = nums.length;
    for (let i: number = 0; i < len; i++) {
      for (let j: number = 0; j < len - 1 - i; j++) {
        if (nums[j] > nums[j + 1]) {
          const tmp = nums[j];
          nums[j] = nums[j + 1];
          nums[j + 1] = tmp;
        }
      }
    }
    return nums[nums.length - 1]
  }

  // Determine the maximum height in each row and set the height of each element in arr to that maximum value
  reSetHeightSize() {
    for (let i = 0; i < this.arr.length; i += 3) {
      let arrTmp: number[] = []
      arrTmp.push(this.arr[i].heightSize)
      arrTmp.push(this.arr[i + 1].heightSize)
      arrTmp.push(this.arr[i + 2].heightSize)
      let maxHeight = this.sortReturnMax(arrTmp)
      this.arr[i].heightSize = maxHeight
      this.arr[i + 1].heightSize = maxHeight
      this.arr[i + 2].heightSize = maxHeight
    }
  }

  @Builder
  ListItemChangeRegion() {
    List({ space: 14 }) {
      ForEach(this.arr, (item: Region, index: number) => {
        ListItem() {
          TextComponentTwo({ content: item.name })
            .onAreaChange((oldValue: Area, newValue: Area) => {
              // After traversing all ListItems and collecting their heights, call reSetHeightSize to calculate the maximum height of each row,
              // then change the state variable isBoolean to trigger UI re-rendering
              this.arr[index].heightSize = newValue.height as number
              if (this.flags === this.arr.length - 1) {
                this.isBoolean = false
                this.reSetHeightSize()
              }
              this.flags += 1
            })
        }
      })
    }
    .lanes(3)
  }

  @Builder
  ListItemSameHeight() {
    List({ space: 14 }) {
      ForEach(this.arr, (item: Region, index: number) => {
        ListItem() {
          TextComponentTwo({ content: item.name, hgt: item.heightSize })
        }
      }, (item: Region) => JSON.stringify(item))
    }
    .lanes(3)
  }

  build() {
    Column() {
      if (this.isBoolean) {
        // On the first layout, the onRegionChange callback is triggered by component area changes to collect and calculate ListItem heights
        this.ListItemChangeRegion()
      } else {
        // When isBoolean changes, re-layout enters the else branch; at this point, all ListItems have the maximum height in their row
        this.ListItemSameHeight()
      }
    }
    .width('100%')
    .height('100%')
    .padding({ top: 5 })
    .backgroundColor("#fff")
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

img

Limitations or Considerations

  • This example supports API Version 19 Release and above.
  • This example supports HarmonyOS 5.1.1 Release SDK and above.
  • Compilation and execution require DevEco Studio 5.1.1 Release and above.

Written by Bunyamin Akcay

Top comments (0)