HarmonyOS Next鸿蒙开发:arkUI布局组件之RelativeContainer,结合案例使用扇形菜单

111 阅读4分钟

相对布局 (RelativeContainer)

1、概述

4.0默认是现形布局,而5.0默认新建page,就是相对布局,所以这也是更新的一个特点。相对布局做一些复杂页面时候更加容易,而且布局不建议包裹太多组件,从而影响性能。

在应⽤的开发过程中,经常需要设计复杂界⾯,此时涉及到多个相同或不同组件之间的嵌套。如果布局组件嵌套深 度过深,或者嵌套组件数过多,会带来额外的开销。如果在布局的⽅式上进⾏优化,就可以有效的提升性能,减少 时间开销。 RelativeContainer就是采⽤相对布局的容器,⽀持容器内部的⼦元素设置相对位置关系,适⽤于界⾯复杂场景的情 况,对多个⼦组件进⾏对⻬和排列。⼦元素⽀持指定兄弟元素作为锚点,也⽀持指定⽗容器作为锚点,基于锚点做 相对位置布局。下图是⼀个RelativeContainer的概念图,图中的虚线表示位置的依赖关系。

image.png ⼦元素并不完全是上图中的依赖关系。⽐如,Item4可以以Item2为依赖锚点,也可以以RelativeContainer⽗容器 为依赖锚点。

2、基本概念

锚点:通过锚点设置当前元素基于哪个元素确定位置。

对⻬⽅式:通过对⻬⽅式,设置当前元素是基于锚点的上中下对⻬,还是基于锚点的左中右对⻬。

3、规则说明

容器内⼦组件区分⽔平⽅向,垂直⽅向: ⽔平⽅向为left, middle, right,对应容器的HorizontalAlign.Start, HorizontalAlign.Center,HorizontalAlign.End 垂直⽅向为top, center, bottom,对应容器的VerticalAlign.Top, VerticalAlign.Center,VerticalAlign.Bottom ⼦组件可以将容器或者其他⼦组件设为锚点: 参与相对布局的容器内组件必须设置id,不设置id的组件不显示,RelativeContainer容器的固定id 为 container 。 此⼦组件某⼀⽅向上的三个位置可以将容器或其他⼦组件的同⽅向三个位置为锚点,同⽅向上两个以上位置设置锚点以后会跳过第三个。 前端⻚⾯设置的⼦组件尺⼨⼤⼩不会受到相对布局规则的影响。 ⼦组件某个⽅向上设置两个或以上alignRules时不建议设置此⽅向尺⼨⼤⼩。

4、特殊情况

互相依赖,环形依赖时容器内⼦组件全部不绘制。 同⽅向上两个以上位置设置锚点但锚点位置逆序时此⼦组件⼤⼩为0,即不绘制。容器不设置宽⾼时,容器与容器内⼦组件不绘制。

5、基本使用RelativeContainer

案例1:顶部菜单

做一个顶部菜单案例,通常手机app都有左边,右边中间组件区域。

image.png

完整代码:

@Entry
@Component
struct RelativeLayoutPage3 {
  @State message: string = 'Hello World';

  build() {
    RelativeContainer(){
      RelativeContainer() {

        Row(){Text('左边')}.justifyContent(FlexAlign.Center).width(100).height("100%")
        .backgroundColor("#FF3333")
        .alignRules({
          'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
          'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start }
        })
        .id("row1")


        // Text('标题').fontColor(Color.White).textAlign(TextAlign.Center)
        //   .alignRules({
        //   left:{anchor:"row1",align:HorizontalAlign.End},
        //   right:{anchor:"row2",align:HorizontalAlign.Start},
        //   bottom:{anchor:"row1",align:VerticalAlign.Center}
        //   })
        //   .offset({
        //     x:0,
        //     y:10
        //   })
        //   .width(100)

        Row(){Text('右边')}.justifyContent(FlexAlign.Center).width(100).height("100%")
        .backgroundColor("#FFCC00")
        .alignRules({
          'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
          'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
        })
        .id("row2")

      }.backgroundColor(Color.Gray).width('100%').height('10%')
    }

    .height('100%')
    .width('100%')
  }
}

案例2:扇形菜单

只有相对布局其实也不行,我们有时候遇到一些很复杂的ui设计,那么就需要结合多组件进行布局,今天结合相对布局和list组合做个扇形展开菜单。使用List组件装载数据,通过ForEach进行遍历每一个ListItem,最外层使用层叠Stack组件包裹起来,扇形部分就用相对RelativeContainer组件。

List是很常用的滚动类容器组件,一般和子组件ListItem一起使用,List列表中的每一个列表项对应一个ListItem组件,与ListItem是配套使用。

效果:

image.png

完整代码:

@Entry
@Component
struct ListPageMoreBUtton {
  @State flag: boolean = false;
  // 水平方向为left, middle, right,对应容器的HorizontalAlign.Start, HorizontalAlign.Center, HorizontalAlign.End。
  // 垂直方向为top, center, bottom,对应容器的VerticalAlign.Top, VerticalAlign.Center, VerticalAlign.Bottom。

  build() {
    Column() {
      Stack({ alignContent: Alignment.BottomEnd }) {
        List({ space: 10 }) {
          ForEach([1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], (item: number) => {
            ListItem() {
              Text(`${item}`)
                .fontSize(34)
                .width('100%')
                .height(60)
                .borderRadius(10)
                .textAlign(TextAlign.Center)
                .backgroundColor('#ddd')
            }
          })
        }
        .padding(10)
        .height('100%')

        RelativeContainer() {
          Button() {
            Text("+").fontColor(Color.White).fontSize(36)
          }
          .width(60)
          .height(60)
          .alignRules({
            bottom: {
              anchor: "__container__",
              align: VerticalAlign.Bottom
            },
            // left: {
            //   anchor: "__container__",
            //   align: HorizontalAlign.Start
            // }

            right: {
              anchor: "__container__",
              align: HorizontalAlign.End
           }
          })
          .id("bt1")
          .onClick(() => {
            this.flag = !this.flag;
          })

          if (this.flag) {
            Button() {
              Text("A").fontColor(Color.White).fontSize(36)
                .textAlign(TextAlign.Center)
            }
            .width(60)
            .height(60)
            .alignRules({
              bottom: {
                anchor: "bt1",
                align: VerticalAlign.Top
              },
              right: {
                anchor: "bt1",
                align: HorizontalAlign.End
              }
            })
            .backgroundColor(Color.Orange)
            .offset({ y: -30 })
            .id("bt2")
            
            Button() {
              Text("B").fontColor(Color.White).fontSize(36)
                .textAlign(TextAlign.Center)
            }
            .width(60)
            .height(60)
            .alignRules({
              bottom: {
                anchor: "bt1",
                align: VerticalAlign.Top
              },
              right: {
                anchor: "bt1",
                align: HorizontalAlign.Start
              }
            })
            .backgroundColor(Color.Gray)
            .offset({ y: -10, x: -10 })
            .id("bt3")

            Button() {
              Text("C").fontColor(Color.White).fontSize(36)
                .textAlign(TextAlign.Center)
            }
            .width(60)
            .height(60)
            .alignRules({
              bottom: {
                anchor: "bt1",
                align: VerticalAlign.Bottom
              },
              right: {
                anchor: "bt1",
                align: HorizontalAlign.Start
              }
            })
            .backgroundColor(Color.Red)
            .offset({ x: -30 })
            .id("bt4")
          }

        }.width('100%')
        .height(160)
        .offset({ x: -10, y: -10 })
      }
    }
    .width('100%')
    .height('100%')
  }
}