文章目录
- 前言
- 先搞懂一件事:什么是"声明式"
- 代码实现
- Column:竖着排,从上到下
- 核心代码
- Row:横着排,从左到右
- 完整示例
- 重点来了:嵌套组合
- 代码解析
- 小白问答
- 常见误区(小白必踩)
- 完整可运行示例:写一个登录页
- 实现拆解
- 写在最后
前言
我刚转到 HarmonyOS 那会儿,最不习惯的就是它的 UI 写法。以前写界面是一行一行"命令"系统"把这个按钮放到那里",到了 ArkUI 这边,你改成"描述"界面长什么样——剩下的交给框架去算。
这篇文章我就用最朴素的大白话,把Column和Row这两个最基础、也最常用的布局容器讲透。读完你就能自己摆出 90% 的静态页面。不需要任何前置基础,跟着敲一遍就行。
小建议:下面每一段代码都建议你在 DevEco Studio 里新建一个 Page 实测,光看是记不住的。
先搞懂一件事:什么是"声明式"
说白了,声明式就是你描述结果,框架负责过程。
比如你想让两个文字上下排列。命令式思维是"先画第一个,y 坐标加 20,再画第二个"。声明式思维是"我这里有一个竖着排的容器,里面放两个文字"——至于它们具体坐标是多少,你不用管。
代码实现
Column(){Text('第一行')Text('第二行')}这段代码的含义是:建一个竖向容器,里面依次放两个文本。运行出来,两个文本自然就是上下的。就这么简单。
Column:竖着排,从上到下
Column是纵向布局容器,子组件按书写顺序从上往下依次堆叠。它有三个最常用、也最容易混淆的属性,我一个一个说。
核心代码
Column({space:12}){Text('手机号').fontSize(16)Text('密码').fontSize(16)Button('登录').width('100%')}.width('100%').alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.Center)逐行拆解一下:
- 第 1 行
Column({ space: 12 }):space是子组件之间的间距,这里设 12vp,相当于每个元素之间自动留 12 像素的空隙,不用你手动加 margin。 - 第 2~4 行:里面依次放"手机号""密码"两个文本和一个登录按钮。注意 ArkUI 里子组件就直接写在
Column() { ... }的大括号里,没有额外的标签。 - 第 5 行
.width('100%'):让这个 Column 占满父容器宽度。 - 第 6 行
.alignItems(HorizontalAlign.Start):控制水平方向的对齐。Start表示靠左。如果你写成Center就是水平居中。 - 第 7 行
.justifyContent(FlexAlign.Center):控制垂直方向的对齐。Center表示在竖向空间里居中。
这里有个新手必踩的点:
alignItems管的是"交叉轴",justifyContent管的是"主轴"。对 Column 来说,主轴是竖的,交叉轴是横的。记住这句就够了。
Row:横着排,从左到右
Row和Column是一对,区别只是方向:Row 是从左到右横着排。
完整示例
Row({space:8}){Image($r('app.media.icon')).width(24).height(24)Text('我的订单').fontSize(16)Blank()// 弹簧,把后面的内容顶到最右Text('>').fontColor('#999999')}.width('100%').padding(12)几个关键点:
- 第 1 行
Row({ space: 8 }):子组件之间水平间距 8vp。 - 第 3 行
Blank()是个占位弹簧,它会自动把剩余空间吃掉,于是后面的>就被挤到了最右边。这是做"左标题右箭头"布局的标配技巧。 - 第 5 行
.padding(12):给整个 Row 内边距 12vp,让内容不贴边。
重点来了:嵌套组合
真实页面几乎都是 Column 和 Row 互相套。比如一个常见的列表项:左边图标+标题竖排,右边箭头。
代码解析
Row(){Row(){Image($r('app.media.icon')).width(40).height(40)Column(){Text('标题文字').fontSize(16).fontWeight(FontWeight.Bold)Text('副标题说明').fontSize(12).fontColor('#999999')}.alignItems(HorizontalAlign.Start).margin({left:12})}.layoutWeight(1)// 占满左边剩余空间Text('>').fontColor('#999999')}.width('100%').padding(12)这里外层是 Row(左内容 + 右箭头),内层左边又是一个 Row(图标 + 竖排文字的 Column)。.layoutWeight(1)让内层 Row 把中间空间吃满,箭头自然贴右。
小白问答
Q:Column 和 Row 能不能无限嵌套?
能,但别超过 4 层,不然你自己都看不懂,性能也亏。
Q:space 和 margin 有什么区别?space是容器给"所有子元素之间"统一加的空隙;margin是单个元素自己向外撑的空白。想要统一间距用space更省事。
Q:为什么我设了 alignItems 没反应?
八成是你搞混了主轴和交叉轴。Column 的alignItems管水平,Row 的alignItems管垂直,别记反。
常见误区(小白必踩)
| 误区 | 说明 |
|---|---|
| 搞混主轴和交叉轴 | Column 的alignItems管水平、justifyContent管垂直;Row 正好反过来。记反了,对齐永远不对。 |
| space 和 margin 分不清 | space是容器给所有子元素统一加空隙,margin是单个元素自己撑空白。想要均匀间距用space最省事。 |
| 无脑嵌套 | Column/Row 套超过 4 层,自己都看不懂,性能也亏。能扁平就扁平。 |
下面这段代码可以直接复制到 DevEco Studio 里运行。建议你边读边敲,改一改文末「动手改一改」里的参数,亲眼看看效果。
完整可运行示例:写一个登录页
下面这个例子把前面所有知识点串起来。你新建一个 Page,把整段粘进去就能跑。
实现拆解
// 完整登录页:Column 竖向 + Row 行内组合 + 状态绑定@Entry@Componentstruct LoginPage{@Stateaccount:string=''// 账号,@State 让输入能驱动 UI@Statepassword:string=''// 密码build(){Column({space:16}){// 整页竖向,子元素间距 16vpText('欢迎登录').fontSize(24).fontWeight(FontWeight.Bold)// 账号行:固定宽标签 + 输入框Row(){Text('账号').width(60)TextInput({placeholder:'请输入手机号'}).layoutWeight(1)// 输入框吃掉剩余宽度.onChange((v)=>{this.account=v})}// 密码行:用 InputType.Password 隐藏明文Row(){Text('密码').width(60)TextInput({placeholder:'请输入密码'}).layoutWeight(1).type(InputType.Password).onChange((v)=>{this.password=v})}Button('登录').width('100%').backgroundColor('#007DFF').onClick(()=>{// 真实项目里这里换成你的登录请求console.info('账号='+this.account+' 密码='+this.password)})}.width('100%').height('100%').padding(20)}}你会看到什么:一个竖排登录页。顶部粗体标题,中间两行「标签 + 输入框」,底部一根占满宽度的蓝色登录按钮。在输入框打字、点登录,DevEco 的 Log 面板会打印你输入的内容。
动手改一改(建议先试这个):
- 把
Column({ space: 16 })改成space: 32,重新运行,感受元素间距被拉大。 - 给密码行的
TextInput删掉.type(InputType.Password),再输入一次,看明文效果。 - 把
Button('登录')改成Button('注册'),按钮文字立刻变——这就是声明式:改描述即改界面。
写在最后
Column 和 Row 是 ArkUI 的全部地基。你今天只要把"嵌套 + space + 对齐"这三板斧练熟,大部分静态布局都能搞定。下一篇我讲 Flex 弹性布局,专门对付那些"一行塞不下要换行"的场景。
动手敲一遍上面的代码,跑起来看看效果——别偷懒,真的,敲过一遍比看十遍都管用。