当前位置: 首页 > article >正文

【HarmonyOS之旅】基于ArkTS开发(二) -> UI开发之常见布局

目录

1 -> 自适应布局

1.1 -> 线性布局

1.1.1 -> 线性布局的排列

1.1.2 -> 自适应拉伸

1.1.3 -> 自适应缩放

1.1.4 -> 定位能力

1.1.5 -> 自适应延伸

1.2 -> 层叠布局

1.2.1 -> 对齐方式

1.2.2 -> Z序控制

1.3 -> 弹性布局

1.3.1 -> 弹性布局方向

1.3.2 -> 弹性布局换行

1.3.3 -> 弹性布局对齐方式

1.3.4 -> 弹性布局的自适应拉伸

1.3.5 -> 场景示例

1.4 -> 网格布局

1.4.1 -> 容器组件Grid设置

1.4.2 -> 网格子组件GridItem设置

1.4.3 -> 场景示例

2 -> 响应式布局

2.1 -> 栅格布局

2.1.1 -> 栅格系统

2.1.2 -> GridContainer栅格组件使用

2.1.3 -> 场景示例

2.2 -> 媒体查询

2.2.1 -> 媒体查询引入与使用流程

2.2.2 -> 媒体查询条件

2.2.3 -> 场景示例


1 -> 自适应布局

1.1 -> 线性布局

线性布局(LinearLayout)是开发中最常用的布局。线性布局的子组件在线性方向上(水平方向和垂直方向)依次排列。

通过线性容器Row和Column实现线性布局。Column容器内子组件按照垂直方向排列,Row组件中,子组件按照水平方向排列。

1.1.1 -> 线性布局的排列

线性布局的排列方向由所选容器组件决定。根据不同的排列方向,选择使用Row或Column容器创建线性布局,通过调整space,alignItems,justifyContent属性调整子组件的间距,水平垂直方向的对齐方式。

  1. 通过space参数设置主轴(排列方向)上子组件的间距。达到各子组件在排列方向上的等间距效果。
  2. 通过alignItems属性设置子组件在交叉轴(排列方向的垂直方向)的对齐方式。且在各类尺寸屏幕中,表现一致。其中,交叉轴为垂直方向时,取值为VerticalAlign类型,水平方向取值为HorizontalAlign类型。
  3. 通过justifyContent属性设置子组件在主轴(排列方向)上的对齐方式。实现布局的自适应均分能力。取值为FlexAlign类型。

具体使用以及效果如下表所示:

属性名描述Row效果图Column效果图
space

- 横向布局中各子组件的在水平方向的间距

- 纵向布局中个子组件垂直方向间距

alignItems容器排列方向的垂直方向上,子组件在父容器中的对齐方式
justifyContent容器排列方向上,子组件在父容器中的对齐方式

1.1.2 -> 自适应拉伸

在线性布局下,常用空白填充组件Blank,在容器主轴方向自动填充空白空间,达到自适应拉伸效果。

@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 -> 自适应缩放

自适应缩放是指在各种不同大小设备中,子组件按照预设的比例,尺寸随容器尺寸的变化而变化。在线性布局中有下列方法实现:

1. 父容器尺寸确定时,设置了layoutWeight属性的子组件与兄弟元素占主轴尺寸按照权重进行分配,忽略元素本身尺寸设置,在任意尺寸设备下,自适应占满剩余空间。

@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%')
    }
  }
}

2. 父容器尺寸确定时,使用百分比设置子组件以及兄弟组件的width宽度,可以保证各自元素在任意尺寸下的自适应占比。

@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%')
    }
  }
}

上例中,在任意大小的设备中,子组件的宽度占比固定。

1.1.4 -> 定位能力

  • 相对定位

        使用组件的offset属性可以实现相对定位,设置元素相对于自身的偏移量。设置该属性,不影响父容器布局,仅在绘制时进行位置调整。使用线性布局和offset可以实现大部分布局的开发。

