鸿蒙原生 ArkTS 布局方式之 Flex 默认行为子组件的拉伸与收缩深度解析一、引言HarmonyOS NEXT鸿蒙星河版自诞生以来以其全栈自研的底座和原生纯净的生态吸引了越来越多开发者的关注。在鸿蒙应用开发中UI 布局是最基础也最核心的能力之一。ArkTSArk TypeScript作为鸿蒙原生应用开发的推荐语言结合 ArkUI 声明式框架提供了一套强大而灵活的布局系统。在众多布局组件中Flex无疑是最常用、最灵活的一种。它借鉴了 CSS Flexbox 的设计理念但在语法和使用方式上又带有鲜明的鸿蒙特色。很多初学者在刚接触 Flex 时往往会被子组件的拉伸和收缩行为所困惑——为什么有些子组件会自动填满空间为什么有些会超出容器后自动压缩这些行为的背后其实是 Flex 布局的默认参数在起作用。本文将以一个完整的 ArkTS 示例应用为载体深入剖析 Flex 布局的三大默认行为ItemAlign.Stretch拉伸、flexShrink收缩和flexGrow增长。通过逐行解读代码和运行效果分析带你彻底掌握 Flex 布局的精髓。本文示例代码基于 HarmonyOS NEXT API 24ArkTS 声明式范式编写可在 DevEco Studio 5.0 中直接编译运行。二、HarmonyOS NEXT 与 ArkTS 布局体系概览2.1 什么是 ArkTSArkTS 是鸿蒙生态原生的应用开发语言它在 TypeScript 的基础上进行了扩展和约束专为鸿蒙 ArkUI 框架设计。与标准 TypeScript 相比ArkTS 具有以下特点强类型 静态检查所有类型在编译期确定减少运行时错误声明式 UI通过Component和Builder装饰器描述界面数据驱动视图更新响应式状态管理State、Prop、Link等装饰器让状态变化自动反映到 UI安全并发不支持any类型限制了部分动态特性换来更高的运行效率2.2 鸿蒙布局体系的核心组件ArkUI 提供了丰富的布局组件大致可分为三类类别组件适用场景线性布局Flex、Row、Column一维排列最常用层叠布局Stack子组件重叠放置相对布局RelativeContainer子组件间、子组件与容器间的相对定位弹性布局Flex加强版支持 grow / shrink / basis 弹性伸缩其中Flex是最全能的一维布局组件。Row和Column实际上是 Flex 的特例——Row等价于Flex({ direction: FlexDirection.Row })Column等价于Flex({ direction: FlexDirection.Column })。2.3 Flex 布局的核心概念要理解 Flex我们必须先掌握两个轴的概念主轴Main Axis布局排列的方向。FlexDirection.Row时主轴为水平方向从左到右FlexDirection.Column时主轴为垂直方向从上到下。交叉轴Cross Axis与主轴垂直的方向。Row 时交叉轴为垂直方向Column 时交叉轴为水平方向。Flex 布局的所有行为——拉伸、收缩、对齐、间距——都是围绕这两个轴展开的。三、演示①默认拉伸ItemAlign.Stretch3.1 现象与原理运行应用的第一屏你会看到三个彩色方块并排排列它们的高度都被拉伸到了容器的高度。这就是 Flex 的第一个默认行为。Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Stretch }) { Text(子A\n拉伸) .backgroundColor(#FF6B6B) .width(80) // 没有设置 height Text(子B\n拉伸) .backgroundColor(#4ECDC4) .layoutWeight(1) // 没有设置 height Text(子C\n拉伸) .backgroundColor(#45B7D1) .layoutWeight(1) // 没有设置 height } .width(100%) .height(100)关键点在于alignItems: ItemAlign.Stretch——这是 Flex 的默认值也就是说即使你不写alignItems效果也是一样的。Stretch的含义是在交叉轴方向上将子组件拉伸至与容器在该方向上的尺寸一致。在本例中主轴水平方向Row交叉轴垂直方向容器高度100px子组件未设 height→ 沿垂直方向被拉伸至 100px3.2 什么时候会被拉伸子组件被拉伸需要满足一个关键条件在交叉轴方向上没有设置固定尺寸。换句话说height 不设置→ 在 Row 方向 Flex 中被拉伸沿垂直方向width 不设置→ 在 Column 方向 Flex 中被拉伸沿水平方向如果子组件设置了固定高度拉伸就不会生效。这也是演示①中第二组示例所展示的Text(子D\n固定\n40px) .height(40) // 固定高度不被拉伸 Text(子F\n(拉伸)) // 不设 height被拉伸至 100px运行结果中子D 只有 40px 高子F 则充满整个容器——两者形成鲜明对比。3.3 layoutWeight 的特殊作用细心的读者可能会注意到示例中使用了.layoutWeight(1)。这是 ArkUI 特有的属性与 CSS Flexbox 中的flex属性类似但作用机制略有不同。layoutWeight让子组件按权重分配主轴上的剩余空间。当容器宽度超出子组件内容宽度时设置了layoutWeight的子组件会等比例放大。在本演示中layoutWeight(1)让子B 和子C 均分 A 之外的空间形成三栏等宽近似的效果。值得注意的是layoutWeight和flexGrow都可以实现空间分配但layoutWeight的优先级更高且不依赖于容器的Flex类型——它在Row、Column中同样有效。3.4 实际开发中的意义ItemAlign.Stretch在日常开发中应用极为广泛列表项让每个列表项在行方向等高导航栏按钮自动填满导航栏高度卡片布局卡片内容区域等宽或等高掌握 Stretch 的触发条件不设交叉轴尺寸可以避免很多布局对不齐的困扰。四、演示②收缩行为flexShrink4.1 现象与原理切换到第二个演示场景你会看到三个子组件被放置在一个宽度只有 300px的容器中而每个子组件的宽度被设为120px总宽 360px。超出的部分没有被截断或换行而是自动收缩了。这就是 Flex 的第二个默认行为flexShrink的默认值为1。Flex({ direction: FlexDirection.Row }) { Text(子A\n收缩1).width(120) // flexShrink 默认1 Text(子B\n收缩2).width(120).flexShrink(2) // 收缩比是 A/C 的两倍 Text(子C\n收缩1).width(120) // flexShrink 默认1 } .width(300)4.2 收缩值的计算方式当子组件总宽度超出容器时多出的宽度会按flexShrink 值的比例从各子组件中扣减。计算公式如下总溢出宽度 子组件原始宽度之和 - 容器宽度 360px - 300px 60px 收缩比例因子 子组件的 flexShrink 值 / 所有子组件 flexShrink 总和 各子组件的 flexShrink / (1 2 1) 子A 收缩量 60px × (1/4) 15px → 最终宽度 120px - 15px 105px 子B 收缩量 60px × (2/4) 30px → 最终宽度 120px - 30px 90px 子C 收缩量 60px × (1/4) 15px → 最终宽度 120px - 15px 105px可以看到子B 的 flexShrink2收缩量是 A/C 的两倍所以它被压缩得最多90px。这个比例关系在实际运行效果中可以清晰地观察到。4.3 禁止收缩flexShrink 0在某些场景下我们不希望子组件被压缩。例如一个图标按钮的宽度必须保持固定不能被旁边的文字挤小。这时可以将flexShrink设为 0Text(关键按钮).width(80).flexShrink(0)设为 0 后该子组件将始终维持其设定的宽度超出部分由其他允许收缩的子组件承担。4.4 实际开发中的意义flexShrink的默认行为1确保了 Flex 布局在空间不足时不会发生内容溢出或布局断裂。这在以下场景中尤为重要响应式布局不同屏幕宽度下子组件自动适应动态内容后端返回的长文本不会撑破容器边界多语言适配不同语言的文字长度差异很大flexShrink 保证了布局稳定性五、演示③增长行为flexGrow5.1 现象与原理第三个演示场景展示了 flexGrow 的效果子A 固定 80px 宽度子B 和 子C 分别设置了flexGrow(1)和flexGrow(2)。容器宽度设为 100%所以存在大量剩余空间——这些空间被 B 和 C 按 1:2 的比例分配了。Flex({ direction: FlexDirection.Row }) { Text(A\n固定\n80px) .width(80) .flexGrow(0) // 默认值不增长 Text(B\ngrow1) .flexGrow(1) // 占剩余空间的 1/3 Text(C\ngrow2) .flexGrow(2) // 占剩余空间的 2/3 } .width(100%)5.2 增长值的计算方式flexGrow 的计算与 flexShrink 类似但方向相反剩余宽度 容器宽度 - 子组件原始宽度之和 360px假设宽屏 - 80pxA的宽度 280px 分配比例 各子组件的 flexGrow / flexGrow 总和 B 获得 280px × (1/3) ≈ 93px → 最终宽度 ≈ 93px C 获得 280px × (2/3) ≈ 187px → 最终宽度 ≈ 187pxB 的宽度是 C 的一半与代码中flexGrow(1)和flexGrow(2)的比值完全对应。5.3 flexGrow 与 layoutWeight 的区别特性flexGrowlayoutWeight适用范围仅 Flex 容器Row、Column、Flex 均可默认值0不增长未设置计算基准内容宽度 剩余空间严格按权重分配与内容的关系保留内容空间后分配剩余直接按权重分配总空间简单来说flexGrow更接近 CSS Flexbox 的行为保留子组件的基础宽度后再分配剩余空间而layoutWeight则更加霸道直接按比例重新分配总宽度。5.4 实际开发中的意义flexGrow 是实现自适应布局的核心手段搜索栏输入框 flexGrow1 填满剩余空间搜索按钮固定宽度表格头各列按权重分配宽度弹窗内容内容区填满弹窗扣除标题和按钮后的空间六、完整代码逐段解析为了让读者更好地理解整个应用的架构这里给出完整的代码结构分析6.1 整体框架Entry Component struct Index { build() { Column() { // ... 标题 ... // ... 演示①默认拉伸 ... // ... 演示②收缩行为 ... // ... 演示③增长行为 ... } .width(100%) .height(100%) .backgroundColor(#F5F5F5) } }Entry标记该组件为应用的入口页面Component声明这是一个 ArkTS 组件struct Index采用结构体struct而非类class定义这是 ArkTS 的推荐方式build()描述 UI 结构的核心方法不允许有副作用6.2 色彩搭配示例中使用了四组颜色分别对应不同的语义颜色色值含义珊瑚红#FF6B6B第一个子组件碧蓝绿#4ECDC4第二个子组件天蓝#45B7D1第三个子组件暖黄#FDCB6E特殊的固定组件这些色彩来自扁平化配色方案在深色文字背景下具有较高的辨识度。6.3 布局层次整个页面的布局层次如下Column根容器100% × 100%灰底 ├── Text标题Flex 布局子组件拉伸与收缩 ├── Text演示①标题 ├── Text演示①说明 ├── Flex演示①容器300px × 100px │ ├── Text子A红底80px 宽无高度→拉伸 │ ├── Text子B绿底layoutWeight1无高度→拉伸 │ └── Text子C蓝底layoutWeight1无高度→拉伸 ├── Text演示②标题 ├── Text演示②说明 ├── Flex演示②容器300px × 100px │ ├── Text子A120pxflexShrink1 │ ├── Text子B120pxflexShrink2 │ └── Text子C120pxflexShrink1 ├── Text演示③标题 ├── Text演示③说明 └── Flex演示③容器100% × 100px ├── TextA80px 固定flexGrow0 ├── TextBflexGrow1 └── TextCflexGrow2根节点使用Column三个演示场景纵向排列每个场景内部的子组件通过Flex横向排列。这种嵌套结构是 ArkUI 布局的典型写法。6.4 关于 .ets 文件.ets是 ArkTS 源文件的扩展名Extension TypeScript。在 HarmonyOS NEXT 项目中页面文件通常放在entry/src/main/ets/pages/目录下通过Entry装饰器标注为可路由的页面。七、flexGrow、flexShrink 与 Stretch 的协同工作在实际项目中这三种行为很少单独出现它们通常是同时作用的。理解它们的协同关系才能真正掌握 Flex 布局。7.1 同时生效的场景假设一个 Flex 容器宽度为 400px高度为 120px包含三个子组件子A宽度 150px高度不设flexGrow1flexShrink1 子B宽度 100px高度不设flexGrow0flexShrink2 子C宽度 200px高度不设flexGrow2flexShrink1分析各子组件的最终尺寸拉伸alignItemsStretch默认三个子组件的高度都被拉伸至 120px收缩总宽度 450px 400px超出 50px。flexShrink 总和 1214子A 收缩 50×(1/4)12.5px → 137.5px子B 收缩 50×(2/4)25px → 75px子C 收缩 50×(1/4)12.5px → 187.5px增长此例中总宽超出不留剩余空间故 flexGrow 不生效Flex 布局的处理优先级是先确定基础尺寸 → 收缩至容器内如需要→ 分配剩余空间如有剩余。收缩和增长不会同时发生——收缩只在空间不足时有效增长只在空间有余时有效。7.2 Column 方向下的表现前面的示例都是在FlexDirection.Row水平排列下进行的。如果改为FlexDirection.Column垂直排列轴的角色会互换主轴垂直方向交叉轴水平方向Stretch水平拉伸子组件至容器宽度flexShrink / flexGrow在垂直方向上收缩/增长综合场景中展示了一段 Column 方向的 FlexFlex({ direction: FlexDirection.Column, alignItems: ItemAlign.Stretch }) { Text(固定高 50px).height(50) // 固定高度不被拉伸 Text(增长1).flexGrow(1) // 在垂直方向增长 Text(增长2).flexGrow(2) // 在垂直方向增长两倍 } .width(100%) .height(180)这里子组件在水平方向上被拉伸填满容器宽度同时在垂直方向上按 flexGrow 比例分配剩余高度。这种交叉轴拉伸 主轴弹性的组合是实现自适应布局的常用手法。八、Flex 布局常见陷阱与避坑指南8.1 陷阱一显式设置交叉轴尺寸导致拉伸失效// ❌ 错误设了 height不会被拉伸 Text(子组件).height(50) // ✅ 正确不设 height或者使用 .height(auto) Text(子组件)在Row方向的 Flex 中height(auto)表示自动计算高度在 Stretch 作用下会被覆盖为容器高度。而height(50)表示固定 50pxStretch 不会覆盖固定值。8.2 陷阱二flexShrink 0 但依然溢出Text(很长很长的文字).width(300).flexShrink(0)如果 Text 内容本身有最小宽度限制比如连续无断点的英文字符串即使设置了flexShrink(0)也可能无法压缩到理想的宽度。这时需要配合textOverflow和maxLines属性来实现文字截断。8.3 陷阱三flexGrow 与 layoutWeight 混用两者都用于空间分配但计算方式不同。混用时可能导致意料之外的宽度分配。建议在同一个 Flex 容器中只使用其中一种。8.4 陷阱四忽视容器的尺寸约束Flex 的弹性行为是以容器的尺寸为基准的。如果容器的父级没有明确的尺寸约束比如父级也是auto高度Flex 容器的尺寸可能不符合预期。建议始终为 Flex 容器设置明确的width和height或者使用.width(100%)和.layoutWeight(1)让容器在父级中占据确定的空间。九、进阶使用 Flex 构建真实页面掌握了 Flex 的三大默认行为后我们可以用它构建各种常见的 UI 布局。下面是一个简单的示范9.1 自适应搜索栏Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) { TextInput({ placeholder: 搜索... }) .flexShrink(1) // 空间不足时收缩 .layoutWeight(1) // 优先填满剩余空间 Button(搜索) .width(64) .flexShrink(0) // 固定宽度绝不收缩 } .width(100%) .height(40)9.2 等分布局三栏Flex({ direction: FlexDirection.Row }) { Text(左侧).layoutWeight(1) Text(中间).layoutWeight(1) Text(右侧).layoutWeight(1) } .width(100%)9.3 底部固定 内容自适应Column() { Flex({ direction: FlexDirection.Column }) { // 主内容区增长填满剩余空间 Text(内容区域).flexGrow(1).layoutWeight(1) // 底部按钮固定高度 Button(确认).height(48).flexShrink(0) } .width(100%) .height(100%) } .width(100%) .height(100%)十、总结本文通过一个完整的 ArkTS 示例应用深入剖析了 Flex 布局的三大默认行为默认行为属性默认值作用条件拉伸alignItemsItemAlign.Stretch子组件未设交叉轴尺寸收缩flexShrink1子组件总尺寸超出容器增长flexGrow0子组件总尺寸小于容器这三个行为共同构成了 Flex 布局的弹性基础。理解它们就等于掌握了 ArkUI 布局最核心的能力。在实际开发中我们往往不需要显式设置这些属性——Flex 的默认值已经覆盖了大部分常见场景的需求。这就是为什么我们说Flex 默认行为如此重要用最少的代码实现最自然的弹性效果。最后给读者几点建议先理解主轴和交叉轴的关系再学习具体属性区分 Stretch沿交叉轴拉伸和 flexGrow沿主轴增长两者作用方向不同多用.layoutWeight(1)实现水平等分它比 flexGrow 更直观在真机或模拟器中调试布局勾选 DevEco Studio 的 Inspector 面板查看布局线框Flex 布局的弹性哲学本质上是让 UI 去适应容器而非让容器去适应 UI。这种约束自适配的思想与鸿蒙万物互联、多端协同的理念不谋而合。掌握 Flex你就掌握了鸿蒙 UI 开发的半壁江山。本文配套完整示例代码可在鸿蒙项目entry/src/main/ets/pages/Index.ets中查看基于 HarmonyOS NEXT API 24 开发。作者AtomCodedeepseek-v4-flash日期2025年