【学习目标】
- 理解 Flex 布局的主轴、交叉轴、换行规则和方向反转,掌握其与线性布局(Row/Column)的区别与联系;
- 掌握 flexGrow、flexShrink、flexBasis 三大核心属性的计算逻辑与使用场景;
- 掌握 FlexOptions 中核心配置规则,以及 Flex 主轴/交叉轴间距的精准配置方法;
- 能够使用 Flex 实现常见的自适应布局,如“顶部搜索栏+中间推荐标签+底部操作区”的搜索推荐结构。
一、Flex 布局基础与 FlexOptions 核心属性
Flex 布局的核心配置通过 FlexOptions 接口实现,它是控制 Flex 布局行为的唯一入口,包含方向、换行、对齐、间距四大类属性,是 Flex 与 Row/Column 最核心的区别所在。
1.1 FlexOptions 核心属性说明
属性名类型核心作用可选值/枚举生效条件默认值directionFlexDirection定义主轴方向Row、RowReverse、Column、ColumnReverse所有场景RowwrapFlexWrap定义主轴换行规则NoWrap(不换行)、Wrap(自动换行)、WrapReverse(反向换行)所有场景NoWrapspace{main: LengthMetrics, cross: LengthMetrics}双轴间距配置main:主轴间距;cross:交叉轴间距(仅换行生效)无(默认0)alignContentFlexAlign交叉轴多行整体对齐(Flex独有)Start、Center、End、SpaceBetween、SpaceAround、SpaceEvenlywrap = Wrap/WrapReverseStartalignItemsItemAlign交叉轴单行子项对齐Auto、Start、Center、End、Baseline、Stretch所有场景Center1.2 Flex 与 Row/Column 的核心区别(聚焦 FlexOptions 独有能力)
对比维度Flex 布局(FlexOptions)Row/Column 布局方向控制支持FlexOptions.direction动态切换,覆盖正序/反转、横向/纵向全组合方向固定(Row仅横向、Column仅纵向),无反转能力,无对应配置项换行能力支持FlexOptions.wrap配置自动/反向换行,原生适配流式布局无换行能力,子组件超容器时直接溢出/压缩,无对应配置项多行对齐能力独有FlexOptions.alignContent,支持交叉轴多行整体对齐+间距分配无多行对齐能力,仅支持单行子项对齐,无对应配置项单行对齐配置统一通过FlexOptions.alignItems配置,支持ItemAlign全枚举值,默认Center无统一配置项,Row用VerticalAlign、Column用HorizontalAlign,枚举值互通,Row默认Center、Column默认Start间距配置能力支持FlexOptions.space双轴间距,同时控制主轴/交叉轴间距仅支持主轴单一间距,交叉轴无原生间距配置,无对应双轴配置项1.3 选型建议
- 简单布局(标题栏、按钮组、单行列表):优先用 Row/Column(轻量、高性能,无额外布局开销);
- 复杂布局(流式标签、多行列表、需动态切换排列方向):必须用 Flex + FlexOptions 组合,发挥其配置化优势;
- 性能敏感场景(长列表项、高频刷新组件):优先用 Row/Column,避免 Flex 因 FlexOptions 多配置项带来的二次布局性能损耗。
二、工程结构
本节创建新工程 FlexApplication(基于鸿蒙 API 12+ / Stage 模型),用于编写和运行 Flex 布局相关演示代码- FlexApplication/
- ├── AppScope/
- ├── entry/
- │ ├── src/
- │ │ ├── main/
- │ │ │ ├── ets/
- │ │ │ │ ├── entryability/
- │ │ │ │ │ └── EntryAbility.ets
- │ │ │ │ └── pages/
- │ │ │ │ ├── Index.ets # 演示入口页(按钮导航)
- │ │ │ │ ├── FlexBasicPage.ets # Flex基础语法+FlexOptions核心属性演示
- │ │ │ │ ├── FlexGrowPage.ets # flexGrow(剩余空间分配)演示
- │ │ │ │ ├── FlexShrinkPage.ets # flexShrink(空间不足压缩)演示
- │ │ │ │ ├── FlexBasisPage.ets # flexBasis(基准尺寸)演示
- │ │ │ │ ├── FlexReversePage.ets # FlexOptions方向反转(direction)演示
- │ │ │ │ └── FlexComplexPage.ets # 综合案例(搜索推荐布局)
- │ │ │ ├── resources/
- │ │ │ └── module.json5
- │ └── build-profile.json5
- └── build-profile.json5
复制代码 2.2 演示入口(Index.ets)
- import router from '@ohos.router';
- // 定义按钮数据类型
- interface ButtonItem {
- name: string, // 按钮显示文本
- url: string // 跳转页面路径(需与pages目录下的文件路径一致)
- }
- @Entry // 页面入口装饰器,标记当前组件为应用入口
- @Component // 组件装饰器,标记当前结构体为UI组件
- struct Index {
- // 按钮列表数据 - 状态变量
- @State buttonList: ButtonItem[] = [
- { name: "Flex 基础(FlexOptions)", url: "pages/FlexBasicPage" },
- { name: "flexGrow 放大", url: "pages/FlexGrowPage" },
- { name: "flexShrink 缩小", url: "pages/FlexShrinkPage" },
- { name: "flexBasis 基准尺寸", url: "pages/FlexBasisPage" },
- { name: "FlexOptions 方向反转", url: "pages/FlexReversePage" },
- { name: "Flex 综合案例(搜索推荐)", url: "pages/FlexComplexPage" }
- ];
- build() {
- // 外层Column:垂直排列所有按钮,space:20 表示子组件间距20vp
- Column({ space: 20 }) {
- // 页面标题
- Text("Flex 布局(FlexOptions)演示")
- .fontSize(28)
- .fontWeight(FontWeight.Bold)
- .fontColor($r('app.color.text_primary')) // 引用color.json中的颜色资源
- .margin({ bottom: 40 });
- // 循环渲染按钮
- ForEach(
- this.buttonList, // 数据源
- (item: ButtonItem) => { // 渲染函数:每个item生成一个按钮
- Button(item.name)
- .width('80%')
- .height(50)
- .backgroundColor($r('app.color.bg_blue_light'))
- .fontColor($r('app.color.text_white'))
- .borderRadius(8)
- .onClick(() => { // 点击事件:跳转到对应页面
- router.pushUrl({ url: item.url });
- });
- },
- (item:ButtonItem, index: number) => `${item.name}-${index}` // 唯一标识:name+索引
- );
- }
- .width('100%')
- .height('100%')
- .justifyContent(FlexAlign.Center) // 垂直居中所有子组件
- .backgroundColor($r('app.color.bg_page'));
- }
- }
复制代码 运行效果
2.3 颜色资源配置(color.json)
文件路径为 entry/src/main/resources/base/color/color.json
- {
- "color": [
- { "name": "text_primary", "value": "#333333" },
- { "name": "text_secondary", "value": "#666666" },
- { "name": "text_white", "value": "#FFFFFF" },
- { "name": "bg_page", "value": "#F5F5F5" },
- { "name": "bg_white", "value": "#FFFFFF" },
- { "name": "bg_blue_light", "value": "#1677FF" },
- { "name": "bg_blue_light_ultra", "value": "#E8F3FF" },
- { "name": "bg_red_light", "value": "#FF4D4F" },
- { "name": "bg_green_light", "value": "#52C41A" },
- { "name": "bg_gray_light", "value": "#E0E0E0" },
- { "name": "bg_gray_ultra_light", "value": "#F0F0F0" },
- { "name": "bg_purple_light", "value": "#722ED1" }
- ]
- }
复制代码 三、基础示例代码(FlexBasicPage.ets)
- import { LengthMetrics } from '@kit.ArkUI';
- @Entry
- @Component
- struct FlexBasicPage {
- // 演示子组件列表
- private rowItems: string[] = ["元素1", "元素2", "元素3", "元素4"];
- private colItems: string[] = ["元素A", "元素B", "元素C"];
- build() {
- Column({ space: 15 }) {
- // 页面标题
- Text("FlexOptions 核心属性演示")
- .fontSize(24)
- .fontWeight(FontWeight.Bold)
- .width('100%')
- .textAlign(TextAlign.Center)
- .margin({ bottom: 10 });
- // 示例1:Flex核心能力 - 横向+自动换行+双轴间距(Row无此能力)
- Text("示例1:direction=Row + wrap=Wrap + 双轴space")
- .fontSize(14)
- .fontColor($r('app.color.text_secondary'))
- .width('90%')
- .margin({ bottom: 5 });
- Flex({
- direction: FlexDirection.Row, // 主轴:横向正序
- wrap: FlexWrap.Wrap, // 核心:自动换行(Flex独有)
- alignItems: ItemAlign.Center, // 交叉轴单行居中
- alignContent: FlexAlign.Start, // 交叉轴多行起始对齐
- space: { // 双轴间距
- main: LengthMetrics.vp(10), // 主轴横向间距10vp
- cross: LengthMetrics.vp(10) // 交叉轴纵向间距10vp(仅换行生效)
- }
- }) {
- // 4个120vp宽的子组件,总宽度超容器,触发自动换行
- ForEach(this.rowItems, (item: string) => {
- Text(item)
- .width(120)
- .height(60)
- .backgroundColor($r('app.color.bg_blue_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- }, (item:string, index: number) => `${item}-${index}`)
- }
- .width('90%')
- .backgroundColor($r('app.color.bg_gray_ultra_light'))
- .padding(10)
- .borderRadius(8);
- // 示例2:Flex替代Column - 纵向+精准间距(等价Column并增强)
- Text("示例2:direction=Column + 主轴space(等价Column)")
- .fontSize(14)
- .fontColor($r('app.color.text_secondary'))
- .width('90%')
- .margin({ top: 20, bottom: 5 });
- Flex({
- direction: FlexDirection.Column, // 主轴:纵向(等价Column)
- wrap: FlexWrap.NoWrap, // 纵向无需换行
- space: { main: LengthMetrics.vp(8) } // 纵向主轴间距8vp
- }) {
- ForEach(this.colItems, (item: string) => {
- Text(item)
- .width(120)
- .height(60)
- .backgroundColor($r('app.color.bg_blue_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- }, (item:string, index: number) => `${item}-${index}`)
- }
- .width('90%')
- .height(100)
- .backgroundColor($r('app.color.bg_gray_ultra_light'))
- .padding(10)
- .borderRadius(8);
- }
- .width('100%')
- .height('100%')
- .padding(15)
- .backgroundColor($r('app.color.bg_page'));
- }
- }
复制代码 运行效果
- 示例1(横向Flex):4个元素因总宽度超容器自动换行,每行元素交叉轴居中,行与行、元素与元素间距均为10vp,完美实现流式布局;
- 示例2(纵向Flex):3个元素垂直排列,纵向间距8vp,因容器高度不足,子组件被正常截断(NoWrap生效)。
四、flexGrow:剩余空间分配
flexGrow 是 Flex 子组件专属属性,用于容器有剩余空间时,按权重分配剩余空间,仅当子组件总尺寸 < 容器主轴尺寸时生效,分配方向由 FlexOptions.direction 决定排列方向(横向分配宽度、纵向分配高度)。
4.1 核心计算逻辑
- 剩余空间 = 容器主轴尺寸 - 所有子组件初始尺寸之和 - 主轴总间距
- 总权重 = 所有设置 flexGrow 子组件的属性值之和
- 单个组件新增尺寸 = 剩余空间 × (当前组件 flexGrow / 总权重)
- 最终尺寸 = 初始尺寸 + 新增尺寸
实战计算示例
假设容器宽度 300vp(屏幕90%),主轴间距 5vp,两个子组件无初始宽度:
- 剩余空间 = 300 - 0 - 5 = 295vp
- 总权重 = 1(蓝色) + 2(红色) = 3
- 蓝色组件新增宽度 = 295 × 1/3 ≈ 98.3vp → 最终宽度 ≈ 98.3vp
- 红色组件新增宽度 = 295 × 2/3 ≈ 196.7vp → 最终宽度 ≈ 196.7vp
4.2 示例代码(FlexGrowPage.ets)
- import { LengthMetrics } from '@kit.ArkUI';
- @Entry
- @Component
- struct FlexGrowPage {
- build() {
- Column({ space: 15 }) {
- Text("flexGrow 剩余空间按权重分配")
- .fontSize(24)
- .fontWeight(FontWeight.Bold)
- .width('100%')
- .textAlign(TextAlign.Center)
- .margin({ bottom: 10 });
- Text("权重1:2 → 蓝色占1/3,红色占2/3(无初始宽度)")
- .fontSize(12)
- .fontColor($r('app.color.text_secondary'))
- .width('90%')
- .textAlign(TextAlign.Center);
-
- // Flex基础配置 + flexGrow实现空间分配
- Flex({
- direction: FlexDirection.Row, // 横向布局
- space: { main: LengthMetrics.vp(5) } // 主轴间距5vp
- }) {
- Text("flexGrow: 1")
- .flexGrow(1) // 权重1:占剩余空间1/3
- .height(60)
- .backgroundColor($r('app.color.bg_blue_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- Text("flexGrow: 2")
- .flexGrow(2) // 权重2:占剩余空间2/3
- .height(60)
- .backgroundColor($r('app.color.bg_red_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- }
- .width('90%')
- .backgroundColor($r('app.color.bg_gray_ultra_light'))
- .padding(10)
- .borderRadius(8);
- }
- .width('100%')
- .height('100%')
- .padding(15)
- .backgroundColor($r('app.color.bg_page'));
- }
- }
复制代码 运行效果
父容器宽度为屏幕90%,两个子组件无初始宽度,剩余空间按1:2权重分配,视觉上蓝色组件占约1/3宽度,红色组件占约2/3宽度,完美实现空间均分/按比例分配。
关键注意点
- flexGrow 默认为0,即不参与剩余空间分配;
- 仅当子组件总尺寸 < 容器尺寸时生效,若子组件总尺寸超出容器,需配合 wrap 或 flexShrink;
- 纵向布局(direction=Column)时,flexGrow 分配的是容器剩余高度。
五、flexShrink:空间不足压缩(Flex子组件核心属性)
flexShrink 是 Flex 子组件专属属性,用于容器空间不足时,按规则压缩子组件尺寸,必须配合 FlexOptions 的 wrap: NoWrap 才生效(若 wrap: Wrap,子组件会自动换行,不会触发压缩)。
5.1 核心计算逻辑(与flexGrow不同,需结合初始尺寸)
- 超出空间 = 所有子组件初始尺寸之和 + 子组件间总间距 - 容器主轴尺寸
- 总权重 = ∑(子组件 flexShrink × 子组件 flexBasis(基准尺寸))(flexBasis 为 auto 时等价初始尺寸)
- 单个组件压缩尺寸 = 超出空间 × (当前组件 flexShrink × 子组件 flexBasis(基准尺寸) / 总权重)
- 最终尺寸 = 初始尺寸 - 压缩尺寸
实战计算示例
容器宽度 300vp,主轴间距 5vp,两个子组件初始宽度均为200vp,wrap: NoWrap:
- 超出空间 = (200+200) + 5 - 300 = 105vp
- 总权重 = (1×200)「蓝色」 + (0×200)「红色」 = 200
- 蓝色组件压缩尺寸 = 105 × (1×200/200) = 105vp → 最终宽度 95vp
- 红色组件压缩尺寸 = 105 × (0×200/200) = 0 → 最终宽度 200vp
5.2 示例代码(FlexShrinkPage.ets)
- import { LengthMetrics } from '@kit.ArkUI';
- @Entry
- @Component
- struct FlexShrinkPage {
- build() {
- Column({ space: 15 }) {
- Text("flexShrink 空间不足时压缩(依赖wrap: NoWrap)")
- .fontSize(24)
- .fontWeight(FontWeight.Bold)
- .width('100%')
- .textAlign(TextAlign.Center)
- .margin({ bottom: 10 });
- // 核心配置:wrap: NoWrap 才会触发压缩,Wrap则换行
- Flex({
- direction: FlexDirection.Row,
- wrap: FlexWrap.NoWrap, // 必须关闭换行!否则压缩失效
- space: { main: LengthMetrics.vp(5) }
- }) {
- Text("flexShrink: 1")
- .width(200)
- .flexShrink(1) // 参与压缩:承担全部压缩量
- .height(60)
- .backgroundColor($r('app.color.bg_blue_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- Text("flexShrink: 0")
- .width(200)
- .flexShrink(0) // 不压缩:保留原始200vp宽度
- .height(60)
- .backgroundColor($r('app.color.bg_red_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- }
- .width('90%')
- .backgroundColor($r('app.color.bg_gray_ultra_light'))
- .padding(10)
- .borderRadius(8);
- Text("flexShrink:0 保留原尺寸,flexShrink:1 承担全部压缩(修改wrap为Wrap则换行)")
- .fontSize(12)
- .fontColor($r('app.color.text_secondary'))
- .width('90%')
- .textAlign(TextAlign.Center);
- }
- .width('100%')
- .height('100%')
- .padding(15)
- .backgroundColor($r('app.color.bg_page'));
- }
- }
复制代码 运行效果
- 红色组件(flexShrink:0)完全不压缩,保留200vp原始宽度,几乎占满整个容器;
- 蓝色组件(flexShrink:1)承担全部压缩量,被压缩至约95vp,仅占剩余空间;
- 若将 wrap 改为 Wrap,两个组件会自动换行,压缩效果完全消失,恢复200vp原始宽度。
关键注意点
- flexShrink 默认为1,即所有子组件默认参与压缩,按「flexShrink×初始尺寸」比例承担压缩量;
- 设置 flexShrink: 0 可让子组件完全不压缩,保留原始尺寸(实战常用:固定尺寸组件不被压缩);
- 压缩仅在 wrap: NoWrap 时生效,这是Flex与Row的核心共性(Column、Row无换行能力,默认压缩)。
六、flexBasis:基准尺寸
flexBasis 设置组件在父容器(Flex/Row/Column)主轴方向的初始尺寸基准,是 flexGrow/flexShrink 计算剩余空间/压缩空间的 唯一基准 ,优先级高于同方向的 width/height,生效维度由 direction 决定(横向控宽、纵向控高)。换行(wrap: Wrap)时 flexBasis 仍为初始基准,系统优先换行而非压缩,最终尺寸由 flexBasis、内容尺寸、行剩余空间共同调整。
6.1 仅支持2种取值(无百分比)
取值类型说明示例生效维度(direction决定)数值固定基准尺寸(单位:vp),优先级最高flexBasis(100)Row→宽度;Column→heightauto由子组件width/height决定初始基准尺寸(默认值)flexBasis('auto')Row→width;Column→height重要:flexBasis 不支持百分比字符串(如flexBasis('50%')),如需百分比尺寸,直接使用width/height即可,flexBasis会自动识别为auto并沿用width/height值。
6.2 核心特性
- 优先级:flexBasis(数值)> width/height > flexBasis('auto');
- 基准作用:flexGrow/flexShrink仅基于flexBasis的结果计算,与原始width/height无关;
- 维度匹配:始终与主轴方向匹配,横向布局控制宽度,纵向布局控制高度,无需手动调整。
6.3 示例代码(FlexBasisPage.ets)
运行效果
- 场景1:蓝色组件宽度固定150vp(flexBasis(150)覆盖width(200),优先级更高);
- 场景2:红色组件宽度100vp(flexBasis('auto'),沿用width的100vp作为基准);
- 场景3:绿色组件宽度90vp(flexBasis('50%')无效,自动转为auto,沿用width(90));
- 场景4:紫色组件以100vp为基准占满剩余空间,width(300)被覆盖;
- 实战场景:按钮固定60vp宽度,右侧元素弹性占满剩余空间。
七、方向反转
direction 是 FlexOptions 最核心的属性之一,支持主轴方向正序/反转,是实现逆向UI布局的核心方案,无需手动调整子组件编写顺序,仅修改一个属性即可实现方向切换。
7.1 direction 完整取值与适用场景
取值主轴方向子组件排列顺序核心适用场景Row(默认)横向从左到右常规横向布局RowReverse横向从右到左逆向横向布局(如右对齐操作栏)Column纵向从上到下常规纵向布局(等价Column)ColumnReverse纵向从下到上底部弹出列表、逆向滚动布局、倒序排列7.2 示例代码(FlexReversePage.ets)
- import { LengthMetrics } from '@kit.ArkUI';
- @Entry
- @Component
- struct FlexReversePage {
- build() {
- Column({ space: 20 }) {
- Text("FlexOptions.direction 方向反转演示")
- .fontSize(24)
- .fontWeight(FontWeight.Bold)
- .width('100%')
- .textAlign(TextAlign.Center)
- .margin({ bottom: 10 });
- // 示例1:横向反转 - RowReverse
- Text("示例1:direction = RowReverse(横向从右到左)")
- .fontSize(14)
- .fontColor($r('app.color.text_secondary'))
- .width('90%');
- Flex({
- direction: FlexDirection.RowReverse, // 核心:横向反转
- space: { main: LengthMetrics.vp(10) } // 间距规则不变,沿反转后主轴生效
- }) {
- Text("元素1")
- .width(80)
- .height(60)
- .backgroundColor($r('app.color.bg_blue_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- Text("元素2")
- .width(80)
- .height(60)
- .backgroundColor($r('app.color.bg_red_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- Text("元素3")
- .width(80)
- .height(60)
- .backgroundColor($r('app.color.bg_green_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- }
- .width('90%')
- .backgroundColor($r('app.color.bg_gray_ultra_light'))
- .padding(10)
- .borderRadius(8);
- // 示例2:纵向反转 - ColumnReverse(底部起始布局)
- Text("示例2:direction = ColumnReverse(纵向从下到上)")
- .fontSize(14)
- .fontColor($r('app.color.text_secondary'))
- .width('90%')
- .margin({ top: 20 });
- Flex({
- direction: FlexDirection.ColumnReverse, // 核心:纵向反转
- space: { main: LengthMetrics.vp(8) } // 纵向间距沿反转后主轴生效
- }) {
- Text("元素A")
- .height(40)
- .backgroundColor($r('app.color.bg_blue_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- Text("元素B")
- .height(40)
- .backgroundColor($r('app.color.bg_red_light'))
- .textAlign(TextAlign.Center)
- .fontColor($r('app.color.text_white'))
- .borderRadius(4);
- }
- .width('90%')
- .height(120)
- .backgroundColor($r('app.color.bg_gray_ultra_light'))
- .padding(10)
- .borderRadius(8);
- }
- .width('100%')
- .height('100%')
- .padding(15)
- .backgroundColor($r('app.color.bg_page'));
- }
- }
复制代码 7.3 运行效果
- 横向反转区域:子组件显示顺序为「元素3 → 元素2 → 元素1」,从容器右侧向左侧排列,主轴间距10vp正常生效,无需调整子组件编写顺序;
- 纵向反转区域:子组件显示顺序为「元素B → 元素A」,从容器底部向顶部排列,在120vp高的容器内从底部开始排布,纵向间距8vp正常生效。
7.4 方向反转关键注意点
- 反转仅改变子组件排列顺序和主轴起始方向,space 间距、justifyContent/alignItems 对齐逻辑会自动适配(Start/End 随反转方向动态变化,无需手动修改);
- 反转布局的性能损耗极低,仅为布局顺序调整,无额外渲染开销;
- 无需手动调整子组件的编写顺序,仅通过修改 direction 即可实现正序/反转切换,符合“一次编写,多端适配”原则;
- 反转后,flexGrow/flexShrink/flexBasis 的计算逻辑不变,仍基于反转后的主轴方向生效。
八、Flex 综合实战案例:移动端搜索推荐布局
8.1 需求说明(典型移动端业务布局)
实现“顶部搜索栏 + 中间流式推荐标签”的核心布局,要求:
- 顶部搜索栏:固定顶部,左右留间距,输入框占满剩余宽度,搜索按钮固定尺寸;
- 中间标签区:支持流式自动换行(核心,Row无法实现),标签横向/纵向间距均匀,占满页面中间剩余高度;
- 整体适配:使用Flex独有能力实现,无冗余布局嵌套。
8.2 示例代码(FlexComplexPage.ets)
- import { LengthMetrics } from '@kit.ArkUI';
- import router from '@ohos.router';
- @Entry
- @Component
- struct FlexComplexPage {
- // 1. 定义标签数据源
- // 猜你想搜标签数组
- private guessTags: string[] = [
- "鸿蒙开发", "Flex布局", "ArkTS教程",
- "自适应布局", "多设备适配", "Stack布局"
- ];
- // 历史搜索标签数组
- private historyTags: string[] = ["弹性布局", "鸿蒙组件"];
- build() {
- Column() {
- // 顶部搜索栏
- Row() {
- SymbolGlyph($r('sys.symbol.chevron_left_circle'))
- .fontSize(30)
- .fontColor([$r('app.color.text_primary')])
- .onClick(() => {
- router.back()
- });
- Blank();
- Search({ placeholder: "输入搜索关键词" })
- .backgroundColor($r('app.color.bg_gray_ultra_light'))
- .placeholderColor($r('app.color.text_primary'))
- .constraintSize({ minWidth: 200, maxWidth: 400 })
- .width('calc(100% - 120vp)')
- Button("搜索")
- .height(32)
- .backgroundColor($r('app.color.bg_red_light'))
- .fontColor($r('app.color.text_white'))
- .borderRadius(16)
- .margin({ left: 15 })
- .fontSize(14);
- }
- .justifyContent(FlexAlign.SpaceBetween)
- .width('100%')
- .height(50)
- .backgroundColor($r('app.color.bg_white'))
- .padding({ left: 15, right: 15 })
- .alignItems(VerticalAlign.Center);
- // 中间内容区(可滚动)
- Scroll() {
- Column({space:15}) {
- // 猜你想搜区域
- Text("猜你想搜")
- .fontSize(16)
- .fontWeight(FontWeight.Medium)
- .fontColor($r('app.color.text_primary'));
- // 流式标签:Flex + ForEach 动态渲染
- Flex({
- wrap: FlexWrap.Wrap, // 自动换行(核心)
- space: { // 双轴间距
- main: LengthMetrics.vp(10), // 主轴(横向)间距
- cross: LengthMetrics.vp(8) // 交叉轴(纵向)间距
- }
- }) {
- ForEach(
- this.guessTags,
- (tag: string) => {
- Text(tag)
- .padding({ left: 12, right: 12, top: 6, bottom: 6 })
- .backgroundColor($r('app.color.bg_gray_ultra_light'))
- .fontColor($r('app.color.text_primary'))
- .borderRadius(12)
- .fontSize(14);
- },
- (tag: string, index: number) => `${tag}-${index}`
- )
- }
- .width('100%');
- // 历史搜索区域标题栏
- Row() {
- Text("历史搜索")
- .fontSize(16)
- .fontWeight(FontWeight.Medium)
- .fontColor($r('app.color.text_primary'));
- Button("清空历史")
- .width(80)
- .height(28)
- .backgroundColor($r('app.color.bg_gray_light'))
- .fontColor($r('app.color.text_primary'))
- .borderRadius(6)
- .fontSize(12)
- .margin({ left: 10 });
- }
- .width('100%')
- .justifyContent(FlexAlign.SpaceBetween);
- // 历史搜索流式标签
- Flex({
- wrap: FlexWrap.Wrap, // 自动换行
- space: {
- main: LengthMetrics.vp(10),
- cross: LengthMetrics.vp(8)
- }
- }) {
- ForEach(
- this.historyTags,
- (tag: string) => {
- Text(tag)
- .padding({ left: 12, right: 12, top: 6, bottom: 6 })
- .backgroundColor($r('app.color.bg_blue_light_ultra'))
- .fontColor($r('app.color.text_primary'))
- .borderRadius(12)
- .fontSize(14);
- },
- (tag: string, index: number) => `${tag}-${index}`
- )
- }
- .width('100%');
- }
- .width('100%')
- .height('100%')
- .padding(15)
- .alignItems(HorizontalAlign.Start);
- }
- .layoutWeight(1) // 占满剩余空间
- .width('100%')
- .backgroundColor($r('app.color.bg_white'));
- }
- .width('100%')
- .height('100%')
- .alignItems(HorizontalAlign.Start);
- }
- }
复制代码 8.3 运行效果
九、核心总结
- Flex 布局的核心价值是支持自动换行(wrap:Wrap)和方向反转(RowReverse/ColumnReverse),这是Row/Column无法实现的关键能力;
- Flex 子组件三大核心属性:flexGrow(剩余空间按权重分配)、flexShrink(空间不足按规则压缩,需wrap:NoWrap)、flexBasis(基准尺寸,优先级>width/height,仅支持数值/auto);
- FlexOptions 核心配置:direction 控制主轴方向/反转,wrap 控制换行规则,space实现主轴/交叉轴双轴精准间距,alignContent 实现交叉轴多行对齐;
- 布局选型原则:简单单行/单列用Row/Column(轻量高性能),复杂流式/反转/多行布局用Flex(功能强大),性能敏感场景优先Row/Column;
十、代码仓库
- 工程名称:FlexApplication
- 仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
十一、下节预告
本节我们系统掌握了 Flex 布局的核心配置、三大弹性属性(flexGrow/flexShrink/flexBasis)与实战用法,实现了流式标签复杂布局。下一节,我们将学习鸿蒙 ArkTS 中实现Z轴层叠排列的核心布局——Stack 叠层布局,重点掌握:
- Stack 布局的核心定位与适用场景,理解其与 Flex/Row/Column 的核心区别;
- Alignment 全局对齐、zIndex 层级控制、offset 相对偏移、position 绝对定位四大核心能力;
- 实战落地移动端高频场景:App 图标消息角标、商品卡片层叠遮罩与悬浮标签,掌握层叠布局的精准定位与交互适配技巧。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |