【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@Componentstruct ListExample1 {@State arr: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]@State alignListItem: ListItemAlign = ListItemAlign.Startbuild() {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@Componentstruct ListExample2 {@State arr: string[] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]@State alignListItem: ListItemAlign = ListItemAlign.Startbuild() {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.mzph.cn/bicheng/68603.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

物联网网关Web服务器--Boa服务器移植与测试

1、Boa服务器介绍 BOA 服务器是一个小巧高效的web服务器&#xff0c;是一个运行于unix或linux下的&#xff0c;支持CGI的、适合于嵌入式系统的单任务的http服务器&#xff0c;源代码开放、性能高。 Boa 嵌入式 web 服务器的官方网站是http://www.boa.org/。 特点 轻量级&#x…

tomcat状态一直是Exited (1)

docker run -di -p 80:8080 --nametomcat001 你的仓库地址/tomcat:9执行此命令后tomcat一直是Exited(1)状态 解决办法&#xff1a; 用以下命令创建运行 docker run -it --name tomcat001 -p 80:8080 -d 你的仓库地址/tomcat:9 /bin/bash最终结果 tomcat成功启动

三天急速通关Java基础知识:Day1 基本语法

三天急速通关JAVA基础知识&#xff1a;Day1 基本语法 0 文章说明1 关键字 Keywords2 注释 Comments2.1 单行注释2.2 多行注释2.3 文档注释 3 数据类型 Data Types3.1 基本数据类型3.2 引用数据类型 4 变量与常量 Variables and Constant5 运算符 Operators6 字符串 String7 输入…

表单中在不设置required的情况下在label前加*必填标识

参考:https://blog.csdn.net/qq_55798464/article/details/136233718 需求&#xff1a;在发票类型前面添加*必填标识 我最开始直接给发票类型这个表单类型添加了验证规则required:true&#xff0c;问题来了&#xff0c;这个发票类型它是有默认值的&#xff0c;所以我点击保存…

2025寒假备战蓝桥杯01---朴素二分查找的学习

文章目录 1.暴力方法的引入2.暴力解法的思考 与改进3.朴素二分查找的引入4.朴素二分查找的流程5.朴素二分查找的细节6.朴素二分查找的题目 1.暴力方法的引入 对于下面的这个有序的数据元素的组合&#xff0c;我们的暴力解法就是挨个进行遍历操作&#xff0c;一直找到和我们的这…

ROS机器人学习和研究的势-道-术-转型和变革的长期主义习惯

知易行难。说说容易做到难。 例如&#xff0c;不受成败评价影响&#xff0c;坚持做一件事情10年以上&#xff0c;专注事情本身。 机器人专业不合格且失败讲师如何让内心保持充盈的正能量&#xff08;节选&#xff09;-CSDN博客 时间积累 注册20年。 创作历程10年。 创作10年…

渗透测试之XEE[外部实体注入]漏洞 原理 攻击手法 xml语言结构 防御手法

目录 原理 XML语言解释 什么是xml语言&#xff1a; 以PHP举例xml外部实体注入 XML语言结构 面试题目 如何寻找xxe漏洞 XEE漏洞修复域防御 提高版本 代码修复 php java python 手动黑名单过滤(不推荐) 一篇文章带你深入理解漏洞之 XXE 漏洞 - 先知社区 原理 XXE&…

BUUCTF_Web([GYCTF2020]Ezsqli)

1.输入1 &#xff0c;正常回显。 2.输入1 &#xff0c;报错false&#xff0c;为字符型注入&#xff0c;单引号闭合。 原因&#xff1a; https://mp.csdn.net/mp_blog/creation/editor/145170456 3.尝试查询字段&#xff0c;回显位置&#xff0c;数据库&#xff0c;都是这个。…

react使用react-redux状态管理

1、安装 npm install react-redux2、创建store.js import { createStore } from redux;// 定义初始状态 const initialState {counter: 888 };// 定义 reducer 函数&#xff0c;根据 action 类型更新状态 function reducer(state initialState, action) {switch (action.ty…

JAVA使用自定义注解,在项目中实现EXCEL文件的导出

首先定义一个注解 Retention(RetentionPolicy.RUNTIME) Target(ElementType.FIELD) public interface Excel {/*** 导出时在excel中排序*/int sort() default Integer.MAX_VALUE;/*** 导出到Excel中的名字.*/String name() default "";/*** 首行字段的批注*/String …

【2024年华为OD机试】 (B卷,100分)- 金字塔,BOSS的收入(Java JS PythonC/C++)

一、问题描述 微商模式收入计算 题目描述 微商模式中&#xff0c;下级每赚 100 元就要上交 15 元。给定每个级别的收入&#xff0c;求出金字塔尖上的人的收入。 输入描述 第一行输入 N&#xff0c;表示有 N 个代理商上下级关系。接下来输入 N 行&#xff0c;每行三个数&am…

光伏储能交直流微电网Matlab/Simulink仿真模型

博士毕业后项目和课题的交接工作也都基本上结束了&#xff0c;之前从20年我博一开始创作的博客&#xff0c;我也将从25年伊始重新进行更新&#xff0c;在保留原有内容的基础上&#xff0c;在对现如今的研究热点进行补充&#xff0c;希望能为各位校友提供一定的研究思路。首先是…

Windows中安装RabbitMQ

安装Erlang 下载地址&#xff1a;https://www.erlang.org/downloads 配置环境变量 变量名&#xff1a;ERLANG_HOME 变量值&#xff1a;D:\tools\Erlang-27.2 Path&#xff1a;%ERLANG_HOME%\bin 验证配置是否成功 erl 安装RabbitMQ 下载地址&#xff1a;https://www.rabbitm…

线性代数概述

矩阵与线性代数的关系 矩阵是线性代数的研究对象之一&#xff1a; 矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合&#xff0c;是线性代数中的核心概念之一。矩阵的定义和性质构成了线性代数中矩阵理论的基础&#xff0c;而矩阵运算则简洁地表示和…

2024年度总结:从后端Java到全栈成长的蜕变

目录 前言1. 用数据与实践书写成长篇章2. 技术与生活的双重蜕变3. 技术的进阶与生活的绽放 前言 今年是我入行的第十年&#xff0c;也是记录在CSDN平台上的第五年。这五年来&#xff0c;我始终坚持记录成长的点滴&#xff0c;将个人事业与博客创作紧密相连。一路走来&#xff0…

关于new和delete的匹配问题

目录 引入 原理 引入 大家先看如下代码&#xff1a; class A { private:int _a 0; };int main() {A* arr1 new A[10];delete arr1;return 0; } 可以发现这里new了一个数组但却用delete释放。先看运行结果&#xff1a; 可以看到程序正常结束并没有崩 &#xff0c;别急我们…

蓝桥杯3526 子树的大小 | 数学规律

题目传送门 这个题目是一个数学题&#xff0c;比较好的方法是从上往下寻找子树的最左和最右的结点&#xff0c;每层统计子结点数&#xff0c;到树的底部时打印结果。 如何求最左、最右的子结点呢&#xff1f; 对于第i个结点,其前面有i-1个结点,每个结点各有m个孩子,再加上1号结…

计算机毕业设计Python+卷积神经网络租房推荐系统 租房大屏可视化 租房爬虫 hadoop spark 58同城租房爬虫 房源推荐系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Mysql 主从复制原理及其工作过程,配置一主两从实验

主从原理&#xff1a;MySQL 主从同步是一种数据库复制技术&#xff0c;它通过将主服务器上的数据更改复制到一个或多个从服务器&#xff0c;实现数据的自动同步。 主从同步的核心原理是将主服务器上的二进制日志复制到从服务器&#xff0c;并在从服务器上执行这些日志中的操作…

00_专栏《Redis 7.x企业级开发实战教程》介绍

大家好,我是袁庭新。Redis作为一款高性能、多用途的内存数据库,凭借其丰富的数据结构、高速读写能力、原子操作特性及发布订阅等功能,在缓存加速、分布式锁、消息队列等场景中不可或缺,极大提升了系统性能与开发效率,是现代互联网应用架构的关键组件。 你是否在学习Redis…