【Journey of HarmonyOS Next】Developing Based on ArkTS (2) – > Common layouts for UI development



This content originally appeared on DEV Community and was authored by ZHZL-m

Image description

1 -> Adaptive layout

1.1 -> Linear layout

LinearLayout is the most commonly used layout in development. The subcomponents of a linear layout are arranged sequentially in the linear direction (horizontal and vertical).

Linear layout is achieved through linear container rows and columns. The sub-components in the Column container are arranged in the vertical direction, and in the Row component, the sub-components are arranged in the horizontal direction.

1.1.1 -> Arrangement of linear layouts

The direction of the linear layout is determined by the selected container components. Depending on the arrangement direction, choose to use the Row or Column container to create a linear layout, adjust the spacing of the sub-components by adjusting the space, alignItems, justifyContent properties, and align the horizontal and vertical directions.

The space parameter sets the spacing of the subassemblies on the spindle (direction of arrangement). Achieve the effect of equal spacing of each sub-component in the direction of arrangement.
The alignItems property sets the alignment of the subcomponents on the intersection axis (the vertical direction of the alignment direction). And the performance is consistent across all screen sizes. If the intersection axis is vertical, the value is VerticalAlign and the horizontal value is HorizontalAlign.
The alignment of the subcomponents on the main axis (direction of arrangement) is set via the justifyContent property. Achieve adaptive equalization of layouts. The value in the example is FlexAlign.

1.1.2 -> Adaptive stretching

Under the linear layout, the commonly used blank filling component Blank automatically fills the blank space in the direction of the container spindle to achieve the adaptive stretching effect.

@Entry
@Component
struct BlankExample {
  build() {
    Column() {
      Row() {
        Text('Bluetooth').fontSize(18)
        Blank()
        Toggle({ type: ToggleType.Switch, isOn: true })
      }.backgroundColor(0xFFFFFF).borderRadius(15).padding({ left: 12 }).width('100%')
    }.backgroundColor(0xEFEFEF).padding(20).width('100%')
  }
}

1.1.3 -> Adaptive scaling

Adaptive scaling refers to the fact that in a variety of devices of different sizes, the size of sub-components changes according to a preset scale, and the size changes with the size of the container. This is done in the following way in a linear layout:

  1. When the size of the parent container is determined, the child components and sibling elements with the layoutWeight attribute set are allocated according to the weight, ignoring the size setting of the element itself, and the remaining space is automatically occupied under any size device.
@Entry
@Component
struct layoutWeightExample {
  build() {
    Column() {
      Text('1:2:3').width('100%')
      Row() {
        Column() {
          Text('layoutWeight(1)')
            .textAlign(TextAlign.Center)
        }.layoutWeight(2).backgroundColor(0xffd306).height('100%')

        Column() {
          Text('layoutWeight(2)')
            .textAlign(TextAlign.Center)
        }.layoutWeight(4).backgroundColor(0xffed97).height('100%')

        Column() {
          Text('layoutWeight(6)')
            .textAlign(TextAlign.Center)
        }.layoutWeight(6).backgroundColor(0xffd306).height('100%')

      }.backgroundColor(0xffd306).height('30%')

      Text('2:5:3').width('100%')
      Row() {
        Column() {
          Text('layoutWeight(2)')
            .textAlign(TextAlign.Center)
        }.layoutWeight(2).backgroundColor(0xffd306).height('100%')

        Column() {
          Text('layoutWeight(5)')
            .textAlign(TextAlign.Center)
        }.layoutWeight(5).backgroundColor(0xffed97).height('100%')

        Column() {
          Text('layoutWeight(3)')
            .textAlign(TextAlign.Center)
        }.layoutWeight(3).backgroundColor(0xffd306).height('100%')
      }.backgroundColor(0xffd306).height('30%')
    }
  }
}
  1. When determining the size of the parent container, use the percentage to set the width of the child component and the sibling component to ensure the adaptive proportion of each element at any size.
