Hongmeng: ContentTable, Stack, Flex and other components and layouts implement image and text display interfaces

Show results

38d92b82951c466c9eea6d47f2e94dd3.png

I. Overview

Follow the official website to continue learning HarmonyOS

This blog post implements a development demo of a food details page

Through this development process, learn how to use container components Stack and Flex and basic components Image and Text , build user-defined components , and complete food introductions with pictures and texts.

2. Build Stack layout

1.Food name

Create Stack component and Text subcomponent

Stack component is a stacked component that can contain one or more sub-components, and its characteristic is that the latter sub-component covers the previous sub-component.

@Entry
@Component
struct MyComponent {
  build() {
    Stack() {
        Text('Tomato')
            .fontSize(26)
            .fontWeight(500)
            .fontColor(Color.White)
    }
  }
}

Previewer effect: 

34e2f012a9084c7f92a12bcabc48097f.png

2. Food pictures

Create an Image component and specify the URL of the Image component

The Text component needs to be displayed above the Image component, so declare the Image component first .

Image resources are placed in the rawfile folder under resources . When referencing resources under rawfile , use the form $rawfile('filename') , where filename is  the relative path of the file in the rawfile  directory.

Currently $rawfile only supports the Image control to reference image resources.

@Entry
@Component
struct MyComponent {
  build() {
    Stack() {
        Image($rawfile('Tomato.png'))
        Text('Tomato')
            .fontSize(26)
            .fontWeight(500)
    }
  }
}

Previewer effect: 

5feee84032c84f249fee04095d06f75c.png

3. Access images through resources

In addition to specifying the image path, you can also use the  media resource reference symbol $r  to reference resources. You need to follow the rules of the resource qualifier of the resources folder.

Right-click the resources folder, click New>Resource Directory , and select Resource Type as Media (picture resources)

Note: The newly created Resource Directory directory can only be under the base directory, but the base directory has media files by default.

Directly put Tomato.png into the media folder, and you can reference the application resources in the form of $r('app.type.name')

Tomato.pngr('app.media.Tomato') .

Code:

@Entry
@Component
struct MyComponent {
  build() {
    Stack() {
      Image($r('app.media.Tomato'))
        .objectFit(ImageFit.Contain)
        .height(357)
      //Image($rawfile('Tomato.png'))

      Text('Tomato')
        .fontSize(26)
        .fontWeight(500)
    }
  }
}

Previewer: 

e3297d18775a46dcae3c8fba449f2caa.png

4.Image component objectFit attribute

In the example, the objectFit attribute of the image is set to ImageFit.Contain , which means that the image is completely displayed within the boundary while maintaining the aspect ratio of the image.

The default property of Image 's objectFit is ImageFit.Cover ,
which means it can be enlarged or reduced while maintaining the aspect ratio so that it fills the entire display boundary.

If you want the Image to fill the entire screen, the reasons are as follows:
    1. The width and height of the Image are not set.
    2. The objectFit attribute uses the default value ImageFit.Cover

5. Set Stack layout properties

Stack is aligned in the center by default . In this example, it is modified to be aligned at the bottom and starting end .
Set the Stack construction parameter alignContent to Alignment.BottomStart.

Alignment , like FontWeight , is a built-in enumeration type provided by the framework.

Code:

@Entry
@Component
struct MyComponent {
  build() {
    Stack({ alignContent: Alignment.BottomStart }) {
      Image($r('app.media.Tomato'))
        .objectFit(ImageFit.Contain)
        .height(357)
      //Image($rawfile('Tomato.png'))

      Text('Tomato')
        .fontSize(26)
        .fontWeight(500)
    }
  }
}

Previewer:  

fe28ca1159ef4d879955177cfe18a768.png

6. Adjust the margin of the Text component

The margin property adjusts the component's outer margins

(1).margin(Length) , that is, the outer margins of the top, right, bottom, and left sides are all Length .

(2).margin  { top?: Length,
                    right?: Length,
                    bottom?: Length,
                    left?:Length }, that is, specify the margins of the four sides respectively

Code:

@Entry
@Component
struct MyComponent {
  build() {
    Stack({ alignContent: Alignment.BottomStart }) {
        Image($r('app.media.Tomato'))
            .objectFit(ImageFit.Contain)
            .height(357)
        Text('Tomato')
            .fontSize(26)
            .fontWeight(500)
            .margin({left: 26, bottom: 17.4})
    }   
  }
}

Previewer:

8509562c2d0542e6933b8586e782ec8b.png

6. Adjust the structure between components and semanticize component names

Create the page entry component as FoodDetail , create Column in FoodDetail , and set alignItems (HorizontalAlign.Center) to be centered in the horizontal direction.

