线性布局(LinearLayout)是开发中最常用的布局。线性布局的子组件在线性方向上(水平方向和垂直方向)依次排列。
通过线性容器Row和Column实现线性布局。Column容器内子组件按照垂直方向排列,Row组件中,子组件按照水平方向排列。
线性布局的排列方向由所选容器组件决定。根据不同的排列方向,选择使用Row或Column容器创建线性布局,通过调整space,alignItems,justifyContent属性调整子组件的间距,水平垂直方向的对齐方式。
具体使用以及效果如下表所示:
属性名 | 描述 | Row效果图 | Column效果图 |
|---|---|---|---|
space | - 横向布局中各子组件的在水平方向的间距 - 纵向布局中个子组件垂直方向间距 | ||
alignItems | 容器排列方向的垂直方向上,子组件在父容器中的对齐方式 | ||
justifyContent | 容器排列方向上,子组件在父容器中的对齐方式 |

在线性布局下,常用空白填充组件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. 父容器尺寸确定时,设置了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%')
}
}
}上例中,在任意大小的设备中,子组件的宽度占比固定。
使用组件的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)
}
}自适应延伸是在不同尺寸设备下,当页面显示内容个数不一并延伸到屏幕外时,可通过滚动条拖动展示。适用于线性布局中内容无法一屏展示的场景。常见以下两类实现方法。
List子项过多一屏放不下时,未展示的子项通过滚动条拖动显示。通过scrollBar属性设置滚动条的常驻状态,edgeEffect属性设置拖动到极限的回弹效果。
@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)
}
} @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)
}
} 线性布局中,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。在Column或者Row外层包裹一个可滚动的容器组件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) // 滚动到边沿后回弹
}
}@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) // 滚动到边沿后回弹
}
}层叠布局(StackLayout)用于在屏幕上预留一块区域来显示组件中的元素,提供元素可以重叠的布局。通过层叠容器Stack实现,容器中的子元素依次入栈,后一个子元素覆盖前一个子元素显示。
设置子元素在容器内的对齐方式。支持左上,上中,右上,左,中,右,右下,中下,右下九种对齐方式,如下表所示:
名称 | 描述 | 图示 |
|---|---|---|
TopStart | 顶部起始端 | |
Top | 顶部横向居中 | |
TopEnd | 顶部尾端 | |
Start | 起始端纵向居中 | |
Center | 横向和纵向居中 | |
End | 尾端纵向居中 | |
BottomStart | 底部起始端 | |
Bottom | 底部横向居中 | |
BottomEnd | 底部尾端 | |

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

容器组件属性
通过Flex组件提供的Flex接口创建弹性布局。如下:
Flex(options?: { direction?: FlexDirection, wrap?: FlexWrap, justifyContent?: FlexAlign, alignItems?: ItemAlign, alignContent?: FlexAlign })
参数direction决定主轴的方向,即子组件的排列方向。可选值有:

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)
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)
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)
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)
默认情况下,子组件在Flex容器中都排在一条线(又称"轴线")上。通过wrap参数设置子组件换行方式。可选值有:
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)
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)
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)
主轴对齐
通过justifyContent参数设置在主轴方向的对齐方式,存在下面六种情况:

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)
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)
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)
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)
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)
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参数设置子组件在交叉轴的对齐方式,可选值有:
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)
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)
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)
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)
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)
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布局中生效,可选值有:
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) 
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) 
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) 
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) 
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) 
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) 
在弹性布局父组件尺寸不够大的时候,通过子组件的下面几个属性设置其再父容器的占比,达到自适应布局能力。
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)
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。
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) 
使用弹性布局,可以实现子组件沿水平方向排列,两端对齐,子组件间距平分,竖直方向上子组件居中的效果。示例如下:
@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%')
}
}
网格布局(GridLayout)是自适应布局中一种重要的布局,具备较强的页面均分能力,子组件占比控制能力。通过Grid容器组件和子组件GridItem实现,Grid用于设置网格布局相关参数,GridItem定义子组件相关特征。优势如下:
行列数量占比
通过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开始,依次编号。
子组件横跨多行时,通过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)
使用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 })
}
}在大屏设备上展示效果如下:

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

栅格系统作为一种辅助布局的定位工具,在平面设计和网站设计都起到了很好的作用,对移动设备的界面设计有较好的借鉴作用。总结栅格系统对于移动设备的优势主要有:
为实现栅格布局效果,声明式范式提供了GridContainer栅格容器组件,配合其子组件的通用属性useSizeType来实现栅格布局。
栅格系统有Column、Margin、Gutter三个概念。

系统栅格断点
栅格系统以设备的水平宽度(屏幕密度像素值,vp)作为断点依据,定义设备的宽度类型,设置栅格总列数,间隔,边距,形成了一套断点规则。
不同设备水平宽度下,栅格系统默认总列数(columns),边距(margin),间隔(gutter)定义如下:
设备水平宽度断点范围 | 设备宽度类型 | 描述 | columns | gutter | margin |
|---|---|---|---|---|---|
0 < 水平宽度 < 320vp | XS | 最小宽度类型设备。 | 2 | 12vp | 12vp |
320vp <= 水平宽度 < 600vp | SM | 小宽度类型设备。 | 4 | 24vp | 24vp |
600vp <= 水平宽度 < 840vp | MD | 中等宽度类型设备。 | 8 | 24vp | 32vp |
840 <= 水平分辨率 | LG | 大宽度类型设备。 | 12 | 24vp | 48vp |
首先使用栅格容器组件创建栅格布局。
栅格容器创建与设置
通过接口 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列上。

使用栅格布局可以灵活地在不同的设备宽度类型下呈现合适的效果,不必写大量的代码兼容不同宽度类型的设备。
@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)运行效果:

媒体查询(Media Query)作为响应式设计的核心,在移动设备上应用十分广泛。它根据不同设备类型或同设备不同状态修改应用的样式。媒体查询的优势有:
媒体查询通过媒体查询接口,设置查询条件并绑定回调函数,在对应的条件的回调函数里更改页面布局或者实现业务逻辑,实现页面的响应式设计。具体步骤如下:
首先导入媒体查询模块。
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)媒体查询条件由媒体类型,逻辑操作符,媒体特征组成,其中媒体类型可省略,逻辑操作符用于连接不同媒体类型与媒体特征,其中,媒体特征要使用()包裹且可以有多个。具体规则如下:
语法规则
[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(, )将其组合起来,详细解释说明如下表。
类型 | 说明 |
|---|---|
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-的同时,也支持了< =,> =,< ,> 操作符。
类型 | 说明 |
|---|---|
<= | 小于等于,例如: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。 |
下例中使用媒体查询,实现屏幕横竖屏切换时给页面文本应用不同的内容和样式的效果。
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。
