如何实现同一行的ListItem文本高度不一致按最大高度展示
==============================

#### 问题现象

在同一行中有多个ListItem时，ListItem中显示的文本高度不一致，导致各ListItem高度不一致，由于文本的长度不确定，无法通过设置height属性来固定ListItem的高度。

问题图预览：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/65/v3/uIjKX91rRZOf6R2uDe8LVQ/zh-cn_image_0000002628392150.png?HW-CC-KV=V1&HW-CC-Date=20260709T133905Z&HW-CC-Expire=86400&HW-CC-Sign=4A97CF03168A839A6473A26ED2DA7A2484CD63B5F9E695C8E8BD368317B863A6 "点击放大")

#### 背景知识

*   [List组件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-list)为列表组件，包含相同宽度的列表项，适合连续、多行呈现同类数据，可通过属性设置为多列。可以通过[onAreaChange](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-component-area-change-event#onareachange)获取各ListItem的组件区域变化前后的值，如组件高度。
*   [Flex组件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-flex)为弹性容器组件，可通过修改[FlexOptions](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-flex#flexoptions对象说明)的wrap和alignItems的参数，配合子组件的宽度，设置子组件多行多列显示，实现与List、Grid相似的布局。

#### 解决方案

*   **方案一**：使用Flex容器组件，修改FlexOptions的wrap和alignItems的参数，实现多行多列显示，且子组件在交叉轴方向拉伸填充。示例代码如下：
    
    import { LengthUnit } from '@kit.ArkUI';
    
    export class Area {
      name: string = '';
      heightSize: number = 0;
    
      constructor(name: string) {
        this.name = name;
      }
    }
    
    const ARR: Area\[\] = \[
      new Area('铜梁区'), new Area('潼南区'), new Area('荣昌区'), new Area('开州区'), new Area('梁平区'),
      new Area('石柱土家族自治县'),
      new Area('秀山土家族苗族自治县kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'),
      new Area('酉阳土家族苗族自治县'), new Area('彭水苗族土家族自治县'),
      new Area('铜梁区'), new Area('潼南区'), new Area('荣昌区'), new Area('开州区'), new Area('梁平区'),
      new Area('石柱土家族自治县'),
      new Area('秀山土家族苗族自治县kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'),
      new Area('酉阳土家族苗族自治县'), new Area('彭水苗族土家族自治县'),
      new Area('铜梁区'), new Area('潼南区'), new Area('荣昌区'), new Area('开州区'), new Area('梁平区'),
      new Area('石柱土家族自治县'),
      new Area('秀山土家族苗族自治县kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'),
      new Area('酉阳土家族苗族自治县'), new Area('彭水苗族土家族自治县'),
    \];
    
    _// 元素子组件_
    @Component
    struct TextComponentOne {
      content: string = '';
    
      build() {
        Column() {
          Text(this.content)
            .fontSize(14)
            .padding(9)
            .width(this.content.length >= 9 ? '100%' : null)
            .textAlign(this.content.length >= 9 ? TextAlign.Start : TextAlign.Center);
        };
      }
    }
    
    @Entry
    @Component
    struct Index {
      private arr: Area\[\] = ARR;
    
      build() {
        Scroll() {
          Flex({
            justifyContent: FlexAlign.Center,
            wrap: FlexWrap.Wrap, _// Flex容器的元素多行/列排布_
            alignItems: ItemAlign.Stretch, _// 元素在Flex容器中，交叉轴方向拉伸填充_
            space: {
              cross: { value: 0, unit: LengthUnit.VP }
            }
          }) {
            ForEach(this.arr, (item: Area) => {
              Column() {
                TextComponentOne({ content: item.name });
              }
             _ // 格式、宽度、圆角等属性需在Flex元素设置，若设置在元素子组件内，交叉轴防线拉伸填充可能不生效_
              .justifyContent(item.name.length >= 9 ? FlexAlign.Start : FlexAlign.Center)
              .width('30%')
              .border({ width: 0, radius: 7 })
              .backgroundColor('#ffe5e5e5')
              .margin({
                bottom: 5,
                top: 5,
                right: 3,
                left: 3
              });
            });
          };
        }
        .width('100%');
      }
    }
    
    修正效果图如下：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/a4/v3/mQr62QeMS0mLmPQLtmohnw/zh-cn_image_0000002658791433.png?HW-CC-KV=V1&HW-CC-Date=20260709T133905Z&HW-CC-Expire=86400&HW-CC-Sign=8D1A7AD33D35DF2B8D4544D2980DF3022F713D927CE0A4DD18233BDB7AB9E5B4 "点击放大")
    
*   **方案二**：通过onAreaChange获取各ListItem高度并判断行内最大高度，通过状态变量变化和if/else条件语句，刷新页面布局，将组件高度设置为行内最大高度。
    
    1.  计算每个ListItem的高度，并判断同一行中所有ListItem的最大高度。
        
        _// 判断行内高度的最大值，并将arr中的元素的高度设置为高度最大值_
        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);
            arrTmp.sort((a, b) => a - b);
            let maxHeight = Math.max(...arrTmp);
            this.arr\[i\].heightSize = maxHeight;
            this.arr\[i + 1\].heightSize = maxHeight;
            this.arr\[i + 2\].heightSize = maxHeight;
          }
        }
        
    2.  首次布局时，通过onAreaChange收集并计算ListItem高度。
        
        @Builder
        ListItemChangeRegion() {
          List({ space: 8 }) {
            ForEach(this.arr, (item: RegionOne, index: number) => {
              ListItem() {
                TextComponentTwo({ content: item.name })
                  .onAreaChange((oldValue: Area, newValue: Area) => {
                   _ // 遍历全部ListItem并收集高度后，调用reSetHeightSize方法计算各行的最大高度，并再完成遍历后改变状态变量isBoolean的值，UI渲染改变_
                    this.arr\[index\].heightSize = newValue.height as number;
                    if (this.flags === this.arr.length - 1) {
                      this.isBoolean = false;
                      this.reSetHeightSize();
                    }
                    this.flags += 1;
                    console.info(\`Ace: on area change, oldValue is ${JSON.stringify(oldValue)} value is ${JSON.stringify(newValue)}\`);
                  });
              };
            });
          }
          .lanes(3);
        }
        
    3.  二次布局时，设置组件高度为行内最大高度。
        
        @Builder
        ListItemSameHeight() {
          List({ space: 8 }) {
            ForEach(this.arr, (item: RegionOne, index: number) => {
              ListItem() {
                TextComponentTwo({ content: item.name, hgt: item.heightSize, index: index });
              };
            }, (item: RegionOne) => JSON.stringify(item));
          }
          .lanes(3);
        }
        
    4.  使用条件语句if和状态变量isBoolean，通过判断状态变量的值的变化，重新对布局进行刷新。
        
        if (this.isBoolean) {
         _ // 首次布局，组件区域变化触发onRegionOneChange回调，收集并计算ListItem高度_
          this.ListItemChangeRegion();
        } else {
         _ // isBoolean值发生改变，重新布局进入else语句，此时，各ListItem的高度已是行内的最大高度值_
          this.ListItemSameHeight();
        }
        
    
    完整示例参考如下：
    
    class RegionOne {
      name: string = '';
      heightSize: number = 0;
    
      constructor(name: string) {
        this.name = name;
      }
    }
    
    const ARR: RegionOne\[\] = \[
      new RegionOne('铜梁区'), new RegionOne('潼南区'), new RegionOne('荣昌区'), new RegionOne('开州区'),
      new RegionOne('梁平区'),
      new RegionOne('石柱土家族自治县'),
      new RegionOne('秀山土家族苗族自治县kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'),
      new RegionOne('酉阳土家族苗族自治县'), new RegionOne('彭水苗族土家族自治县'),
      new RegionOne('铜梁区'), new RegionOne('潼南区'), new RegionOne('荣昌区'), new RegionOne('开州区'),
      new RegionOne('梁平区'),
      new RegionOne('石柱土家族自治县'),
      new RegionOne('秀山土家族苗族自治县kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'),
      new RegionOne('酉阳土家族苗族自治县'), new RegionOne('彭水苗族土家族自治县'),
      new RegionOne('铜梁区'), new RegionOne('潼南区'), new RegionOne('荣昌区'), new RegionOne('开州区'),
      new RegionOne('梁平区'),
      new RegionOne('石柱土家族自治县'),
      new RegionOne('秀山土家族苗族自治县kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk'),
      new RegionOne('酉阳土家族苗族自治县'), new RegionOne('彭水苗族土家族自治县'),
    \];
    
    _// 元素子组件_
    @Component
    struct TextComponentTwo {
      content: string = '';
      hgt: Length = 'auto';
      index: number = 0;
    
      build() {
        Column() {
          Text(this.content)
            .zIndex(this.index)
            .fontSize(14)
            .padding(9)
            .width(115)
            .textAlign(this.content.length >= 9 ? TextAlign.Start : TextAlign.Center);
        }
        .height(this.hgt)
        .backgroundColor('#ffe5e5e5')
        .justifyContent(this.content.length >= 9 ? FlexAlign.Start : FlexAlign.Center)
        .border({
          width: 0,
          radius: 7
        });
      }
    }
    
    @Entry
    @Component
    struct Page {
      private arr: RegionOne\[\] = ARR;
      private flags: number = 0;
      @State private isBoolean: boolean = true;
    
     _ // 判断行内高度的最大值，并将arr中的元素的高度设置为高度最大值_
      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);
          arrTmp.sort((a, b) => a - b);
          let maxHeight = Math.max(...arrTmp);
          this.arr\[i\].heightSize = maxHeight;
          this.arr\[i + 1\].heightSize = maxHeight;
          this.arr\[i + 2\].heightSize = maxHeight;
        }
      }
    
      @Builder
      ListItemChangeRegion() {
        List({ space: 8 }) {
          ForEach(this.arr, (item: RegionOne, index: number) => {
            ListItem() {
              TextComponentTwo({ content: item.name })
                .onAreaChange((oldValue: Area, newValue: Area) => {
                _  // 遍历全部ListItem并收集高度后，调用reSetHeightSize方法计算各行的最大高度，并再完成遍历后改变状态变量isBoolean的值，UI渲染改变_
                  this.arr\[index\].heightSize = newValue.height as number;
                  if (this.flags === this.arr.length - 1) {
                    this.isBoolean = false;
                    this.reSetHeightSize();
                  }
                  this.flags += 1;
                  console.info(\`Ace: on area change, oldValue is ${JSON.stringify(oldValue)} value is ${JSON.stringify(newValue)}\`);
                });
            };
          });
        }
        .lanes(3);
      }
    
      @Builder
      ListItemSameHeight() {
        List({ space: 8 }) {
          ForEach(this.arr, (item: RegionOne, index: number) => {
            ListItem() {
              TextComponentTwo({ content: item.name, hgt: item.heightSize, index: index });
            };
          }, (item: RegionOne) => JSON.stringify(item));
        }
        .lanes(3);
      }
    
      build() {
        Column() {
          if (this.isBoolean) {
          _  // 首次布局，组件区域变化触发onRegionOneChange回调，收集并计算ListItem高度_
            this.ListItemChangeRegion();
          } else {
         _   // isBoolean值发生改变，重新布局进入else语句，此时，各ListItem的高度已是行内的最大高度值_
            this.ListItemSameHeight();
          }
        }
        .width('100%')
        .height('100%')
        .padding({ top: 5, left: 12, right: 6 });
      }
    }
    
    效果预览：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/11/v3/mYNezj4ERNOZvcRnuZJ2wg/zh-cn_image_0000002628552046.png?HW-CC-KV=V1&HW-CC-Date=20260709T133905Z&HW-CC-Expire=86400&HW-CC-Sign=DE97181150C524AF7944FF437A770578CAA2F9BE98B038EBBCA52B22C6BA2217 "点击放大")
    

#### 总结

*   行内各元素高度不同，要使元素高度按行内最大高度展示，推荐使用Flex容器组件，通过改变wrap和alignItems的参数，配合设置元素宽度实现。
*   若场景需要使用List组件，则可以通过onAreaChange获取组件高度，自定义方法判断和记录行内组件最大高度，并改变状态变量的值触发UI渲染改变，将组件的高度设置为行内组件的最大高度。当元素数量较多时，此方案可能出现性能问题。