四. 布局高级(鸿蒙NEXT版)

515 阅读8分钟

今日核心:

1.弹性布局 Flex

2.定位

3.层叠布局

(注:找不到图片啦,所以只能图片自己放就好,命名遵循变量命名规则)

1. 弹性布局(Flex)

  弹性布局(Flex)提供更加有效的方式对容器中的子元素进行排列、对齐和分配剩余空间。常用于页面头部导航栏的均匀分布、页面框架的搭建、多行数据的排列等。

容器默认存在主轴与交叉轴,子元素默认沿主轴排列,子元素在主轴方向的尺寸称为主轴尺寸,在交叉轴方向的尺寸称为交叉轴尺寸。

image.png

1.1. 基本使用

弹性容器组件: Flex() 基本使用

Flex(参数对象) { 
    子组件1 
    子组件2 
    子组件3 
    子组件N 
 }

示例代码

@Entry 
@Component 
struct Index {
  build() {
    Flex() {
      Text('1') 
        .width('33%') 
        .height(30) 
        .backgroundColor(Color.Orange) 
      Text('2') 
        .width('33%') 
        .height(30) 
        .backgroundColor(Color.Pink) 
      Text('3')
        .width('33%')
        .height(30) 
        .backgroundColor(Color.Orange)
      }
    .width('100%')
    .height(200) 
    .backgroundColor('#ccc') 
  } 
}

1.2. 布局方向

在弹性布局中,容器的子元素可以按照任意方向排列(默认水平排列)。通过设置参数 direction,可以决定主轴的方向,从而控制子元素的排列方向

参数:direction

值:枚举 FlexDirection

image.png 示例代码

@Entry 
@Component
struct Index { 
  build() {
    // 垂直排列 
    Flex({direction: FlexDirection.Column }) { 
      Text('1') 
        .width('33%') 
        .height(30) 
        .backgroundColor(Color.Orange) 
      Text('2')
        .width('33%')
        .height(30)
        .backgroundColor(Color.Pink) 
      Text('3')
        .width('33%')
        .height(30) 
        .backgroundColor(Color.Orange)
      } 
    .width('100%')
    .height(200) 
    .backgroundColor('#ccc')
  }
}

1.3. 主轴对齐方式

参数:justifyContent

值:枚举 FlexAlign(属性与线性布局主轴对齐方式相同)

image.png

示例代码

@Entry 
@Component
struct Index { 
  build() {
    Flex({ justifyContent: FlexAlign.SpaceBetween }) { 
      Text('1') 
        .width(30) 
        .height(30) 
        .backgroundColor(Color.Orange)
      Text('2')
        .width(30)
        .height(30)
        .backgroundColor(Color.Pink)
      Text('3')
        .width(30) 
        .height(30)
        .backgroundColor(Color.Orange)
      Text('4') 
        .width(30)
        .height(30) 
        .backgroundColor(Color.Pink) 
      } 
      .width('100%') 
      .height(100) 
      .backgroundColor('#ccc') 
    } 
  }

1.4. 交叉轴对齐方式

参数:alignItems

值:枚举 ItemAlign

image.png 示例代码

@Entry
@Component struct Index {
  build() { 
    Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween }) {
      Text('1') 
        .width(30)
        .height(30) 
        .backgroundColor(Color.Orange) 
      Text('2') 
        .width(30)
        .height(30) 
        .backgroundColor(Color.Pink)
        
        // 交叉轴拉伸 
        .alignSelf(ItemAlign.Stretch) 
      Text('3')
        .width(30)
        .height(30) 
        .backgroundColor(Color.Orange) 
      Text('4')
        .width(30)
        .height(30)
        .backgroundColor(Color.Pink) 
      } 
      .width('100%')
      .height(200) 
      .backgroundColor('#ccc') 
    }
  }

1.5. 布局换行

弹性布局分为单行布局和多行布局。默认情况下,Flex 容器中的子元素都排在一条线(又称“轴线”)上。子元素尺寸总和大于 Flex 容器尺寸时,子元素尺寸会自动挤压。

wrap 属性控制当子元素主轴尺寸之和大于容器主轴尺寸时,Flex 是单行布局还是多行布局。在多行布局时,通过交叉轴方向,确认新行排列方向。

参数:wrap

