构建食物列表List布局

About 3 min

构建食物列表List布局

使用List组件和ForEach循环渲染,构建食物列表布局。

  1. 在pages目录新建页面FoodCategoryList.ets,将index.ets改名为FoodDetail.ets,并将其添加到config.json文件下的pages标签,位于第一序位的页面为首页。

    "js": [
      {
        "pages": [
          "pages/FoodCategoryList",
          "pages/FoodDetail"
        ],
    ]
    
    1
    2
    3
    4
    5
    6
    7
  2. 新建FoodList组件作为页面入口组件,FoodListItem为其子组件。List组件是列表组件,适用于重复同类数据的展示,其子组件为ListItem,适用于展示列表中的单元。

    @Component
    struct FoodListItem {
      build() {}
    }
    
    @Entry
    @Component
    struct FoodList {
      build() {
        List() {
          ListItem() {
            FoodListItem()
          }
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  3. 引入FoodData类和initializeOnStartup方法。

    import { FoodData } from '../model/FoodData'
    import { initializeOnStartup } from '../model/FoodDataModels'
    
    1
    2
  4. FoodList和FoodListItem组件数值传递。在FoodList组件内创建类型为FoodData[]成员变量foodItems,调用initializeOnStartup方法为其赋值。在FoodListItem组件内创建类型为FoodData的成员变量foodItem。将父组件foodItems数组的第一个元素的foodItems[0]作为参数传递给FoodListItem。

    import { FoodData } from '../model/FoodData'
    import { initializeOnStartup } from '../model/FoodDataModels'
    
    @Component
    struct FoodListItem {
      private foodItem: FoodData
      build() {}
    }
    
    @Entry
    @Component
    struct FoodList {
      private foodItems: FoodData[] = initializeOnStartup()
      build() {
        List() {
          ListItem() {
            FoodListItem({ foodItem: this.foodItems[0] })
          }
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  5. 声明子组件FoodListItem 的UI布局。创建Flex组件,包含食物图片缩略图,食物名称,和食物对应的卡路里。

    import { FoodData } from '../model/FoodData'
    import { initializeOnStartup } from '../model/FoodDataModels'
    
    @Component
    struct FoodListItem {
      private foodItem: FoodData
      build() {
        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
          Image(this.foodItem.image)
            .objectFit(ImageFit.Contain)
            .height(40)
            .width(40)
            .backgroundColor('#FFf1f3f5')
            .margin({ right: 16 })
          Text(this.foodItem.name)
            .fontSize(14)
            .flexGrow(1)
          Text(this.foodItem.calories + ' kcal')
            .fontSize(14)
        }
        .height(64)
        .margin({ right: 24, left:32 })
      }
    }
    
    @Entry
    @Component
    struct FoodList {
      private foodItems: FoodData[] = initializeOnStartup()
      build() {
        List() {
          ListItem() {
            FoodListItem({ foodItem: this.foodItems[0] })
          }
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37

    zh-cn_image_0000001204776353

  6. 创建两个FoodListItem。在List组件创建两个FoodListItem,分别给FoodListItem传递foodItems数组的第一个元素this.foodItems[0]和第二个元素foodItem: this.foodItems[1]。

    import { FoodData } from '../model/FoodData'
    import { initializeOnStartup } from '../model/FoodDataModels'
    
    @Component
    struct FoodListItem {
        private foodItem: FoodData
        build() {
            Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
                Image(this.foodItem.image)
                    .objectFit(ImageFit.Contain)
                    .height(40)
                    .width(40)
                    .backgroundColor('#FFf1f3f5')
                    .margin({ right: 16 })
                Text(this.foodItem.name)
                    .fontSize(14)
                    .flexGrow(1)
                Text(this.foodItem.calories + ' kcal')
                    .fontSize(14)
            }
            .height(64)
            .margin({ right: 24, left:32 })
        }
    }
    
    @Entry
    @Component
    struct FoodList {
      private foodItems: FoodData[] = initializeOnStartup()
      build() {
        List() {
          ListItem() {
            FoodListItem({ foodItem: this.foodItems[0] })
          }
          ListItem() {
            FoodListItem({ foodItem: this.foodItems[1] })
          }
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

    zh-cn_image_0000001204537865

  7. 单独创建每一个FoodListItem肯定是不合理的。这就需要引入ForEach循环渲染,ForEach语法如下。

    ForEach(
        arr: any[], // Array to be iterated
        itemGenerator: (item: any) => void, // child component generator
        keyGenerator?: (item: any) => string // (optional) Unique key generator, which is recommended.
    )
    
    1
    2
    3
    4
    5

    ForEach组有三个参数,第一个参数是需要被遍历的数组,第二个参数为生成子组件的lambda函数,第三个参数是键值生成器。出于性能原因,即使第三个参数是可选的,强烈建议开发者提供。keyGenerator使开发框架能够更好地识别数组更改,而不必因为item的更改重建全部节点。

    遍历foodItems数组循环创建ListItem组件,foodItems中每一个item都作为参数传递给FoodListItem组件。

    ForEach(this.foodItems, item => {
        ListItem() {
            FoodListItem({ foodItem: item })
        }
    }, item => item.id.toString())
    
    1
    2
    3
    4
    5

    整体的代码如下。

    import { FoodData } from '../model/FoodData'
    import { initializeOnStartup } from '../model/FoodDataModels'
    
    @Component
    struct FoodListItem {
      private foodItem: FoodData
      build() {
        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
          Image(this.foodItem.image)
            .objectFit(ImageFit.Contain)
            .height(40)
            .width(40)
            .backgroundColor('#FFf1f3f5')
            .margin({ right: 16 })
          Text(this.foodItem.name)
            .fontSize(14)
            .flexGrow(1)
          Text(this.foodItem.calories + ' kcal')
            .fontSize(14)
        }
        .height(64)
        .margin({ right: 24, left:32 })
      }
    }
    
    @Entry
    @Component
    struct FoodList {
      private foodItems: FoodData[] = initializeOnStartup()
      build() {
        List() {
          ForEach(this.foodItems, item => {
            ListItem() {
              FoodListItem({ foodItem: item })
            }
          }, item => item.id.toString())
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
  8. 添加FoodList标题。

    @Entry
    @Component
    struct FoodList {
      private foodItems: FoodData[] = initializeOnStartup()
      build() {
        Column() {
          Flex({justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center}) {
            Text('Food List')
              .fontSize(20)
              .margin({ left:20 })
          }
          .height('7%')
          .backgroundColor('#FFf1f3f5')
          List() {
            ForEach(this.foodItems, item => {
              ListItem() {
                FoodListItem({ foodItem: item })
              }
            }, item => item.id.toString())
          }
          .height('93%')
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    zh-cn_image_0000001169678922