HarmonyOS 基础 UI 构建 —— 组件、布局与沉浸式效果
本文献给:
已完成环境搭建、能成功运行第一个鸿蒙项目的开发者。本文将系统讲解 HarmonyOS 中常用的基础组件和布局容器,并带你实现沉浸式效果,帮助你搭建出美观实用的应用界面。
你将学到:
- Text、Button、Image 等基础组件的常用属性与事件
- TextInput、Progress、List 等交互组件的基本用法
- Column、Row、Flex 线性与弹性布局
- RelativeContainer 锚点相对布局
- Stack 层叠布局与 Grid 网格布局
- 沉浸式效果(状态栏/导航栏透明)的实现方法
目录
一、基础组件(上)—— Text、Button、Image
1.1 Text 文本组件
Text 是用于显示文本的基础组件,支持多种样式和事件。
@Entry
@Component
struct TextDemo {
build() {
Column() {
Text('Hello HarmonyOS')
.fontSize(24) // 字号
.fontColor(Color.Blue) // 字体颜色
.fontWeight(FontWeight.Bold) // 粗细
.textAlign(TextAlign.Center) // 对齐方式
.maxLines(2) // 最大行数
.textOverflow({ overflow: TextOverflow.Ellipsis }) // 溢出省略号
.onClick(() => { // 点击事件
console.log('Text clicked');
})
}
.width('100%')
.padding(20)
}
}
常用属性速览:
| 属性 | 说明 |
|---|---|
.fontSize(size) |
字号,单位 vp |
.fontColor(color) |
字体颜色 |
.fontWeight(weight) |
粗细,如 FontWeight.Bold |
.textAlign(align) |
水平对齐 |
.maxLines(n) |
最大行数 |
.textOverflow({ overflow }) |
溢出处理,可选 Clip / Ellipsis |
.onClick(callback) |
点击事件 |
1.2 Button 按钮组件
Button 是最常用的交互组件,支持多种样式和表单。
Button('点击我', { type: ButtonType.Capsule, stateEffect: true })
.fontSize(18)
.width('60%')
.height(40)
.backgroundColor(Color.Green)
.borderRadius(20)
.onClick(() => {
// 处理点击逻辑
})
type:按钮类型,Capsule(胶囊)、Normal(默认)、Circle(圆形)。stateEffect:是否开启点击态效果(按下时变暗)。- 支持所有通用样式(宽高、背景、圆角等)。
1.3 Image 图片组件
Image 用于显示本地或网络图片。
// 本地资源(放在 resources/base/media 目录下)
Image($r('app.media.icon'))
.width(100)
.height(100)
.objectFit(ImageFit.Cover) // 图片填充方式
// 网络图片
Image('https://example.com/photo.jpg')
.width(200)
.height(200)
.borderRadius(10)
常用属性:
objectFit:Cover(裁剪填充)、Contain(完整显示)、Fill(拉伸变形)。- 网络图片需要声明网络权限(在
module.json5中添加ohos.permission.INTERNET)。
二、基础组件(下)—— TextInput、Progress、List
2.1 TextInput 输入框
用于接收用户文本输入。
@State inputValue: string = '';
TextInput({ placeholder: '请输入用户名', text: this.inputValue })
.width('80%')
.height(40)
.backgroundColor(Color.White)
.borderRadius(8)
.type(InputType.Normal) // 输入类型:Normal / Password / Email 等
.onChange((value: string) => {
this.inputValue = value; // 实时更新状态
})
placeholder:占位提示文字。type:Normal/Password/Email/Number等。.onChange用于监听输入变化,通常配合@State实现双向绑定。
2.2 Progress 进度条
用于显示操作进度或加载状态。
Progress({ value: 60, total: 100, type: ProgressType.Linear })
.width('80%')
.height(20)
.color(Color.Blue)
value/total:当前值与总值。type:Linear(条形)、Ring(环形)、ScaleRing(带刻度环形)、Eclipse(月食形)、Capsule(胶囊形)。
2.3 List 列表组件
用于展示可滚动的列表数据。
@State items: string[] = ['项目 A', '项目 B', '项目 C'];
List() {
ForEach(this.items, (item: string, index: number) => {
ListItem() {
Text(item)
.fontSize(18)
.padding(12)
}
.border({ width: { bottom: 1 }, color: '#eee' })
})
}
.width('100%')
.height('60%')
- 必须与
ListItem配合使用。 ForEach用于循环渲染列表项,需要提供唯一 key(此示例索引自动生成)。- 支持设置分割线、滚动条、编辑模式等。
三、线性布局 —— Column 与 Row
线性布局是最基础的布局方式,Column 表示垂直排列,Row 表示水平排列。
3.1 Column 垂直布局
Column({ space: 10 }) { // space 设置子组件间距
Text('第一行')
Text('第二行')
Button('按钮')
}
.width('100%')
.height(200)
.backgroundColor('#f5f5f5')
.justifyContent(FlexAlign.Center) // 主轴(垂直)对齐
.alignItems(HorizontalAlign.Start) // 交叉轴(水平)对齐
justifyContent控制主轴对齐(Start / Center / End / SpaceBetween 等)。alignItems控制交叉轴对齐(Start / Center / End)。
3.2 Row 水平布局
Row({ space: 8 }) {
Image($r('app.media.icon')).width(40).height(40)
Text('标题').fontSize(18)
Blank() // 空白占位,推动后面的元素靠右
Button('操作').width(60).height(30)
}
.width('90%')
.height(60)
.padding(10)
Blank()组件可占据剩余空间,常用于两端对齐。- 支持
layoutWeight属性,设置子组件在主轴上的权重分配(类似 Flex 的 flex-grow)。
四、弹性布局 —— Flex
Flex 提供了比 Column/Row 更灵活的弹性盒子模型,当效果用 Column/Row 难以实现时,可以使用 Flex。
Flex({
direction: FlexDirection.Row, // 主轴方向
wrap: FlexWrap.Wrap, // 自动换行
justifyContent: FlexAlign.SpaceAround,
alignItems: ItemAlign.Center
}) {
Text('A').width(80).height(60).backgroundColor(Color.Red)
Text('B').width(80).height(60).backgroundColor(Color.Green)
Text('C').width(80).height(60).backgroundColor(Color.Blue)
}
.width('100%')
.height(150)
.backgroundColor('#f0f0f0')
与 Column/Row 的区别:
- Column/Row 是 Flex 的特定封装(方向固定)。
- Flex 可直接设置
direction,支持Column、Row、ColumnReverse、RowReverse。 - Flex 的
wrap属性能实现自动换行,更适合弹性伸缩场景。
五、相对布局 —— RelativeContainer
RelativeContainer 允许子组件相对于父容器或其他子组件进行定位,通过锚点实现复杂界面。
5.1 基本用法
RelativeContainer() {
Text('左上角')
.alignRules({
top: { anchor: '__container__', align: VerticalAlign.Top },
left: { anchor: '__container__', align: HorizontalAlign.Start }
})
.id('topLeftText')
Button('右下角')
.alignRules({
bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
right: { anchor: '__container__', align: HorizontalAlign.End }
})
.id('bottomRightBtn')
}
.width('100%')
.height(300)
.backgroundColor(Color.White)
'__container__'代表父容器。- 通过
top、bottom、left、right、center、middle等规则设定对齐目标。 - 每个子组件必须有唯一的
id,用于其他组件参考。
5.2 相对于其他子组件定位
RelativeContainer() {
Image($r('app.media.avatar'))
.width(60)
.height(60)
.id('avatar')
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
Text('用户名')
.id('username')
.alignRules({
top: { anchor: 'avatar', align: VerticalAlign.Bottom }, // 在头像下方
middle: { anchor: 'avatar', align: HorizontalAlign.Center }
})
.margin({ top: 10 })
}
.width('100%')
.height(200)
这种相对定位方式避免了复杂的嵌套布局,代码更扁平、易维护。
六、其他常用容器 —— Stack 与 Grid
6.1 Stack 层叠布局
Stack 将子组件按添加顺序层叠放置,后添加的组件显示在上层。常用于悬浮按钮、文字覆盖图片等场景。
Stack({ alignContent: Alignment.BottomEnd }) {
Image($r('app.media.banner'))
.width('100%')
.height(200)
Button('查看详情')
.margin(16)
.backgroundColor(Color.Orange)
}
.width('90%')
.height(200)
.borderRadius(10)
alignContent设置所有子组件的默认对齐方式,可被单个子组件覆盖。- 使用
position或offset可实现更精细的定位。
6.2 Grid 网格布局
Grid 用于创建多行多列的布局,适合相册、商品列表等。
@State items: string[] = ['1', '2', '3', '4', '5', '6'];
Grid() {
ForEach(this.items, (item: string) => {
GridItem() {
Text(item)
.width('100%')
.height(100)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Orange)
.borderRadius(8)
}
})
}
.columnsTemplate('1fr 1fr 1fr') // 三列等分
.rowsGap(10)
.columnsGap(10)
.width('90%')
.height(300)
columnsTemplate:定义列宽模板,'1fr 1fr'表示两列等分。rowsTemplate:定义行高模板。- 支持
GridItem的合并单元格(rowStart/rowEnd、columnStart/columnEnd)。
七、沉浸式效果实现
沉浸式效果让应用界面延伸到状态栏和导航栏区域,提升视觉体验。
7.1 核心概念
- 窗口安全区:为避免被系统栏遮挡,系统默认会为应用内容留出安全区域。
- 实现沉浸式需要让内容延伸到安全区,并处理可能的遮挡问题。
7.2 实现步骤
第 1 步:设置窗口全屏布局
在 module.json5 中对 Ability 进行配置:
{
"abilities": [{
"name": "EntryAbility",
"window": {
"fullScreen": true
}
}]
}
或者在页面中动态设置:
import window from '@ohos.window';
onPageShow() {
window.getLastWindow(getContext()).then((win) => {
win.setWindowLayoutFullScreen(true);
});
}
第 2 步:使用 expandSafeArea 属性
在布局根组件上使用 .expandSafeArea,让内容延伸到安全区。
Column()
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
.width('100%')
.height('100%')
.linearGradient({
angle: 180,
colors: [[0x1E90FF, 0.0], [0x87CEEB, 1.0]]
})
SafeAreaType.SYSTEM:指状态栏和导航栏区域。SafeAreaEdge.TOP/BOTTOM:指定在哪些边缘扩展。
第 3 步:处理重叠内容
内容扩展后,可能会和状态栏图标重叠。可以为顶部标题增加 padding 或使用 margin 避开。
Column() {
Text('首页标题')
.fontSize(20)
.fontColor(Color.White)
.margin({ top: 30 }) // 向下偏移,避免与状态栏重叠
// 其他内容...
}
.width('100%')
.height('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
通过上述配置,即可实现背景延伸到状态栏的沉浸式效果。
八、常见错误与注意事项
8.1 布局未填满父容器
Column/Row 默认高度/宽度根据内容自适应,若想占满父容器,需显式设置 .width('100%') 和 .height('100%')。
8.2 组件 id 重复导致 RelativeContainer 报错
每个子组件 id 必须唯一,否则运行时报错。使用有意义的 id 并检查重复。
8.3 Grid 未指定列模板导致布局异常
Grid 必须设置 columnsTemplate,否则可能只显示一列。通常使用 '1fr 1fr' 等 fr 单位。
8.4 沉浸式效果但部分手机仍有黑边
- 确认已设置
fullScreen: true或调用setWindowLayoutFullScreen。 - 检查是否同时设置了
expandSafeArea,只设全屏而不扩展安全区无法让内容绘制到状态栏下方。 - 某些系统组件(如 TitleBar)可能自带安全区适配,需自行处理。
8.5 List 性能问题
长列表建议使用 LazyForEach 替代 ForEach,实现懒加载,提升性能。
九、小结
| 组件 / 布局 | 关键点 |
|---|---|
| Text、Button、Image | 基础显示与交互组件,常用样式属性 |
| TextInput、Progress、List | 高级交互与数据展示组件 |
| Column / Row | 线性布局,主轴与交叉轴对齐 |
| Flex | 弹性布局,支持换行和反转 |
| RelativeContainer | 锚点相对定位,减少嵌套 |
| Stack | 层叠布局,适合悬浮效果 |
| Grid | 网格布局,适合多列排列 |
| 沉浸式效果 | 通过 fullScreen 和 expandSafeArea 实现背景延伸至状态栏 |
觉得文章有帮助?别忘了:
👍 点赞 👍 – 给我一点鼓励
⭐ 收藏 ⭐ – 方便以后查看
🔔 关注 🔔 – 获取更新通知
标签: #HarmonyOS #ArkUI #布局 #沉浸式效果 #基础组件 #学习笔记 #鸿蒙开发
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)