# 如何解决Scroll组件设置高度限制后，无法顶部对齐的问题

#### 问题现象

在实现滚动列表效果时，由于加载的数据数量不确定，比如购物车场景时。需要实现在数据较多时列表能正常滚动，数据较少时，数据能从顶部开始排布。但是当Scroll组件设置layoutWeight属性时，如果数据较少，Scroll居中排布，无法实现顶部对齐效果。不设置layoutWeight属性时数据少可以实现顶部对齐，但是数据过多时会导致数据展示不全。即：Scroll组件设置高度与不设置高度会产生不同的排布效果。项目结构如下：

```
Column() {
  Text()
  Scroll()
}
```

#### 效果预览

#### 背景知识

*   Scroll组件：该组件是可滚动的容器组件，当子组件的布局尺寸超过Scroll组件的尺寸时，内容可以滚动。同时，若子组件的布局尺寸小于Scroll组件的尺寸时，则Scroll组件不可滚动，且默认居中排布。
*   align：该属性是组件的通用位置设置属性，可以设置组件内子组件的排布方式，其参数枚举详见：Alignment。
*   layoutWeight：该属性为组件的通用属性，用于设置组件的布局权重，使用该属性的组件在父容器（Row/Column/Flex）的主轴方向按照各子组件的layoutWeight属性参数大小的比值分配父组件剩余尺寸。

#### 问题定位

针对Scroll组件设置高度与不设置高度会产生不同的排布效果的分析如下（Scroll组件为深灰色）：

*   Scroll未设置layoutWeight时：
    
    若此时没有设置Scroll组件的其它高度限制，比如.height属性，则Scroll组件会自适应内部子组件的总高度（最高为屏幕显示高度，也就是0~100%的高度）。此时若是Scroll的父组件为Column组件，由于Column组件默认自上而下排布，所以Scroll组件会从Column的顶部开始布局。
    
    由图片现象可知，Scroll组件没有高度限制时，高度随子组件高度变化。当Scroll背景颜色与页面背景颜色一致时，布局满足列表从顶部开始排列。但是数据过多时，由于Scroll组件高度会被子组件撑到屏幕高度的100%，若Scroll组件上方有其它兄弟组件，会导致Scroll展示不全，底部被挤出显示区。滚动列表展示不全的相关问题可参考行业常见问题：List展示不全。
    
    Scroll未设置高度限制时，顶部对齐效果是Scroll组件从父组件Column组件顶部开始布局，并不是Scroll子组件从Scroll顶部开始布局。
    

*   Scroll设置layoutWeight时：
    
    此时相当于给了Scroll组件的高度限制，其高度为父组件除去其它子组件后的剩余高度。Scroll子组件的布局尺寸小于Scroll组件的尺寸时，其子组件默认居中排布，问题复现如下：
    
    由图片现象可知，Scroll组件设置高度限制后，当其内数据较少时，默认居中排布。
    

#### 分析结论

Scroll组件需要设置高度限制，否则若Scroll背景颜色与页面背景颜色不一致时，会有显示问题，且若Scroll上方存在兄弟组件，Scroll组件过高时会展示不全。当设置Scroll组件高度后，可以通过设置.align(Alignment.Top)属性实现Scroll子组件顶部排布效果。

#### 修改建议

通过以下方式可实现列表数据较少时顶部对齐，数据较多时可滚动的效果：

*   **方案一**：Scroll组件设置高度限制，同时增加.align(Alignment.Top)属性。
    
    ```
    @Entry
    @Component
    export struct SceneOne {
      private scroller: Scroller = new Scroller();
      @State arr: string[] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'];
      @State arr1: string[] = ['1', '2', '3', '4'];
      @State arr2: string[] = [];
      @State value: boolean = true;
    
      build() {
        Column() {
          Text('我是标题，点击我更新数据')
            .width('100%')
            .margin({
              top: 10,
              bottom: 10
            })
            .textAlign(TextAlign.Center)
            .onClick(() => {
              // 点击刷新数据
              if (this.value) {
                this.arr2 = this.arr;
              } else {
                this.arr2 = this.arr1;
              }
              this.value = !this.value;
            })
          Scroll(this.scroller) {
            Column() {
              ForEach(this.arr2, (item: string) => {
                Column() {
                  Text('数据' + item)
                    .textAlign(TextAlign.Start)
                    .fontSize(18)
                }
                .width('95%')
                .height(72)
                .backgroundColor(Color.White)
                .borderRadius(12)
                .justifyContent(FlexAlign.Center)
                .margin({
                  top: 6,
                  bottom: 6
                })
              })
            }
          }
          .scrollBar(BarState.Off)
          .width('95%')
          .align(Alignment.Top) // Scroll组件内部子组件按照顶部开始排列
          .layoutWeight(1) // Scroll组件自动占满父组件剩余部分
          .backgroundColor(Color.Gray)
        }
        .width('100%')
        .height('100%')
      }
    }
    ```
    
*   **方案二**：可以采用List组件实现。
    
    List组件在限制过高度后，当其子组件高度小于List组件高度时，默认从顶部开始排布。参考方案一，核心修改代码如下：
    
    ```
    @Entry
    @Component
    export struct SceneTwo {
      @State arr: string[] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'];
      @State arr1: string[] = ['1', '2', '3', '4'];
      @State arr2: string[] = [];
      @State value: boolean = true;
    
      build() {
        Column() {
          Text('我是标题，点击我更新数据')
            .width('100%')
            .margin({
              top: 10,
              bottom: 10
            })
            .textAlign(TextAlign.Center)
            .onClick(() => {
              // 点击刷新数据
              if (this.value) {
                this.arr2 = this.arr;
              } else {
                this.arr2 = this.arr1;
              }
              this.value = !this.value;
            })
          List() {
            ForEach(this.arr2, (item: string) => {
              ListItem() {
                Column() {
                  Column() {
                    Text('数据' + item)
                      .fontSize(18)
                  }
                  .justifyContent(FlexAlign.Center)
                  .width('95%')
                  .height(72)
                  .backgroundColor(Color.White)
                  .borderRadius(12)
                }
                .justifyContent(FlexAlign.Center)
                .width('100%')
              }
              .margin({
                top: 6,
                bottom: 6
              })
            })
          }
          .scrollBar(BarState.Off)
          .width('95%')
          .layoutWeight(1) // List组件自动占满父组件剩余部分
          .backgroundColor(Color.Gray)
        }
        .width('100%')
        .height('100%')
      }
    }
    ```
    

#### 总结

1.  Scroll组件在未设置高度时，其高度跟随子组件高度自适应。可以实现数据量少时，数据从顶部开始排列，但是数据较多时，Scroll组件会默认撑开到屏幕的100%高度，当Scroll上方有其它兄弟组件时，会出现Scroll显示不全的问题。
2.  当Scroll组件设置高度限制后，若子组件高度小于Scroll组件高度，则默认居中排布，可以采用.align(Alignment.Top)通用属性使其从顶部开始排布。
3.  由于List组件在设置高度限制后，若子组件高度小于List组件高度时是默认从顶部开始排布，所以也可以用List组件替代Scroll组件。