@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 })
  }
} 
  • 绝对定位

        线性布局中可以使用组件的positon属性实现绝对布局(AbsoluteLayout),设置元素左上角相对于父容器左上角偏移位置。对于不同尺寸的设备,使用绝对定位的适应性会比较差,在屏幕的适配上有缺陷。

@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 -> 自适应延伸

自适应延伸是在不同尺寸设备下,当页面显示内容个数不一并延伸到屏幕外时,可通过滚动条拖动展示。适用于线性布局中内容无法一屏展示的场景。常见以下两类实现方法。

  • List组件

        List子项过多一屏放不下时,未展示的子项通过滚动条拖动显示。通过scrollBar属性设置滚动条的常驻状态,edgeEffect属性设置拖动到极限的回弹效果。

  • 纵向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)
    }
  }
  • 横向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组件

线性布局中,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。在Column或者Row外层包裹一个可滚动的容器组件Scroll实现。

  • 纵向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) // 滚动到边沿后回弹
  }
}
  • 横向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 -> 层叠布局

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

1.2.1 -> 对齐方式

设置子元素在容器内的对齐方式。支持左上,上中,右上,左,中,右,右下,中下,右下九种对齐方式,如下表所示:

名称描述图示
TopStart顶部起始端
Top顶部横向居中
TopEnd顶部尾端
Start起始端纵向居中
Center横向和纵向居中
End尾端纵向居中
BottomStart底部起始端
Bottom底部横向居中
BottomEnd底部尾端

1.2.2 -> Z序控制

Stack容器中兄弟组件显示层级关系可以通过zIndex属性改变。zIndex值越大,显示层级越高,即zIndex值大的组件会覆盖在zIndex值小的组件上方。

  • 在层叠布局中,如果后面子元素尺寸大于前面子元素尺寸,则前面子元素完全隐藏。

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)

上图中,最后的子元素3的尺寸大于前面的所有子元素,所以,前面两个元素完全隐藏。改变子元素1,子元素2的zIndex属性后,可以将元素展示出来。

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 -> 弹性布局

弹性布局(Flex布局)是自适应布局中使用最为灵活的布局。弹性布局提供一种更加有效的方式来对容器中的子组件进行排列、对齐和分配空白空间。弹性布局

  • 容器:​​​Flex组件作为Flex布局的容器,用于设置布局相关属性。

  • 子组件:Flex组件内的子组件自动成为布局的子组件。

  • 主轴:Flex组件布局方向的轴线,子组件默认沿着主轴排列。主轴开始的位置称为主轴起始端,结束位置称为主轴终点端。

  • 交叉轴:垂直于主轴方向的轴线。交叉轴起始的位置称为交叉轴首部,结束位置称为交叉轴尾部。

容器组件属性

通过Flex组件提供的Flex接口创建弹性布局。如下:

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

1.3.1 -> 弹性布局方向

参数direction决定主轴的方向,即子组件的排列方向。可选值有:

  • FlexDirection.Row(默认值):主轴为水平方向,子组件从起始端沿着水平方向开始排布。
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:主轴为水平方向,子组件从终点端沿着FlexDirection. Row相反的方向开始排布。
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:主轴为垂直方向,子组件从起始端沿着垂直方向开始排布。
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:主轴为垂直方向,子组件从终点端沿着FlexDirection. Column相反的方向开始排布。
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 -> 弹性布局换行

默认情况下,子组件在Flex容器中都排在一条线(又称"轴线")上。通过wrap参数设置子组件换行方式。可选值有:

  • FlexWrap. NoWrap(默认值):不换行。如果子组件的宽度总和大于父元素的宽度,则子组件会被压缩宽度。

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:换行,每一行子组件按照主轴方向排列。
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:换行,每一行子组件按照主轴反方向排列。
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 -> 弹性布局对齐方式

主轴对齐