@Entry
@Component
struct WidthExample {
  build() {
    Column() {
      Row() {
        Column() {
          Text('left width 20%')
            .textAlign(TextAlign.Center)
        }.width('20%').backgroundColor(0xffd306).height('100%')

        Column() {
          Text('center width 50%')
            .textAlign(TextAlign.Center)
        }.width('50%').backgroundColor(0xffed97).height('100%')

        Column() {
          Text('right width 30%')
            .textAlign(TextAlign.Center)
        }.width('30%').backgroundColor(0xffd306).height('100%')
      }.backgroundColor(0xffd306).height('30%')
    }
  }
}

In the example above, in a device of any size, the width of the sub-component is fixed.

1.1.4 -> Positioning capabilities

Relative positioning

Relative positioning can be achieved using the offset property of the component, setting the offset of the element relative to itself. This property is set so that the parent container layout is not affected, and only the position is adjusted when drawn. Most of the layout development can be achieved using linear layouts and offsets.

@Entry
@Component
struct OffsetExample {
  @Styles eleStyle() {
    .size({ width: 120, height: '50' })
    .backgroundColor(0xbbb2cb)
    .border({ width: 1 })
  }

  build() {
    Column({ space: 20 }) {
      Row() {
        Text('1').size({ width: '15%', height: '50' }).backgroundColor(0xdeb887).border({ width: 1 }).fontSize(16)
        Text('2  offset(15, 30)')
          .eleStyle()
          .fontSize(16)
          .align(Alignment.Start)
          .offset({ x: 15, y: 30 })
        Text('3').size({ width: '15%', height: '50' }).backgroundColor(0xdeb887).border({ width: 1 }).fontSize(16)
        Text('4 offset(-10%, 20%)')
          .eleStyle()
          .fontSize(16)
          .offset({ x: '-5%', y: '20%' })
      }.width('90%').height(150).border({ width: 1, style: BorderStyle.Dashed })
    }
    .width('100%')
    .margin({ top: 25 })
  }
} 

Absolute positioning
In a linear layout, you can use the positon property of the component to implement an absolute layout (AbsoluteLayout) by setting the upper-left corner of the element relative to the upper-left corner of the parent container. For devices of different sizes, the adaptability of using absolute positioning will be poor, and there will be defects in the adaptation of the screen.

@Entry
@Component
struct PositionExample {
  @Styles eleStyle(){
    .backgroundColor(0xbbb2cb)
    .border({ width: 1 })
    .size({ width: 120, height: 50 })
  }

  build() {
    Column({ space: 20 }) {
      // 设置子组件左上角相对于父组件左上角的偏移位置
      Row() {
        Text('position(30, 10)')
          .eleStyle()
          .fontSize(16)
          .position({ x: 10, y: 10 })

        Text('position(50%, 70%)')
          .eleStyle()
          .fontSize(16)
          .position({ x: '50%', y: '70%' })

        Text('position(10%, 90%)')
          .eleStyle()
          .fontSize(16)
          .position({ x: '10%', y: '80%' })
      }.width('90%').height('100%').border({ width: 1, style: BorderStyle.Dashed })
    }
    .width('90%').margin(25)
  }
}

1.1.5 -> Adaptive extension

Adaptive extension is a display that can be dragged by scrolling bars when the number of displayed content on the page is different and extends off-screen under different sizes of devices. It is suitable for scenarios where the content cannot be displayed on one screen in a linear layout. There are two common types of implementations.

List component

When there are too many children in the list and cannot be placed on one screen, the undisplayed children are displayed by dragging and dragging the scroll bar. The scrollBar property sets the permanent state of the scrollbar, and the edgeEffect property sets the rebound effect of dragging to the limit.

Vertical List:

  @Entry
  @Component
  struct ListExample1 {
    @State arr: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]
    @State alignListItem: ListItemAlign = ListItemAlign.Start

    build() {
      Column() {
        List({ space: 20, initialIndex: 0 }) {
          ForEach(this.arr, (item) => {
            ListItem() {
              Text('' + item)
                .width('100%')
                .height(100)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
            .border({ width: 2, color: Color.Green })
          }, item => item)
        }
        .border({ width: 2, color: Color.Red, style: BorderStyle.Dashed })
        .scrollBar(BarState.On) // 滚动条常驻
        .edgeEffect(EdgeEffect.Spring) // 滚动到边缘再拖动回弹效果

      }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding(20)
    }
  }