值:枚举 FlexWrap

示例代码

@Entry 
@Component 
struct Index { 
  build() { 
    Flex({ wrap:FlexWrap.Wrap }) { 
      Text('1')
        .width('33%')
        .height(30)
        .backgroundColor(Color.Orange) 
      Text('2')
        .width('33%') 
        .height(30) 
        .backgroundColor(Color.Pink)
      Text('3')
        .width('33%') 
        .height(30) 
        .backgroundColor(Color.Orange) 
      Text('4')
        .width('33%') 
        .height(30) 
        .backgroundColor(Color.Pink) 
      } 
      .width('100%')
      .height(200) 
      .backgroundColor('#ccc')
    } 
  }

1.6. 综合案例-菜单

  image.png

1.6.1. 参考代码
@Entry 
@Component 
struct Index {
  build() { 
    Column() {
      Text('阶段选择') 
        .fontWeight(600) 
        .fontSize(24)
        .width('100%') 
        .padding(15) 
      Flex({ wrap: FlexWrap.Wrap }) { 
        Text('ArkUI') 
        .padding(10)
        .margin(5) 
        .backgroundColor('#f6f7f9') 
      Text('ArkTS') 
        .padding(10) 
        .margin(5)
        .backgroundColor('#f6f7f9') 
      Text('界面开发')
        .padding(10) 
        .margin(5)
        .backgroundColor('#f6f7f9')
      Text('系统能力')
        .padding(10)
        .margin(5)
        .backgroundColor('#f6f7f9')
      Text('权限控制') 
        .padding(10) 
        .margin(5) 
        .backgroundColor('#f6f7f9')
      Text('元服务') 
        .padding(10) 
        .margin(5) 
        .backgroundColor('#f6f7f9') 
      } 
    }
    .width('100%')
    .height('100%') 
    .padding(10) 
  }
}

1.7. 自适应拉伸

1.7.1. flexGrow

作用:设置父容器的剩余空间分配给此属性所在组件的比例。用于分配父组件的剩余空间

属性:flexGrow(数字)

参数:数字,占用父级剩余尺寸的份数

示例代码

@Entry 
@Component 
struct Index {
  build() { 
    Flex() { 
      Text('内容1')
        .width(60) 
        .height(100) 
        .backgroundColor(Color.Pink)
      Text('内容2') 
        .height(100)
        .backgroundColor(Color.Orange)
        
        // 占用父组件剩余空间1份 
        .flexGrow(1) 
      Text('内容3') 
        .height(100)
        .backgroundColor(Color.Pink) 
        
        // 占用父组件剩余空间1份 
        .flexGrow(1) 
      } 
      .width('100%')
      .height(100) 
      .backgroundColor('#ccc') 
    }
  }
1.7.2. flexBasis

作用:设置子元素在父容器主轴方向上的基准尺寸。

属性:flexBasis(数字)

参数:数字,基准尺寸(单位vp)

@Entry
@Component
struct Index { 
  build() { 
    Flex() {
      Text('内容1') 
      .width(60)
      .height(100) 
      .backgroundColor(Color.Pink) 
      .flexBasis(60) 
    Text('内容2') 
      .height(100)
      .backgroundColor(Color.Orange)
      .flexGrow(1) 
      .flexBasis(120) 
    Text('内容3')
      .height(100) 
      .backgroundColor(Color.Pink)
      .flexGrow(1) 
    } 
    .width('100%') 
    .height(100) 
    .backgroundColor('#ccc') 
  } 
}
  1. 如果设置了该值,则子项占用的空间为设置的值;如果没设置该属性,那子项的空间为 width/height 的值

  2. flexGrow 基于 flexBasis 分配父组件剩余空间

2. 定位

作用:改变组件位置

分类:

● 绝对定位:position,相对父组件左上角进行偏移

● 相对定位:offset,相对自身左上角进行偏移

2.1. 绝对定位

属性:position()

参数:{x: 水平偏移量, y: 垂直偏移量}

偏移量取值

● 数字,单位是vp

● 百分比,参照父组件尺寸计算结果

示例代码

@Entry 
@Component 
struct Index {
  build() { 
    Column() {
      Text('文字内容') 
        .width(80) 
        .height(40) 
        .backgroundColor(Color.Pink) 
        .position({ x: 0, y: 0 }) 
      }
      .width('100%') 
      .height(200) 
      .backgroundColor('#ccc') 
    } 
  }

绝对定位特点:

  1. 参照父组件左上角进行偏移

  2. 绝对定位后的组件不再占用自身原有位置

2.2. 相对定位

属性:offset()

参数:{x: 水平偏移量, y: 垂直偏移量}

偏移量取值

●. 数字,单位是vp

●. 百分比,参照父组件尺寸计算结果

@Entry
@Component
struct Index { 
  build() {
    Column() {
      Text('内容1') 
        .width(80)
        .height(40) 
        .backgroundColor(Color.Pink)
      Text('内容2')
        .width(80) 
        .height(40) 
        .backgroundColor(Color.Orange) 
        
        // 占位 
        .offset({ x: 100, y: -30 }) 
      Text('内容3')
        .width(80) 
        .height(40)
        .backgroundColor(Color.Brown) 
      } 
      .width('100%') 
      .height(200)
      .backgroundColor('#ccc') 
    }
  }

相对定位特点:

  1. 相对自身左上角进行偏移

  2. 相对定位后的组件仍然占用自身原有位置

2.3. 案例-人气热播故事

image.png

2.3.1. 参考代码

@Entry 
@Component 
struct Index {
  build() { 
    Column() { 
      Column() { 
        // 图片 
        Image($r('app.media.moco')) 
          .width('100%') 
          .aspectRatio(0.8) 
          .borderRadius(8) 
        // 文字
        Row({ space: 10}) {
          Image($r('app.media.ic_device_earphone_roc_filled')) 
            .width(20)
            .backgroundColor('#0cbff9') 
            .padding(3) 
            .borderRadius(10) 
            .fillColor('#fff') 
          Text('飞狗MOCO') 
            .fontSize(16) 
            .fontColor('#4b3635')
            .fontWeight(600) 
          } 
          .width('100%') 
          .padding(5)
          .alignItems(VerticalAlign.Top) 
          
          // VIP Text('VIP') 
          .width(34)
          .height(18) 
          .backgroundColor('#f3912e')
          .border({ width: 2, color: '#ffe7a0' })
          .borderRadius({ topLeft: 8, bottomRight: 8 })
          .position({x: 0, y:0})
          .fontSize(12) 
          .fontColor('#ffe788') 
          .textAlign(TextAlign.Center) 
          .fontWeight(700) 
          .fontStyle(FontStyle.Italic)
        }
        .width('45%') 
      } 
      .padding(10)
    }
  }

2.4. Z 序控制

定位后的组件,默认后定义的组件在最上面显示,可以通过 zIndex 属性调整显示层级

属性:zIndex(数字)

特点:取值为整数数字,取值越大,显示层级越高

@Entry
@Component 
struct Index { 
  build() {
    Column() {
      Text('内容1')
        .width(100) 
        .height(50) 
        .backgroundColor(Color.Pink)
        .position({x: 0, y:0}) 
        
        // Z 轴显示顺序,取值越大,显示层级越高
        .zIndex(1)
      Text('内容2')
        .width(200)
        .height(60) 
        .backgroundColor(Color.Orange)
        .position({x: 50,y: 30})
      } 
      .width(300)
      .height(200)
      .backgroundColor('#ccc')
    } 
  }

3. 层叠布局

层叠布局(StackLayout)用于在屏幕上预留一块区域来显示组件中的元素,提供元素可以重叠的布局。层叠布局通过 Stack 容器组件实现位置的固定定位与层叠,容器中的子元素依次入栈,后一个子元素覆盖前一个子元素,子元素可以叠加,也可以设置位置。

层叠布局具有较强的页面层叠、位置定位能力,其使用场景有广告、卡片层叠效果等。

3.1. 基本使用

Stack 组件为容器组件,容器内可包含各种子元素。其中子元素默认进行居中堆叠。子元素被约束在Stack下,进行自己的样式定义以及排列。

示例代码

@Entry 
@Component 
struct Index { 
  build() {
    Column(){ 
      Stack() {
        Column(){
        
          } 
          .width('90%')
          .height(130) 
          .backgroundColor(Color.Gray) 
        Text('text') 
          .width('60%')
          .height('60%') 
          .backgroundColor(Color.Orange) 
        Button('button')
          .width('30%') 
          .height('30%') 
          .backgroundColor('#ff8ff3eb')
          .fontColor('#000') } 
          .width('100%')
          .height(150)
          .backgroundColor(Color.Pink) 
        } 
        .margin(10) 
      }
    }

 

3.2. 对齐方式

参数:alignContent

取值:枚举 Alignment

image.png

示例代码

@Entry 
@Component
struct Index { 
  build() {
    Column(){
      Stack({ alignContent: Alignment.BottomEnd }) {
        Column(){} 
          .width('90%') 
          .height(130) 
          .backgroundColor(Color.Gray)
        Text('text') 
          .width('60%')
          .height('60%') 
          .backgroundColor(Color.Orange) 
        Button('button') 
          .width('30%')
          .height('30%')
          .backgroundColor('#ff8ff3eb')
          .fontColor('#000') 
        } 
        .width('100%') 
        .height(150)
        .backgroundColor(Color.Pink) 
      }
      .margin(10) 
    } 
  }

3.3. Z 序控制

属性:zIndex(数字)

特点:取值为整数数字,取值越大,显示层级越高

示例代码

@Entry
@Component
struct Index {
  build() { 
    Column(){ 
      Stack({ alignContent: Alignment.BottomEnd }) {
        Column(){} 
          .width('90%') 
          .height(130)
          .backgroundColor(Color.Gray)
        Text('text') 
          .width('60%')
          .height('60%')
          .backgroundColor('rgba(0,0,0,0.3)')
          
          // Z 序:显示在按钮上一层
          .zIndex(1) Button('button')
          .width('30%') 
          .height('30%') 
          .backgroundColor('#ff8ff3eb')
          .fontColor('#000') 
        } 
        .width('100%')
        .height(150)
        .backgroundColor(Color.Pink)
      }
      .margin(10) 
    }
  }

4. 综合案例-B站-视频卡片

  image.png

4.1. 布局思路

区域划分:

●. 图片区域(层叠布局)

○. 图片组件

○. 文字内容:播放 + 评论 + 时长(Flex 布局)

●. 文字区域

○. 标题文字

4.2. 参考代码

@Entry
@Component
struct Index {
  build() {
    Column(){
      Column() {

        // 图片区域 -- 层叠布局
        Stack({ alignContent: Alignment.BottomStart}) {
        
          // 图片组件
          Image($r('app.media.tieyi'))
            .width(200)
            .height(125)
            .objectFit(ImageFit.Cover)
            .borderRadius({
              topLeft: 10,
              topRight: 10
            })

          // 文字内容
          Row() {

            // 播放量
            Row({ space: 5}) {
              Image($r('app.media.ic_bofangshu'))
                .width(16)
                .fillColor('#fff')
              Text('282万')
                .fontColor('#fff')
                .fontSize(12)
            }
            .margin({right: 5})

            // 评论数
            Row({ space: 5}) {
              Image($r('app.media.ic_ziyuan'))
                .fillColor('#fff')
                .width(14)
              Text('8655')
                .fontColor('#fff')
                .fontSize(12)
            }
            
            // 空白填充组件
            Blank()
            // 右侧时长
            Text('4:33')
              .fontColor('#fff')
              .fontSize(12)
          }
          .width('100%')
          .height(24)
          .padding({left: 5, right: 5})
        }

        // 文字区域
        Column() {
          // 标题文字
          Text('【凤凰传奇新歌】欢迎来到国风统治区:唢呐一响神曲《铁衣流派推广曲》')
            .fontSize(13)
            .textOverflow({overflow: TextOverflow.Ellipsis})
            .maxLines(2
            .lineHeight(16)
          // 点赞更多
          Row(){
            Text('19万点赞')
              .padding(3)
              .backgroundColor('#fef0ef')
              .fontSize(10)
              .fontColor('#e66c43')
            Image($r('app.media.ic_gengduo'))
              .width(14)
              .fillColor('#bfbfbf')
          }
          .width('100%')
          .margin({top: 10})
          .justifyContent(FlexAlign.SpaceBetween)
        }
        .padding(5)
      }
      .width(200)
      .height(200)
      .backgroundColor('#fff')
      .borderRadius(10)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f6f7')
    .padding(10)
  }
}
```3