通过justifyContent参数设置在主轴方向的对齐方式,存在下面六种情况:

  • FlexAlign.Start(默认值):子组件在主轴方向起始端对齐, 第一个子组件与父元素边沿对齐,其他元素与前一个元素对齐。
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:子组件在主轴方向居中对齐。
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:子组件在主轴方向终点端对齐, 最后一个子组件与父元素边沿对齐,其他元素与后一个元素对齐。
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:Flex主轴方向均匀分配弹性元素,相邻子组件之间距离相同。第一个子组件和最后一个子组件与父元素边沿对齐。
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:Flex主轴方向均匀分配弹性元素,相邻子组件之间距离相同。第一个子组件到主轴起始端的距离和最后一个子组件到主轴终点端的距离是相邻元素之间距离的一半。
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主轴方向元素等间距布局,相邻子组件之间的间距、第一个子组件与主轴起始端的间距、最后一个子组件到主轴终点端的间距均相等。
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)

交叉轴对齐

容器和子组件都可以设置交叉轴对齐方式,且子组件设置的对齐方式优先级较高。

容器组件设置交叉轴对齐

可以通过Flex组件的alignItems参数设置子组件在交叉轴的对齐方式,可选值有:

  • ItemAlign.Auto:使用Flex容器中默认配置。

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:交叉轴方向首部对齐。
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:交叉轴方向居中对齐。
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:交叉轴方向底部对齐。
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:交叉轴方向拉伸填充,在未设置尺寸时,拉伸到容器尺寸。
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:交叉轴方向文本基线对齐。
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)

子组件设置交叉轴对齐

子组件的alignSelf属性也可以设置子组件在父容器交叉轴的对齐格式,且会覆盖Flex布局容器中alignItems默认配置。如下例所示:

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)

上例中,Flex容器中alignItems设置交叉轴子组件的对齐方式为居中,子组件自身设置了alignSelf属性的情况,覆盖父组件的alignItem值,表现为alignSelf的定义。

内容对齐

可以通过alignContent参数设置子组件各行在交叉轴剩余空间内的对齐方式,只在多行的flex布局中生效,可选值有:

  • FlexAlign.Start: 子组件各行与交叉轴起点对齐。

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: 子组件各行在交叉轴方向居中对齐。
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: 子组件各行与交叉轴终点对齐。
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: 子组件各行与交叉轴两端对齐,各行间垂直间距平均分布。
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: 子组件各行间距相等,是元素首尾行与交叉轴两端距离的两倍。
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: 子组件各行间距,子组件首尾行与交叉轴两端距离都相等。
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.3.4 -> 弹性布局的自适应拉伸

在弹性布局父组件尺寸不够大的时候,通过子组件的下面几个属性设置其再父容器的占比,达到自适应布局能力。

  • flexBasis:设置子组件在父容器主轴方向上的基准尺寸。如果设置了该值,则子项占用的空间为设置的值;如果没设置或者为auto,那子项的空间为width/height的值。

Flex() {
  Text('flexBasis("auto")')
    .flexBasis('auto') // 未设置width以及flexBasis值为auto,内容自身宽松
    .height(100)
    .backgroundColor(0xF5DEB3)
  Text('flexBasis("auto")'+' width("40%")')
    .width('40%')
    .flexBasis('auto') //设置width以及flexBasis值auto,使用width的值
    .height(100)
    .backgroundColor(0xD2B48C)

  Text('flexBasis(100)')  // 未设置width以及flexBasis值为100,宽度为100vp
    .flexBasis(100)  
    .height(100)
    .backgroundColor(0xF5DEB3)

  Text('flexBasis(100)')
    .flexBasis(100)
    .width(200) // flexBasis值为100,覆盖width的设置值,宽度为100vp
    .height(100)
    .backgroundColor(0xD2B48C)
}.width('90%').height(120).padding(10).backgroundColor(0xAFEEEE)

  • flexGrow: 设置父容器的剩余空间分配给此属性所在组件的比例。用于"瓜分"父组件的剩余空间。