Horizontal List:

  @Entry
  @Component
  struct ListExample2 {
    @State arr: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]
    @State alignListItem: ListItemAlign = ListItemAlign.Start

    build() {
      Column() {
        List({ space: 20, initialIndex: 0 }) {
          ForEach(this.arr, (item) => {
            ListItem() {
              Text('' + item)
                .height('100%')
                .width(100)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
            .border({ width: 2, color: Color.Green })
          }, item => item)
        }
        .border({ width: 2, color: Color.Red, style: BorderStyle.Dashed })
        .scrollBar(BarState.On) // 滚动条常驻
        .edgeEffect(EdgeEffect.Spring) // 滚动到边缘再拖动回弹效果
        .listDirection(Axis.Horizontal)  // 列表水平排列
      }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding(20)
    }
  } 

Scroll component
In a linear layout, content can be scrolled when the layout size of a child component exceeds the size of the parent component. A scrollable container component is wrapped around a Column or Row.

Portrait Scroll:

@Entry
@Component
struct ScrollExample {
  scroller: Scroller = new Scroller();
  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  build() {
    Scroll(this.scroller) {
      Column() {
        ForEach(this.arr, (item) => {
          Text(item.toString())
            .width('90%')
            .height(150)
            .backgroundColor(0xFFFFFF)
            .borderRadius(15)
            .fontSize(16)
            .textAlign(TextAlign.Center)
            .margin({ top: 10 })
        }, item => item)
      }.width('100%')
    }
    .backgroundColor(0xDCDCDC)
    .scrollable(ScrollDirection.Vertical) // 滚动方向纵向
    .scrollBar(BarState.On) // 滚动条常驻显示
    .scrollBarColor(Color.Gray) // 滚动条颜色
    .scrollBarWidth(30) // 滚动条宽度
    .edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹
  }
}

Horizontal Scroll:

@Entry
@Component
struct ScrollExample {
  scroller: Scroller = new Scroller();
  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  build() {
    Scroll(this.scroller) {
      Row() {
        ForEach(this.arr, (item) => {
          Text(item.toString())
            .height('90%')
            .width(150)
            .backgroundColor(0xFFFFFF)
            .borderRadius(15)
            .fontSize(16)
            .textAlign(TextAlign.Center)
            .margin({ left: 10 })
        }, item => item)
      }.height('100%')
    }
    .backgroundColor(0xDCDCDC)
    .scrollable(ScrollDirection.Horizontal) // 滚动方向横向
    .scrollBar(BarState.On) // 滚动条常驻显示
    .scrollBarColor(Color.Gray) // 滚动条颜色
    .scrollBarWidth(30) // 滚动条宽度
    .edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹
  }
}

1.2 -> Cascading layout

StackLayout is used to reserve an area on the screen to display the elements in the component, providing a layout where the elements can overlap. Through the stacking of containers, the child elements in the container are put into the stack in turn, and the next child element overwrites the previous child element.

1.2.1 -> Z-order control

The display hierarchy of sibling components in a stack container can be changed by using the zIndex property. The larger the zIndex value, the higher the display level, that is, the component with a large zIndex value will be overlaid on the component with a small zIndex value.

In a cascading layout, if the size of the later child element is larger than the size of the previous child element, the front child element is completely hidden.

Stack({ alignContent: Alignment.BottomStart }) {
    Column() {
      Text('Stack子元素1').textAlign(TextAlign.End).fontSize(20)
    }.width(100).height(100).backgroundColor(0xffd306)
    Column() {
      Text('Stack子元素2').fontSize(20)
    }.width(150).height(150).backgroundColor(Color.Pink)
    Column() {
      Text('Stack子元素3').fontSize(20)
    }.width(200).height(200).backgroundColor(Color.Grey)
}.margin({ top: 100 }).width(350).height(350).backgroundColor(0xe0e0e0)

In the image above, the size of the last child element 3 is larger than all the previous child elements, so the first two elements are completely hidden. After changing the zIndex attribute of child element 1 and child element 2, the element can be displayed.

