目录
四. 布局高级
今日核心:
- 线性布局
- Flex 布局
1. 线性布局
线性布局(LinearLayout)是开发中最常用的布局,通过线性容器 Row 和 Column 构建。Column容器内子元素按照垂直方向排列,Row容器内子元素按照水平方向排列。根据不同的排列方向,开发者可选择使用Row或Column容器创建线性布局。
1.1. 间距
在布局容器内,可以通过 space 属性设置布局主方向方向上子元素的间距,使各子元素在布局主方向上有等间距效果。
1.1.1. Row
@Entry
@Component
struct Index {
build() {
Row({ space: 20}) {
Text('子元素1')
.width(100)
.height(40)
.backgroundColor(Color.Pink)
Text('子元素2')
.width(100)
.height(40)
.backgroundColor(Color.Orange)
Text('子元素3')
.width(100)
.height(40)
.backgroundColor(Color.Brown)
}
}
}
1.1.2. Column
@Entry
@Component
struct Index {
build() {
Column({ space: 20}) {
Text('子元素1')
.width(100)
.height(40)
.backgroundColor(Color.Pink)
Text('子元素2')
.width(100)
.height(40)
.backgroundColor(Color.Orange)
Text('子元素3')
.width(100)
.height(40)
.backgroundColor(Color.Brown)
}
}
}
1.2. 布局主方向对齐方式
属性:justifyContent()
参数:枚举FlexAlign
属性 | 描述 |
Start | 首端对齐 |
Center | 居中对齐 |
End | 尾部对齐 |
Spacebetween | 两端对齐 子元素之间间距相等 |
SpaceAround | 子元素两侧间距相等 第一个元素到行首的距离和最后一个元素到行尾的距离是相邻元素之间距离的一半 |
SpaceEvenly | 相邻元素之间的距离、第一个元素与行首的间距、最后一个元素到行尾的间距都完全一样 |
@Entry
@Component
struct Index {
build() {
Row() {
Text('子元素1')
.width(80)
.height(40)
.backgroundColor(Color.Pink)
Text('子元素2')
.width(80)
.height(40)
.backgroundColor(Color.Orange)
Text('子元素3')
.width(80)
.height(40)
.backgroundColor(Color.Brown)
}
.width('100%')
.height(200)
.backgroundColor('#ccc')
// 主布局方向的对齐方式
.justifyContent(FlexAlign.Center)
// 两端对齐
.justifyContent(FlexAlign.SpaceBetween)
}
}
概念:
- 布局主方向在线性布局中叫做布局主轴
- 另外一条坐标轴叫做交叉轴
1.3. 案例-得物-产品卡片
@Entry
@Component
struct Index {
build() {
Column() {
// 产品卡片
Column({space: 10}) {
// 图片
Image($r('app.media.nick'))
.width('100%')
.aspectRatio(1)
.borderRadius({topLeft:5, topRight: 5})
// 标题文字
Text('今晚吃这个 | 每日艺术分享 No.43')
.fontSize(14)
.fontWeight(600)
.fontColor('#414141')
.lineHeight(22)
.padding({left: 10, right: 10})
// 用户与点赞
Row() {
// 用户
Row({space: 5}){
Image($r('app.media.user'))
.width(15)
.borderRadius(10)
Text('插画师分享聚集地')
.fontSize(10)
.fontColor('#787a7d')
}
// 点赞
Row({ space: 5}){
Image($r('app.media.ic_like'))
.width(10)
.aspectRatio(1)
.fillColor('#c2c0ca')
Text('2300')
.fontSize(10)
.fontColor('#787a7d')
}
}
.width('100%')
.padding({left: 10, right: 10})
.justifyContent(FlexAlign.SpaceBetween)
}
.width(200)
.padding({bottom:10})
.backgroundColor('#fff')
.borderRadius(5)
}
.width('100%')
.height('100%')
.backgroundColor('#f5f4f9')
.padding(10)
}
}
1.4. 交叉轴对齐方式
以 Row 为例,主轴在水平方向,交叉轴在垂直方向
属性:alignItems()
参数:枚举类型VerticalAlign
注意:布局容器在交叉轴要有足够空间,否则无法生效
@Entry
@Component
struct Index {
build() {
Row() {
Text('子元素1')
.width(80)
.height(40)
.backgroundColor(Color.Pink)
Text('子元素2')
.width(80)
.height(40)
.backgroundColor(Color.Orange)
Text('子元素3')
.width(80)
.height(40)
.backgroundColor(Color.Brown)
}
.width('100%')
.height(200)
.backgroundColor('#ccc')
// 顶部对齐
.alignItems(VerticalAlign.Top)
// 底部对齐
.alignItems(VerticalAlign.Bottom)
}
}
1.5. 单个子元素交叉轴对齐方式
属性:alignSelf()
参数:枚举ItemAlign
(Stretch拉伸,交叉轴拉伸效果)
@Entry
@Component
struct Index {
build() {
Row() {
Text('子元素1')
.width(80)
.height(40)
.backgroundColor(Color.Pink)
Text('子元素2')
.width(80)
.height(40)
.backgroundColor(Color.Orange)
// 交叉轴方向尺寸拉伸
.alignSelf(ItemAlign.Stretch)
Text('子元素3')
.width(80)
.height(40)
.backgroundColor(Color.Brown)
}
.width('100%')
.height(200)
.backgroundColor('#ccc')
}
}
1.6. 自适应缩放
父容器尺寸确定时,设置了 layoutWeight
属性的子元素与兄弟元素占主轴尺寸按照权重进行分配。
属性:layoutWeight()
参数:数字
@Entry
@Component
struct Index {
build() {
Column() {
// 主轴方向:水平方向
Row() {
Text('左侧')
.width(60)
.height(30)
.backgroundColor('#ccc')
Text('中间')
.height(30)
.backgroundColor(Color.Pink)
.layoutWeight(1)
Text('右侧 layoutWeight')
.height(30)
.backgroundColor('#fc0')
.layoutWeight(1)
}
}
}
}
1.7. 案例-用户关注
@Entry
@Component
struct Index {
build() {
Row() {
Image($r('app.media.user'))
.width(30)
.margin({right: 5})
.borderRadius(15)
Text('插画设计师')
.fontSize(13)
.fontWeight(700)
.layoutWeight(1)
Text('关注')
.width(40)
.height(25)
.border({width: 1, color: '#999'})
.borderRadius(3)
.fontSize(12)
.textAlign(TextAlign.Center)
.fontColor('#999')
}
.width('100%')
.height(40)
.padding({left: 10, right: 10})
.margin({top: 20})
}
}
1.8. 线性布局-Column
- Column 主轴方向: 垂直方向
- Column 交叉轴方向:水平方向
@Entry
@Component
struct Index {
build() {
Column() {
Text('1')
.width(100)
.height(40)
.backgroundColor(Color.Pink)
Text('2')
.width(100)
.height(40)
.backgroundColor(Color.Orange)
// 单个元素-交叉轴对齐方式
.alignSelf(ItemAlign.End)
Text('3')
.width(100)
.height(40)
.backgroundColor(Color.Pink)
}
.width('100%')
.height(300)
.backgroundColor('#ccc')
// 主轴对齐方式 -- 与Row写法一致
.justifyContent(FlexAlign.SpaceBetween)
// 交叉轴对齐方式(水平方向),默认居中
.alignItems(HorizontalAlign.Start)
}
}
1.9. 案例-头条新闻
@Entry
@Component
struct Index {
build() {
Column() {
Row({space: 10}) {
// 左侧
Column() {
Text('第五个国际数学日,全世界和数据一起玩儿')
.fontSize(14)
.fontColor('#444')
Row() {
Text('中国青年网 昨天')
.fontSize(12)
.fontColor('#999')
.layoutWeight(1)
Image($r('app.media.ic_close'))
.width(14)
.fillColor('#999')
}
.width('100%')
// .justifyContent(FlexAlign.SpaceBetween)
}
.height('100%')
.layoutWeight(1)
// 水平左对齐 -- 交叉轴
.alignItems(HorizontalAlign.Start)
// 垂直两端对齐 -- 主轴
.justifyContent(FlexAlign.SpaceBetween)
// 右侧
Image($r('app.media.math'))
.width(100)
}
.width('100%')
.height(80)
// .backgroundColor('#ccc')
.padding(5)
.margin({top: 20})
}
}
}
2. 弹性布局(Flex)
2.1. 换行
弹性布局分为单行布局和多行布局。默认情况下,Flex 容器中的子元素都排在一条线(又称“轴线”)上。子元素尺寸总和大于 Flex 容器尺寸时,子元素尺寸会自动挤压。
wrap
属性控制当子元素主轴尺寸之和大于容器主轴尺寸时,Flex 是单行布局还是多行布局。在多行布局时,通过交叉轴方向,确认新行排列方向。
参数:wrap
值:枚举 FlexWrap
@Entry
@Component
struct Index {
build() {
Column() {
Flex({wrap:FlexWrap.Wrap}) {
Text()
.width(80)
.height(40)
.backgroundColor(Color.Pink)
.margin(5)
Text()
.width(80)
.height(40)
.backgroundColor(Color.Orange)
.margin(5)
Text()
.width(80)
.height(40)
.backgroundColor(Color.Pink)
.margin(5)
Text()
.width(80)
.height(40)
.backgroundColor(Color.Orange)
.margin(5)
Text()
.width(80)
.height(40)
.backgroundColor(Color.Pink)
.margin(5)
Text()
.width(80)
.height(40)
.backgroundColor(Color.Orange)
.margin(5)
}
.width('100%')
.height(200)
.backgroundColor('#ccc')
}
}
}
2.2. 案例-阶段菜单
@Entry
@Component
struct Index {
build() {
Column() {
Text('阶段选择')
.fontWeight(600)
.fontSize(24)
.width('100%')
.padding(15)
Flex({ wrap: FlexWrap.Wrap }) {
Text('ArkUI')
.padding(10)
.margin(5)
.backgroundColor('#f6f7f9')
Text('ArkTS')
.padding(10)
.margin(5)
.backgroundColor('#f6f7f9')
Text('界面开发')
.padding(10)
.margin(5)
.backgroundColor('#f6f7f9')
Text('系统能力')
.padding(10)
.margin(5)
.backgroundColor('#f6f7f9')
Text('权限控制')
.padding(10)
.margin(5)
.backgroundColor('#f6f7f9')
Text('元服务')
.padding(10)
.margin(5)
.backgroundColor('#f6f7f9')
}
}
.width('100%')
.height('100%')
.padding(10)
}
}
3. 案例-微店商品
@Entry
@Component
struct Index {
build() {
Column() {
Flex({ wrap: FlexWrap.Wrap }) {
Column({ space: 8 }) {
Image($r('app.media.top1'))
.width('100%')
.borderRadius({topLeft: 5, topRight: 5})
Text('[ 程序员必备 ] 最高版本-格子衫')
.fontSize(12)
.maxLines(1)
.textOverflow({overflow: TextOverflow.Ellipsis})
.padding({left: 10, right: 10})
Text() {
Span('¥666 ')
.fontSize(12)
.fontColor('#f52555')
Span('销量666')
.fontSize(8)
.fontColor('#999')
}
.padding({left: 10, right: 10})
}
.width('47%')
.backgroundColor('#fff')
.margin({right: 10, bottom: 10})
.borderRadius(5)
.alignItems(HorizontalAlign.Start)
.padding({bottom: 10})
Column({ space: 8 }) {
Image($r('app.media.top2'))
.width('100%')
.borderRadius({topLeft: 5, topRight: 5})
Text('[ 程序员必备 ] 最高版本-格子衫')
.fontSize(12)
.maxLines(1)
.textOverflow({overflow: TextOverflow.Ellipsis})
.padding({left: 10, right: 10})
Text() {
Span('¥888 ')
.fontSize(12)
.fontColor('#f52555')
Span('销量888')
.fontSize(8)
.fontColor('#999')
}
.padding({left: 10, right: 10})
}
.width('47%')
.backgroundColor('#fff')
.margin({right: 10, bottom: 10})
.borderRadius(5)
.alignItems(HorizontalAlign.Start)
.padding({bottom: 10})
Column({ space: 8 }) {
Image($r('app.media.top3'))
.width('100%')
.borderRadius({topLeft: 5, topRight: 5})
Text('[ 程序员必备 ] 最高版本-格子衫')
.fontSize(12)
.maxLines(1)
.textOverflow({overflow: TextOverflow.Ellipsis})
.padding({left: 10, right: 10})
Text() {
Span('¥333 ')
.fontSize(12)
.fontColor('#f52555')
Span('销量666')
.fontSize(8)
.fontColor('#999')
}
.padding({left: 10, right: 10})
}
.width('47%')
.backgroundColor('#fff')
.margin({right: 10, bottom: 10})
.borderRadius(5)
.alignItems(HorizontalAlign.Start)
.padding({bottom: 10})
Column({ space: 8 }) {
Image($r('app.media.top4'))
.width('100%')
.borderRadius({topLeft: 5, topRight: 5})
Text('[ 程序员必备 ] 最高版本-格子衫')
.fontSize(12)
.maxLines(1)
.textOverflow({overflow: TextOverflow.Ellipsis})
.padding({left: 10, right: 10})
Text() {
Span('¥444 ')
.fontSize(12)
.fontColor('#f52555')
Span('销量666')
.fontSize(8)
.fontColor('#999')
}
.padding({left: 10, right: 10})
}
.width('47%')
.backgroundColor('#fff')
.margin({right: 10, bottom: 10})
.borderRadius(5)
.alignItems(HorizontalAlign.Start)
.padding({bottom: 10})
Column({ space: 8 }) {
Image($r('app.media.top2'))
.width('100%')
.borderRadius({topLeft: 5, topRight: 5})
Text('[ 程序员必备 ] 最高版本-格子衫')
.fontSize(12)
.maxLines(1)
.textOverflow({overflow: TextOverflow.Ellipsis})
.padding({left: 10, right: 10})
Text() {
Span('¥555 ')
.fontSize(12)
.fontColor('#f52555')
Span('销量666')
.fontSize(8)
.fontColor('#999')
}
.padding({left: 10, right: 10})
}
.width('47%')
.backgroundColor('#fff')
.margin({right: 10, bottom: 10})
.borderRadius(5)
.alignItems(HorizontalAlign.Start)
.padding({bottom: 10})
Column({ space: 8 }) {
Image($r('app.media.top5'))
.width('100%')
.borderRadius({topLeft: 5, topRight: 5})
Text('[ 程序员必备 ] 最高版本-格子衫')
.fontSize(12)
.maxLines(1)
.textOverflow({overflow: TextOverflow.Ellipsis})
.padding({left: 10, right: 10})
Text() {
Span('¥444 ')
.fontSize(12)
.fontColor('#f52555')
Span('销量444')
.fontSize(8)
.fontColor('#999')
}
.padding({left: 10, right: 10})
}
.width('47%')
.backgroundColor('#fff')
.margin({right: 10, bottom: 10})
.borderRadius(5)
.alignItems(HorizontalAlign.Start)
.padding({bottom: 10})
}
}
.width('100%')
.height('100%')
.backgroundColor('#f5f6f7')
.padding(10)
}
}