Flex() {
  Text('flexGrow(1)')
    .flexGrow(1) 
    .width(100)
    .height(100)
    .backgroundColor(0xF5DEB3)
  
  Text('flexGrow(2)')
    .flexGrow(2)
    .width(100)
    .height(100)
    .backgroundColor(0xD2B48C)

  Text('no flexGrow')
    .width(100) 
    .height(100)
    .backgroundColor(0xF5DEB3)
}.width(400).height(120).padding(10).backgroundColor(0xAFEEEE)

上图中,父容器宽度400vp, 三个子组件原始宽度为100vp,综合300vp,剩余空间100vp根据flexGrow值的占比分配给子组件,未设置flexGrow的子组件不参与“瓜分”。

第一个元素以及第二个元素以2:3分配剩下的100vp。第一个元素为100vp+100vp2/5=140vp,第二个元素为100vp+100vp3/5=160vp。

  • flexShrink: 当父容器空间不足时,子组件的压缩比例。

Flex({ direction: FlexDirection.Row }) {
  Text('flexShrink(3)')
    .flexShrink(3)
    .width(200)
    .height(100)
    .backgroundColor(0xF5DEB3)
  
  Text('no flexShrink')
    .width(200)
    .height(100)
    .backgroundColor(0xD2B48C)

  Text('flexShrink(2)')
    .flexShrink(2)
    .width(200)
    .height(100)
    .backgroundColor(0xF5DEB3)  
}.width(400).height(120).padding(10).backgroundColor(0xAFEEEE) 

1.3.5 -> 场景示例

使用弹性布局,可以实现子组件沿水平方向排列,两端对齐,子组件间距平分,竖直方向上子组件居中的效果。示例如下:

@Entry  
@Component
struct FlexExample {
  build() {
    Column() {
      Column({ space: 5 }) {
        Flex({ direction: FlexDirection.Row, wrap: FlexWrap.NoWrap, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
          Text('1').width('30%').height(50).backgroundColor(0xF5DEB3)
          Text('2').width('30%').height(50).backgroundColor(0xD2B48C)
          Text('3').width('30%').height(50).backgroundColor(0xF5DEB3)
        }
        .height(70)
        .width('90%')
        .backgroundColor(0xAFEEEE)
      }.width('100%').margin({ top: 5 })
    }.width('100%') 
 }
}

1.4 -> 网格布局

网格布局(GridLayout)是自适应布局中一种重要的布局,具备较强的页面均分能力,子组件占比控制能力。通过​​​​​​​Grid容器组件和子组件​​​​​​​GridItem实现,Grid用于设置网格布局相关参数,GridItem定义子组件相关特征。优势如下:

  1. 容器组件尺寸发生变化时,所有子组件以及间距等比例调整,实现布局的自适应能力。
  2. 支持自定义网格布局行数和列数,以及每行每列尺寸占比。
  3. 支持设置网格布局中子组件的行列间距。
  4. 支持设置子组件横跨几行或者几列。

1.4.1 -> 容器组件Grid设置

行列数量占比

通过Grid的组件的columnsTemplate和rowTemplate属性设置网格布局行列数量与尺寸占比。

下面以columnsTemplate为例,介绍该属性的设置,该属性值是一个由多个空格和'数字+fr'间隔拼接的字符串,fr的个数即网格布局的列数,fr前面的数值大小,用于计算该列在网格布局宽度上的占比,最终决定该列的宽度。

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%')
  }
}

定义了四个等分的列,每列宽度相等。

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%')
  }
}

定义了四列,每列宽度比值为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%')
  }
}

定义了三列,每列宽度比值为4:2:3。

效果如下:

排列方式

通过layoutDirection可以设置网格布局的主轴方向,决定子组件的排列方式。

可选值包括Row,RowReverse, Column, ColumnReverse四种情况。

效果如下:

行列间距

columnsGap用于设置网格子组件GridItem垂直方向的间距,rowsGap用于设置GridItem水平方向的间距。

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

上图中,设置网格布局子组件间的垂直间距为20,水平间距为10。

1.4.2 -> 网格子组件GridItem设置

设置子组件占的行列数

网格布局的行列标号从1开始,依次编号。

子组件横跨多行时,通过rowStart设置子组件起始行编号,rowEnd设置终点行编号。当rowStart值与rowEnd值相同时,子组件只占一个网格。示例如下:

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 -> 场景示例

使用grid布局实现一个计算器的排布效果,代码如下:

@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 })
  }
}

在大屏设备上展示效果如下:

在小屏设备下展示效果如下:

2 -> 响应式布局

2.1 -> 栅格布局

栅格系统作为一种辅助布局的定位工具,在平面设计和网站设计都起到了很好的作用,对移动设备的界面设计有较好的借鉴作用。总结栅格系统对于移动设备的优势主要有:

  1. 给布局提供一种可循的规律,解决多尺寸多设备的动态布局问题。
  2. 给系统提供一种统一的定位标注,保证各模块各设备的布局一致性。
  3. 给应用提供一种灵活的间距调整方法,满足特殊场景布局调整的可能性。

为实现栅格布局效果,声明式范式提供了​​​​​​​GridContainer栅格容器组件,配合其子组件的通用属性useSizeType来实现栅格布局。

2.1.1 -> 栅格系统

栅格系统有Column、Margin、Gutter三个概念。

  1. Gutter:元素之间的距离,决定了内容间的紧密程度。作为栅格布局的统一规范。为了保证较好的视觉效果,通常gutter的取值不会大于margin的取值。

  2. Margin:内容距栅格容器边缘的距离,决定了内容可展示的总宽度。作为栅格布局的统一规范。

  3. Column:栅格布局的主要定位工具。根据设备的不同尺寸,把栅格容器分割成不同的列数,在保证margin和gutter符合规范的情况下,根据总Column的个数计算每个Column列的宽度。

系统栅格断点

栅格系统以设备的水平宽度(屏幕密度像素值,vp)作为断点依据,定义设备的宽度类型,设置栅格总列数,间隔,边距,形成了一套断点规则。

不同设备水平宽度下,栅格系统默认总列数(columns),边距(margin),间隔(gutter)定义如下:

设备水平宽度断点范围设备宽度类型描述columnsguttermargin
0 < 水平宽度 < 320vpXS最小宽度类型设备。212vp12vp
320vp <= 水平宽度 < 600vpSM小宽度类型设备。424vp24vp
600vp <= 水平宽度 < 840vpMD中等宽度类型设备。824vp32vp
840 <= 水平分辨率LG大宽度类型设备。1224vp48vp

2.1.2 -> GridContainer栅格组件使用

首先使用栅格容器组件创建栅格布局。

栅格容器创建与设置

通过接口 GridContainer(options?: { columns?: number | 'auto', sizeType?: SizeType, gutter?: Length, margin?: Length}) 创建栅格容器,栅格容器内的所有子组件可以使用栅格布局。

通过参数定义栅格布局的总列数(columns),间隔(gutter),两侧边距(margin)。例如栅格容器总共分为6列,列与列间隔为10vp, 两侧边距为20vp:

GridContainer({ columns: 6, gutter: 10, margin: 20 }) {}

栅格容器不设置参数,或者sizeType设置为SizeType. Auto时使用默认的栅格系统定义,如:

GridContainer() {}
GridContainer({ sizeType: SizeType.Auto })

上述例子中,默认在小宽度类型设备(SizeType. SM)上,栅格容器被分为4列,列与列的间隔为24vp, 两侧边距是24vp。在中等宽度类型设备(SizeType. MD)上,栅格容器被分为8列,列与列的间隔为24vp,两侧边距是32vp。