Stack({ alignContent: Alignment.BottomStart }) {
    Column() {
      Text('Stack子元素1').textAlign(TextAlign.End).fontSize(20)
    }.width(100).height(100).backgroundColor(0xffd306).zIndex(2)
    Column() {
      Text('Stack子元素2').fontSize(20)
    }.width(150).height(150).backgroundColor(Color.Pink).zIndex(1)
    Column() {
      Text('Stack子元素3').fontSize(20)
    }.width(200).height(200).backgroundColor(Color.Grey)
}.margin({ top: 100 }).width(350).height(350).backgroundColor(0xe0e0e0)

1.3 -> Flexible layout

Flex Layout is the most flexible layout used in Adaptive Layout. Flexible layouts provide a more efficient way to arrange, align, and allocate empty space for subcomponents in a container. Flexible layout

Containers:The Flex component is used as a container for Flex layouts to set layout-related properties.

Subcomponents:A subcomponent within a Flex component automatically becomes a subcomponent of the layout.

Spindle:The axis of the Flex assembly layout direction, and the subassemblies are arranged along the main axis by default. The position where the spindle starts is called the spindle start and the end position is called the spindle end.

Cross axis: An axis line perpendicular to the direction of the principal axis. The position where the cross axis starts is called the head of the cross axis, and the end position is called the tail of the cross axis.

Image description

Container component properties

Create a flexible layout by using the Flex API provided by the Flex component. As follows:

Flex(options?: { direction?: FlexDirection, wrap?: FlexWrap, justifyContent?: FlexAlign, alignItems?: ItemAlign, alignContent?: FlexAlign })

1.3.1 -> Flexible layout orientation

The parameter direction determines the direction of the spindle, i.e. the direction in which the subassemblies are arranged. The available values are:

Image description

FlexDirection.Row (default): The main axis is horizontal, and the subassemblies are arranged horizontally from the starting end.

Flex({ direction: FlexDirection.Row }) {
  Text('1').width('33%').height(50).backgroundColor(0xF5DEB3)
  Text('2').width('33%').height(50).backgroundColor(0xD2B48C)
  Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)

FlexDirection.RowReverse: The main axis is horizontal, and the subassembly runs along the FlexDirection from the end end. Rows start to be arranged in the opposite direction.

Flex({ direction: FlexDirection.RowReverse }) {
  Text('1').width('33%').height(50).backgroundColor(0xF5DEB3)
  Text('2').width('33%').height(50).backgroundColor(0xD2B48C)
  Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)

FlexDirection.Column: The main axis is vertical, and the subassemblies are arranged vertically from the starting end.

Flex({ direction: FlexDirection.Column }) {
  Text('1').width('100%').height(50).backgroundColor(0xF5DEB3)
  Text('2').width('100%').height(50).backgroundColor(0xD2B48C)
  Text('3').width('100%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)

FlexDirection.ColumnReverse: The main axis is vertical, and the subassembly follows FlexDirection from the end end. Columns begin to be arranged in the opposite direction.

Flex({ direction: FlexDirection.ColumnReverse }) {
  Text('1').width('100%').height(50).backgroundColor(0xF5DEB3)
  Text('2').width('100%').height(50).backgroundColor(0xD2B48C)
  Text('3').width('100%').height(50).backgroundColor(0xF5DEB3)
}
.height(70)
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)

1.3.2 -> Flexible layout line wrapping

By default, subassemblies are arranged on a line (also known as an “axis”) in a Flex container. Use the wrap parameter to set the wrapping mode of the subcomponent. The available values are:

FlexWrap. NoWrap (default): No line breaks. If the sum of the widths of the child components is greater than the width of the parent element, the child components are compressed in width.

Flex({ wrap: FlexWrap.NoWrap }) {
  Text('1').width('50%').height(50).backgroundColor(0xF5DEB3)
  Text('2').width('50%').height(50).backgroundColor(0xD2B48C)
  Text('3').width('50%').height(50).backgroundColor(0xF5DEB3)
} 
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)

FlexWrap. Wrap: Wraps, where each row of sub-components is arranged in the direction of the main axis.

Flex({ wrap: FlexWrap.Wrap }) {
  Text('1').width('50%').height(50).backgroundColor(0xF5DEB3)
  Text('2').width('50%').height(50).backgroundColor(0xD2B48C)
  Text('3').width('50%').height(50).backgroundColor(0xD2B48C)
} 
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)

FlexWrap. WrapReverse: Wrap the line in which each row of sub-components is arranged in the opposite direction of the main axis.

Flex({ wrap: FlexWrap.WrapReverse}) {
  Text('1').width('50%').height(50).backgroundColor(0xF5DEB3)
  Text('2').width('50%').height(50).backgroundColor(0xD2B48C)
  Text('3').width('50%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding(10)
.backgroundColor(0xAFEEEE)

1.3.3 -> Flexible layout alignment

Spindle alignment

There are six scenarios for setting the alignment in the direction of the main axis via the justifyContent parameter:

Image description

FlexAlign.Start (default): The child assembly is aligned at the start of the major axis direction, the first child component is aligned to the edge of the parent element, and the other elements are aligned to the previous element.

Flex({ justifyContent: FlexAlign.Start }) {  
  Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)
  Text('2').width('20%').height(50).backgroundColor(0xD2B48C)    
  Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)

FlexAlign.Center: The subassembly is centered in the direction of the main axis.

Flex({ justifyContent: FlexAlign.Center }) {  
  Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)  
  Text('2').width('20%').height(50).backgroundColor(0xD2B48C)   
  Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)

FlexAlign.End: The child assembly is aligned at the end end of the major axis direction, the last child assembly is aligned to the edge of the parent element, and the other elements are aligned to the next element.

Flex({ justifyContent: FlexAlign.End }) {  
  Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)  
  Text('2').width('20%').height(50).backgroundColor(0xD2B48C)   
  Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)

FlexAlign.SpaceBetween:The Flex spindle is evenly distributed in the direction of the elastic elements, with the same distance between adjacent subassemblies. The first and last child components are aligned to the edge of the parent element.

Flex({ justifyContent: FlexAlign.SpaceBetween }) {  
  Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)  
  Text('2').width('20%').height(50).backgroundColor(0xD2B48C)   
  Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)

FlexAlign.SpaceAround:The Flex spindle is evenly distributed in the direction of the elastic elements, with the same distance between adjacent subassemblies. The distance from the first subassembly to the start of the spindle and the distance from the last subassembly to the end of the spindle are half the distance between adjacent elements.

Flex({ justifyContent: FlexAlign.SpaceAround }) {  
  Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)  
  Text('2').width('20%').height(50).backgroundColor(0xD2B48C)   
  Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)

FlexAlign.SpaceEvenly:Flex spindle direction elements are evenly spaced with equal spacing between adjacent subassemblies, the spacing between the first subassembly and the start of the spindle, and the spacing from the last subassembly to the end of the spindle.

Flex({ justifyContent: FlexAlign.SpaceEvenly }) {  
  Text('1').width('20%').height(50).backgroundColor(0xF5DEB3)  
  Text('2').width('20%').height(50).backgroundColor(0xD2B48C)   
  Text('3').width('20%').height(50).backgroundColor(0xF5DEB3)
}
.width('90%')
.padding({ top: 10, bottom: 10 })
.backgroundColor(0xAFEEEE)

Cross axis alignment

Both containers and subassemblies can set cross-axis alignment, and the alignment set by subassemblies takes precedence.

The container assembly sets up cross-axis alignment

You can set the alignment of the subassemblies on the cross axis via the alignItems parameter of the Flex component, with the following values:

ItemAlign.Auto: Uses the default configuration in the Flex container.

Flex({ alignItems: ItemAlign.Auto }) {  
  Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)  
  Text('2').width('33%').height(40).backgroundColor(0xD2B48C)  
  Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)

ItemAlign.Start: Aligns the head of the cross axis direction.

Flex({ alignItems: ItemAlign.Start }) {  
  Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)  
  Text('2').width('33%').height(40).backgroundColor(0xD2B48C)  
  Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)

ItemAlign.Center: Center alignment in the direction of the cross axis.

Flex({ alignItems: ItemAlign.Center }) {  
  Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)  
  Text('2').width('33%').height(40).backgroundColor(0xD2B48C)  
  Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)

ItemAlign.End: Aligns the bottom in the direction of the cross axis.