The name of the MyComponent component was changed to FoodImageDisplay , which is a subcomponent of  FoodDetail.

Column is a container component with sub-components arranged vertically. It is essentially a linear layout, so the alignment can only be set in the cross-axis direction.

Code:

@Component
struct FoodImageDisplay {
  build() {
    Stack({ alignContent: Alignment.BottomStart }) {
      Image($r('app.media.Tomato'))
        .objectFit(ImageFit.Contain)
      Text('Tomato')
        .fontSize(26)
        .fontWeight(500)
        .margin({ left: 26, bottom: 17.4 })
    }
    .height(357)
  }
}

@Entry
@Component
struct FoodDetail {
  build() {
    Column() {
      FoodImageDisplay()
    }
    .alignItems(HorizontalAlign.Center)
  }
}

Previewer:

3c7653ab992b4f8a9d23fb5b12497f65.png

3. Build Flex layout

Flex: flexible layout

Use Flex elastic layout to build the food ingredient list of food,

The advantage of flexible layout in this scenario is that it can eliminate unnecessary width and height calculations and set the size of different cells through proportion, which is more flexible.

1. Create a new ContentTable component

Create a new ContentTable component and make it a subcomponent of the page entry component FoodDetail .

Code:

@Component
struct FoodImageDisplay {
  build() {
    Stack({ alignContent: Alignment.BottomStart }) {
      Image($r('app.media.Tomato'))
        .objectFit(ImageFit.Contain)
        .height(357)
      Text('Tomato')
        .fontSize(26)
        .fontWeight(500)
        .margin({ left: 26, bottom: 17.4 })
    }
  }
}

@Component
struct ContentTable {
  build() {}
}

@Entry
@Component
struct FoodDetail {
  build() {
    Column() {
      FoodImageDisplay()
      ContentTable()
    }
    .alignItems(HorizontalAlign.Center)
  }
}

Previewer:

The ContentTable subcomponent is empty and has not been filled with content. The current Previewer effect is the same as the previous section.

2. Create a Flex component to display two types of components in Tomato

One category is Calories : Calories;

One category is nutrition , including: protein (Protein),
                                                      fat (Fat),
                                                      carbohydrates (Carbohydrates)
                                                      vitamin C (VitaminC).

First create the heat category
. Create a new Flex component with a height of 280 and a top, right and left inner margin of 30.
It contains three Text sub-components representing: category name (Calories)
                                                  content name (Calories)
                                                  content value (17kcal)
Flex Components are arranged horizontally by default.

ContentTable code:

@Component
struct ContentTable {
  build() {
    Flex() {
      Text('Calories')
        .fontSize(17.4)
        .fontWeight(FontWeight.Bold)
      Text('Calories')
        .fontSize(17.4)
      Text('17kcal')
        .fontSize(17.4)
    }
    .height(280)
    .padding({ top: 30, right: 30, left: 30 })
  }
}

Previewer:

501da9cb100245a6bae2d7d7d108a657.png

3. Adjust the layout and set the proportion of each part

The category name proportion (layoutWeight) is 1,

The total proportion of ingredient name and ingredient content (layoutWeight) is 2.

The ingredient name and ingredient content are located in the same Flex, and the ingredient name occupies all remaining space flexGrow (1).

ContentTable code:

@Component
struct ContentTable {
  build() {
    Flex() {
      Text('Calories')
        .fontSize(17.4)
        .fontWeight(FontWeight.Bold)
        .layoutWeight(1)
      Flex() {
        Text('Calories')
          .fontSize(17.4)
          .flexGrow(1)
        Text('17kcal')
          .fontSize(17.4)
      }
      .layoutWeight(2)
    }
    .height(280)
    .padding({ top: 30, right: 30, left: 30 })
  }
}

Previewer:

9ca5951920fa48509f45d094cc408cbe.png

4. Create a nutritional classification based on the calorie classification

The nutrition part (Nutrition) includes:
                  protein (Protein),
                  fat (Fat),
                  carbohydrates (Carbohydrates)
                  vitamin C (VitaminC)

Set the outer Flex to be arranged vertically  . FlexDirection.Column
is arranged equidistantly in the main axis direction (vertical direction).  FlexAlign.SpaceBetween
is aligned in the cross axis direction (horizontal axis direction).  ItemAlign.Start

ContentTable code:

@Component
struct ContentTable {
  build() {
    Flex({ direction: FlexDirection.Column,
           justifyContent: FlexAlign.SpaceBetween,
           alignItems: ItemAlign.Start }) {
      Flex() {
        Text('Calories')
          .fontSize(17.4)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        Flex() {
          Text('Calories')
            .fontSize(17.4)
            .flexGrow(1)
          Text('17kcal')
            .fontSize(17.4)
        }
        .layoutWeight(2)
      }

      Flex() {
        Text('Nutrition')
          .fontSize(17.4)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        Flex() {
          Text('Protein')
            .fontSize(17.4)
            .flexGrow(1)
          Text('0.9g')
            .fontSize(17.4)
        }
        .layoutWeight(2)
      }

      Flex() {
        Text(' ')
          .fontSize(17.4)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        Flex() {
          Text('Fat')
            .fontSize(17.4)
            .flexGrow(1)
          Text('0.2g')
            .fontSize(17.4)
        }
        .layoutWeight(2)
      }

      Flex() {
        Text(' ')
          .fontSize(17.4)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        Flex() {
          Text('Carbohydrates')
            .fontSize(17.4)
            .flexGrow(1)
          Text('3.9g')
            .fontSize(17.4)
        }
        .layoutWeight(2)
      }

      Flex() {
        Text(' ')
          .fontSize(17.4)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        Flex() {
          Text('vitaminC')
            .fontSize(17.4)
            .flexGrow(1)
          Text('17.8mg')
            .fontSize(17.4)
        }
        .layoutWeight(2)
      }
    }
    .height(280)
    .padding({ top: 30, right: 30, left: 30 })
  }
}

Previewer:

38d92b82951c466c9eea6d47f2e94dd3.png

5. Optimize code

It can be found that the ingredient units in each ingredient list actually have the same UI structure.

2c75982c6a0849be90ac1400fa46ee09.png

Code can be streamlined through custom @Builder functions

Use custom @Builder to abstract the same UI structure. The methods decorated by
@Builder and the build method of Component are both used to declare some UI rendering structures and follow the same ArkTS syntax.

You can define one or more methods decorated with @Builder, but Component must have only one build method .

Declare the IngredientItem method modified by @Builder in the ContentTable , which is used to declare the category name, ingredient name and ingredient content UI description.

@Component
struct ContentTable {
  @Builder IngredientItem(title:string, name: string, value: string) {
    Flex() {
      Text(title)
        .fontSize(17.4)
        .fontWeight(FontWeight.Bold)
        .layoutWeight(1)
      Flex({ alignItems: ItemAlign.Center }) {
        Text(name)
          .fontSize(17.4)
          .flexGrow(1)
        Text(value)
          .fontSize(17.4)
      }
      .layoutWeight(2)
    }
  }
}

When calling the IngredientItem interface in the build method of ContentTable , you need to use this to call methods in the Component scope to distinguish global method calls.

@Component
struct ContentTable {
  ......
  build() {
    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
      this.IngredientItem('Calories', 'Calories', '17kcal')
      this.IngredientItem('Nutrition', 'Protein', '0.9g')
      this.IngredientItem('', 'Fat', '0.2g')
      this.IngredientItem('', 'Carbohydrates', '3.9g')
      this.IngredientItem('', 'VitaminC', '17.8mg')
    }
    .height(280)
    .padding({ top: 30, right: 30, left: 30 })
  }
}

The full code of the ContentTable component is as follows:

@Component
struct ContentTable {
  @Builder
  IngredientItem(title:string, name: string, value: string) {
    Flex() {
      Text(title)
        .fontSize(17.4)
        .fontWeight(FontWeight.Bold)
        .layoutWeight(1)
      Flex() {
        Text(name)
          .fontSize(17.4)
          .flexGrow(1)
        Text(value)
          .fontSize(17.4)
      }
      .layoutWeight(2)
    }
  }

  build() {
    Flex({ direction: FlexDirection.Column,
           justifyContent: FlexAlign.SpaceBetween,
           alignItems: ItemAlign.Start }) {
      this.IngredientItem('Calories', 'Calories', '17kcal')
      this.IngredientItem('Nutrition', 'Protein', '0.9g')
      this.IngredientItem('', 'Fat', '0.2g')
      this.IngredientItem('', 'Carbohydrates', '3.9g')
      this.IngredientItem('', 'VitaminC', '17.8mg')
    }
    .height(280)
    .padding({ top: 30, right: 30, left: 30 })
  }
}

Previewer:

This section only optimizes the code, and the implementation effect is the same as the previous section.

4. Conclusion

Stack layout and Flex layout have completed the graphic display and nutritional information table of food, and built the first normal view food details page.

The next blog post will continue to follow the official website, develop the food classification list page, and complete the jump and data transfer of the food classification list page and the food details page.

Guess you like

Origin blog.csdn.net/geyichongchujianghu/article/details/134491958