也可以通过参数sizeType指定此栅格容器内的组件使用此设备宽度类型的栅格设置,如:

  GridContainer({ sizeType: SizeType.SM }) {
       Row() {
         Text('1')
           .useSizeType({
             xs: { span: 2, offset: 0 },
             sm: { span: 3, offset: 0 },
             md: { span: 6, offset: 2 },
             lg: { span: 8, offset: 2 },
           })
       }
    }

上述例子中,不管在任何宽度类型的设备上, Text组件都使用SizeType. SM类型的栅格设置, 即占用3列,放置在第1列。

子组件的栅格设置

栅格容器中的组件使用通用属性useSizeType设置不同的设备宽度类型的占用列数和列偏移。其中span表示栅格容器组件占据columns的数量;offset表示列偏移量,指将组件放置在哪一个columns上。 如:

GridContainer() {
   Row() {
     Text('1')
       .useSizeType({
         xs: { span: 2, offset: 0 },
         sm: { span: 2, offset: 0 },
         md: { span: 6, offset: 2 },
         lg: { span: 8, offset: 2 },
       })
   }
}

其中 sm: { span: 2, offset: 0 } 指在设备宽度类型为SM的设备上,Text组件占用2列,且放在栅格容器的第1列上。

2.1.3 -> 场景示例

使用栅格布局可以灵活地在不同的设备宽度类型下呈现合适的效果,不必写大量的代码兼容不同宽度类型的设备。

@Entry
@Component
struct GridContainerExample {
  build() {
    Column({ space: 5 }) {
      GridContainer({ columns: 6 }) {
        Flex({justifyContent:FlexAlign.SpaceAround}) {
          Text('1')
            .useSizeType({
              xs: { span: 2, offset: 0 },
              sm: { span: 2, offset: 0 },
              md: { span: 1, offset: 0 },
              lg: { span: 1, offset: 0 },
            })
            .height(100).backgroundColor(0x4682B4).textAlign(TextAlign.Center)
          Text('2')
            .useSizeType({
              xs: { span: 2, offset: 0 },
              sm: { span: 2, offset: 0 },
              md: { span: 4, offset: 0 },
              lg: { span: 4, offset: 0 },
            })
            .height(100).backgroundColor(0x46F2B4).textAlign(TextAlign.Center)
          Text('3')   
            .useSizeType({
              xs: { span: 2, offset: 0 },
              sm: { span: 2, offset: 0 },
              md: { span: 1, offset: 0 },
              lg: { span: 1, offset: 0 },
            })
            .height(100).backgroundColor(0x46A2B4).textAlign(TextAlign.Center)
        }
      }.width('80%').backgroundColor('gray')
    }.width('100%').margin({ top: 15 })
  }
}

小宽度类型设备(SizeType. SM)运行效果:

中等宽度类型设备(SizeType. MD)运行效果:

2.2 -> 媒体查询

媒体查询(Media Query)作为响应式设计的核心,在移动设备上应用十分广泛。它根据不同设备类型或同设备不同状态修改应用的样式。媒体查询的优势有:

  1. 提供丰富的媒体特征监听能力,针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。

  2. 当屏幕发生动态改变时(比如分屏、横竖屏切换),同步更新应用的页面布局。

2.2.1 -> 媒体查询引入与使用流程

媒体查询通过媒体查询接口,设置查询条件并绑定回调函数,在对应的条件的回调函数里更改页面布局或者实现业务逻辑,实现页面的响应式设计。具体步骤如下:

首先导入媒体查询模块。

import mediaquery from '@ohos.mediaquery'

通过matchMediaSync接口设置媒体查询条件,保存返回的条件监听句柄listener。

listener = mediaquery.matchMediaSync('(orientation: landscape)')

给条件监听句柄listener绑定回调函数onPortrait,当listener检测设备状态变化时执行回调函数。在回调函数内,根据不同设备状态更改页面布局或者实现业务逻辑。