Flex({ alignItems: ItemAlign.End }) {  
  Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)  
  Text('2').width('33%').height(40).backgroundColor(0xD2B48C)  
  Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)

ItemAlign.Stretch: Stretches the fill in the cross-axis direction, extruding to the container size when no size is set.

Flex({ alignItems: ItemAlign.Stretch }) {  
  Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)  
  Text('2').width('33%').height(40).backgroundColor(0xD2B48C)  
  Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)

ItemAlign. Baseline: Baseline alignment of text in the direction of the cross axis.

Flex({ alignItems: ItemAlign.Baseline }) {  
  Text('1').width('33%').height(30).backgroundColor(0xF5DEB3)  
  Text('2').width('33%').height(40).backgroundColor(0xD2B48C)  
  Text('3').width('33%').height(50).backgroundColor(0xF5DEB3)
}
.size({width: '90%', height: 80})
.padding(10)
.backgroundColor(0xAFEEEE)

The subassembly sets up cross-axis alignment

The alignSelf property of a child component can also set the alignment format of the child component on the cross axis of the parent container, and will override the default configuration of alignItems in the Flex layout container. As shown in the following example:

Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) { //容器组件设置子组件居中
  Text('alignSelf Start').width('25%').height(80)
    .alignSelf(ItemAlign.Start)
    .backgroundColor(0xF5DEB3)
  Text('alignSelf Baseline')
    .alignSelf(ItemAlign.Baseline)
    .width('25%')
    .height(80)
    .backgroundColor(0xD2B48C)
  Text('alignSelf Baseline').width('25%').height(100)
    .backgroundColor(0xF5DEB3)
    .alignSelf(ItemAlign.Baseline)
  Text('no alignSelf').width('25%').height(100)
    .backgroundColor(0xD2B48C)
  Text('no alignSelf').width('25%').height(100)
    .backgroundColor(0xF5DEB3)

}.width('90%').height(220).backgroundColor(0xAFEEEE)

In the above example, if the alignItems alignment of the cross-axis child component is set to the center in the alignItems in the Flex container, and the alignSelf property is set to the child component, the alignItem value of the parent component is overridden, which is the definition of alignSelf.

Content alignment

You can use the alignContent parameter to set the alignment of each row of the subassembly in the remaining space of the cross axis, which only takes effect in the flex layout of multiple rows, and the optional values are:

FlexAlign.Start: Each row of the subassembly is aligned to the start of the cross axis.

Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.Start }) {
  Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
  Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
  Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)          

FlexAlign.Center: The rows of the subassembly are centered in the direction of the cross axis.

Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.Center }) {
  Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
  Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
  Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)          

FlexAlign.End: Each row of the subassembly is aligned to the end of the cross axis.

Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.End }) {
  Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
  Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
  Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)          

FlexAlign.SpaceBetween: The rows of the subassembly are aligned to the ends of the cross axis, and the vertical spacing between the rows is evenly distributed.

Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceBetween }) {
  Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
  Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
  Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)          

FlexAlign.SpaceAround: The subassembly rows are evenly spaced, twice the distance between the first and last rows of the element and the ends of the intersection axis.

Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceAround }) {
  Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
  Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
  Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)          

FlexAlign.SpaceEvenly: The spacing between the rows of the subassembly, with the first and last rows of the subassembly being equal to the ends of the cross axis.

Flex({ justifyContent: FlexAlign.SpaceBetween, wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceEvenly }) {
  Text('1').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('2').width('60%').height(20).backgroundColor(0xD2B48C)
  Text('3').width('40%').height(20).backgroundColor(0xD2B48C)
  Text('4').width('30%').height(20).backgroundColor(0xF5DEB3)
  Text('5').width('20%').height(20).backgroundColor(0xD2B48C)
}
.width('90%')
.height(100)
.backgroundColor(0xAFEEEE)          

1.4 -> Grid layout

GridLayout is an important layout in adaptive layout, which has strong page equalization ability and subcomponent proportion control ability. Grid is used to set grid layout parameters, and GridItem defines the characteristics of the child component. The advantages are as follows:

When the size of the container component changes, all sub-components and spacing are adjusted proportionally to achieve the adaptability of the layout.
You can customize the number of rows and columns in the grid layout, as well as the proportion of the size of each row and column.
Support for setting the row and column spacing of subcomponents in a grid layout.
You can set a subcomponent to span several rows or columns.

1.4.1 -> Grid settings for container components

Proportion of the number of rows and columns

Use the columnsTemplate and rowTemplate properties of the Grid component to set the ratio of the number of rows and columns to the size of the grid layout.

The following takes columnsTemplate as an example to introduce the setting of this property, the value of this property is a string spliced by multiple spaces and ‘number + fr’ intervals, the number of fr is the number of columns in the grid layout, and the size of the value in front of fr is used to calculate the proportion of the column in the width of the grid layout, and finally determine the width of the column.

struct GridExample {
  @State Number: Array<string> = ['1', '2', '3', '4']

  build() {
    Column({ space: 5 }) {
      Grid() {
        ForEach(this.Number, (num: string) => {
          GridItem() {
            Text(`列${num}`)
              .fontSize(16)
              .textAlign(TextAlign.Center)
              .backgroundColor(0xd0d0d0)
              .width('100%')
              .height('100%')
              .borderRadius(5)
          }
        })
      }
      .columnsTemplate('1fr 1fr 1fr 1fr')
      .rowsTemplate('1fr')
      .columnsGap(10)
      .rowsGap(20)
      .width('90%')
      .backgroundColor(0xF0F0F0)
      .height(100)
    }.width('100%')
  }
}

Four equally divided columns are defined, each with equal width.

struct GridExample {
  @State Number: Array<string> = ['1', '2', '3', '4']

  build() {
    Column({ space: 5 }) {
      Grid() {
        ForEach(this.Number, (num: string) => {
          GridItem() {
            Text(`列${num}`)
              .fontSize(16)
              .textAlign(TextAlign.Center)
              .backgroundColor(0xd0d0d0)
              .width('100%')
              .height('100%')
              .borderRadius(5)
          }
        })
      }
      .columnsTemplate('1fr 2fr 3fr 4fr')
      .rowsTemplate('1fr')
      .columnsGap(10)
      .rowsGap(20)
      .width('90%')
      .backgroundColor(0xF0F0F0)
      .height(100)
    }.width('100%')
  }
}

Four columns are defined, each with a width ratio of 1:2:3:4.


struct GridExample {
  @State Number: Array<string> = ['1', '2', '3']

  build() {
    Column({ space: 5 }) {
      Grid() {
        ForEach(this.Number, (num: string) => {
          GridItem() {
            Text(`列${num}`)
              .fontSize(16)
              .textAlign(TextAlign.Center)
              .backgroundColor(0xd0d0d0)
              .width('100%')
              .height('100%')
              .borderRadius(5)
          }
        })
      }
      .columnsTemplate('4fr 2fr 3fr')
      .rowsTemplate('1fr')
      .columnsGap(10)
      .rowsGap(20)
      .width('90%')
      .backgroundColor(0xF0F0F0)
      .height(100)
    }.width('100%')
  }
}

Three columns are defined, each with a width ratio of 4:2:3.

Here’s what it looks like:

Arrangement

The layoutDirection allows you to set the direction of the main axis of the grid layout and determine how the subcomponents are arranged.

The following values are available: Row, RowReverse, Column, and ColumnReverse.

Here’s what it looks like:

Row and column spacing

columnsGap is used to set the vertical spacing of the GridItem of the grid subcomponent, and rowsGap is used to set the horizontal spacing of the GridItem.


Grid()
.columnsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(20)

In the image above, the vertical spacing between the grid layout subcomponents is set to 20 and the horizontal spacing is 10.

1.4.2 -> Grid subcomponent GridItem settings

Sets the number of rows and columns occupied by the subcomponent

The row and column designators of the grid layout are numbered sequentially starting at 1.

If a child component spans multiple rows, use rowStart to set the start row number and rowEnd to the end row number. When the rowStart value is the same as the rowEnd value, the child component occupies only one mesh. The following is an example:

Grid() {
    GridItem() {
      Text('5')
        .fontSize(16)
        .textAlign(TextAlign.Center)
        .textStyle()
    }.rowStart(2).rowEnd(3)  // 5子组件从第二行到第三行

    GridItem() {
      Text('4')
        .fontSize(16)
        .textAlign(TextAlign.Center)
        .textStyle()
    }.columnStart(4).columnEnd(5) // 4从第四列到第五列

    GridItem() {
      Text('6')
        .fontSize(16)
        .textAlign(TextAlign.Center)
        .textStyle()
    }.columnStart(2).columnEnd(4)  // 6从第二列到第四列

    GridItem() {
      Text('9')
        .fontSize(16)
        .textAlign(TextAlign.Center)
        .textStyle()
    }.columnStart(3).columnEnd(4)    // 从第三列到第四列
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(20)
.width('90%')
.backgroundColor(0xF0F0F0)
.height('200vp')
.layoutDirection(GridDirection.Column)

1.4.3 -> Scenario example

Use the grid layout to implement the layout of a calculator with the following code:


@Entry
@Component
struct GridExample {
  @State Number: Array<string> = ['1', '2', '3', '+', '4', '5', '6', '-', '7', '8', '9', '*', '0', '.', '/']

  @Styles textStyle(){
    .backgroundColor(0xd0d0d0)
    .width('100%')
    .height('100%')
    .borderRadius(5)
  }

  build() {
    Column({ space: 5 }) {
      Grid() {
        GridItem() {
          Text('0')
            .fontSize(30)
            .textStyle()
        }.columnStart(1).columnEnd(4)

        GridItem() {
          Text('清空')
            .fontSize(16)
            .textAlign(TextAlign.Center)
            .textStyle()
        }.columnStart(1).columnEnd(2)

        GridItem() {
          Text('回退')
            .fontSize(16)
            .textAlign(TextAlign.Center)
            .textStyle()
        }.columnStart(3).columnEnd(4)

        ForEach(this.Number, (day: string) => {
          if (day === '0') {
            GridItem() {
              Text(day)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .textStyle()
            }.columnStart(1).columnEnd(2)
          } else {
            GridItem() {
              Text(day)
                .fontSize(16)
                .textAlign(TextAlign.Center)
                .textStyle()
            }
          }
        })
      }
      .columnsTemplate('1fr 1fr 1fr 1fr')
      .rowsTemplate('2fr 1fr 1fr 1fr 1fr 1fr')
      .columnsGap(10)
      .rowsGap(15)
      .width('90%')
      .backgroundColor(0xF0F0F0)
      .height('70%')
    }.width('100%').margin({ top: 5 })
  }
}

Here’s what it looks like on a large screen:

Here’s what it looks like on a small screen device:

2 -> Responsive layout

2.1 -> Grid layout

As a positioning tool to assist layout, the grid system has played a good role in graphic design and website design, and has a good reference role in the interface design of mobile devices. To summarize, the main advantages of raster system for mobile devices are:

It provides a law to follow for the layout and solves the dynamic layout problem of multiple sizes and devices.
Provide a unified positioning annotation for the system to ensure the consistency of the layout of each module and each device.
It provides a flexible spacing adjustment method for applications to meet the possibility of layout adjustment in special scenarios.
To achieve the grid layout effect, the declarative paradigm provides the GridContainer grid container component, which is implemented with the common property useSizeType of its subcomponents.

2.1.1 -> Grid system

The raster system has three concepts: Column, Margin, and Gutter.

Gutter: The distance between elements, which determines how close the content is. Serves as a uniform specification for grid layouts. In order to ensure a good visual effect, the value of gutter is usually not greater than the value of margin.

Margin: The distance between the content and the edge of the raster container, which determines the total width of the content that can be displayed. Serves as a uniform specification for grid layouts.

Column: The primary positioning tool for the grid layout. According to the different sizes of the device, the raster container is divided into different columns, and the width of each column is calculated according to the total number of columns under the condition that the margin and gutter meet the specifications.

System grid breakpoints

The raster system uses the horizontal width of the device (screen density pixel value, VP) as the breakpoint basis, defines the width type of the device, sets the total number of grid columns, spacing, and margins, and forms a set of breakpoint rules.


This content originally appeared on DEV Community and was authored by ZHZL-m