onPortrait(mediaQueryResult) {
    if (mediaQueryResult.matches) {
        // do something here
    } else {
        // do something here
    }
}
listener.on('change', onPortrait)

2.2.2 -> 媒体查询条件

媒体查询条件由媒体类型,逻辑操作符,媒体特征组成,其中媒体类型可省略,逻辑操作符用于连接不同媒体类型与媒体特征,其中,媒体特征要使用()包裹且可以有多个。具体规则如下:

语法规则

[media-type] [and|not|only] [(media-feature)]

例如:

screen and(round-screen: true):当设备屏幕是圆形时条件成立。

(max-height: 800):当高度小于等于800时条件成立。

(height <= 800):当高度小于等于800时条件成立。

screen and (device-type: tv) or (resolution < 2) :包含多个媒体特征的多条件复杂语句查询,当设备类型为tv或设备分辨率小于2时条件成立。

媒体类型(media-type)

类型说明
screen按屏幕相关参数进行媒体查询。

媒体逻辑操作(and|or|not|only)

媒体逻辑操作符:and、or、not、only用于构成复杂媒体查询,也可以通过comma(, )将其组合起来,详细解释说明如下表。

表1 媒体逻辑操作符
类型说明
and

将多个媒体特征(Media Feature)以“与”的方式连接成一个媒体查询,只有当所有媒体特征都为true,查询条件成立。另外,它还可以将媒体类型和媒体功能结合起来。

例如:screen and (device-type: wearable) and (max-height: 600) 表示当设备类型是智能穿戴且应用的最大高度小于等于600个像素单位时成立。

or

将多个媒体特征以“或”的方式连接成一个媒体查询,如果存在结果为true的媒体特征,则查询条件成立。

例如:screen and (max-height: 1000) or (round-screen:true)表示当应用高度小于等于1000个像素单位或者设备屏幕是圆形时,条件成立。

not

取反媒体查询结果,媒体查询结果不成立时返回true,否则返回false。

例如:not screen and (min-height: 50) and (max-height: 600) 表示当应用高度小于50个像素单位或者大于600个像素单位时成立。

使用not运算符时必须指定媒体类型。

only

当整个表达式都匹配时,才会应用选择的样式,可以应用在防止某些较早的版本的浏览器上产生歧义的场景。一些较早版本的浏览器对于同时包含了媒体类型和媒体特征的语句会产生歧义,比如:

screen and (min-height: 50)

老版本浏览器会将这句话理解成screen,从而导致仅仅匹配到媒体类型(screen),就应用了指定样式,使用only可以很好地规避这种情况。

使用only时必须指定媒体类型。

,(comma)

将多个媒体特征以“或”的方式连接成一个媒体查询,如果存在结果为true的媒体特征,则查询条件成立。其效果等同于or运算符。

例如:screen and (min-height: 1000), (round-screen:true) 表示当应用高度大于等于1000个像素单位或者设备屏幕是圆形时,条件成立。

在MediaQuery Level 4中引入了范围查询,使其能够使用max-,min-的同时,也支持了< =,> =,< ,> 操作符。

表2 媒体逻辑范围操作符
类型说明
<=小于等于,例如:screen and (height < = 50)。
>=大于等于,例如:screen and (height > = 600)。
<小于,例如:screen and (height < 50)。
>大于,例如:screen and (height > 600)。

媒体特征(media-feature)

类型说明
height应用页面显示区域的高度。
min-height应用页面显示区域的最小高度。
max-height应用页面显示区域的最大高度。
width应用页面显示区域的宽度。
min-width应用页面显示区域的最小宽度。
max-width应用页面显示区域的最大宽度。
resolution

设备的分辨率,支持dpi,dppx和dpcm单位。其中:

- dpi表示每英寸中物理像素个数,1dpi≈0.39dpcm;

- dpcm表示每厘米上的物理像素个数,1dpcm ≈ 2.54dpi;

- dppx表示每个px中的物理像素数(此单位按96px=1英寸为基准,与页面中的px单位计算方式不同),1dppx = 96dpi。

min-resolution设备的最小分辨率。
max-resolution设备的最大分辨率。
orientation

屏幕的方向。

可选值:

- orientation: portrait(设备竖屏)

- orientation: landscape(设备横屏)

device-height设备的高度。
min-device-height设备的最小高度。
max-device-height设备的最大高度。
device-width设备的宽度。
min-device-width设备的最小宽度。
max-device-width设备的最大宽度。
round-screen屏幕类型,圆形屏幕为true, 非圆形屏幕为 false。
dark-mode系统为深色模式时为true,否则为false。

2.2.3 -> 场景示例

下例中使用媒体查询,实现屏幕横竖屏切换时给页面文本应用不同的内容和样式的效果。

import mediaquery from '@ohos.mediaquery'

let portraitFunc = null

@Entry
@Component
struct MediaQueryExample {
  @State color: string = '#DB7093'
  @State text: string = 'Portrait'
  listener = mediaquery.matchMediaSync('(orientation: landscape)') // 当设备横屏时条件成立

  onPortrait(mediaQueryResult) {
    if (mediaQueryResult.matches) {
      this.color = '#FFD700'
      this.text = 'Landscape'
    } else {
      this.color = '#DB7093'
      this.text = 'Portrait'
    }
  }

  aboutToAppear() {
    portraitFunc = this.onPortrait.bind(this) // 绑定当前应用实例
    this.listener.on('change', portraitFunc)
  }

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Text(this.text).fontSize(50).fontColor(this.color)
    }
    .width('100%').height('100%')
  }
}

横屏下文本内容为Landscape,颜色为#FFD700。

非横屏下文本内容为Portrait,颜色为#DB7093。


感谢各位大佬支持!!!

互三啦!!!


http://www.kler.cn/a/510557.html

相关文章:

  • 数据结构入门
  • ChatGPT 写作系列
  • mysql_real_connect的概念和使用案例
  • Python Pyside6 加Sqlite3 写一个 通用 进销存 系统 初型
  • Solidity01 Solidity极简入门
  • 【逆境中绽放:万字回顾2024我在挑战中突破自我】
  • 硬件学习笔记--33 GB/T 17215.211相关内容介绍
  • 双序列双指针
  • Python(十七)excel指定列自动翻译成英文
  • 多学科视角下探索开源Github、Git初步学习
  • 企业邮箱iRedMail搭建
  • 一岁征程:学习、挑战与成长
  • wireshark工具简介
  • 如何引导LabVIEW项目相关方合理参与项目?
  • 【2024年华为OD机试】(C卷,100分)- 求满足条件的最长子串的长度 (Java JS PythonC/C++)
  • Spring篇 解决因为Bean的循环依赖——理论篇
  • 基于springboot的教师人事档案管理系统
  • vue移动端统计分析echarts开发小结(多提示框渲染,下载适配,数据量大的时候不分页崩溃等)
  • 用LSTM模型预测股价的例子(1)
  • Chromium 132 编译指南 Linux 篇 - 安装 Chromium 官方工具(三)
  • 河北省乡镇界面图层shp格式arcgis数据乡镇名称和编码2020年wgs84坐标无偏移内容测评
  • 山西省乡镇界面图层shp格式arcgis数据乡镇名称和编码2020年wgs84坐标无偏移测评
  • HRNet,Deep High-Resolution Representation Learning for Visual Recognition解读
  • 缓存、数据库双写一致性解决方案
  • 计算机毕业设计PySpark+Hadoop+Hive机票预测 飞机票航班数据分析可视化大屏 航班预测系统 机票爬虫 飞机票推荐系统 大数据毕业设计
  • Object常用的方法及开发中的使用场景