第3篇:界面基础——构建第一个页面 鸿蒙中文编程
本文介绍了使用ArkUI构建应用界面的基础知识,主要内容包括: 声明式UI设计理念:通过"告诉系统要什么"而非"如何实现"的方式构建界面,类似点菜只需说明需求而非烹饪步骤。 四大基础组件: Text组件:用于显示文字,可设置大小、颜色、对齐等样式 Button组件:可创建多种形态的交互按钮,支持点击事件 Image组件:支持显示网络/本地图片,可设置显示模式
第3篇:界面基础——构建第一个页面
本课目标:掌握ArkUI的基础组件和布局,能制作美观的界面
**作者:**中文编程倡导者—— 李金雨
预计课时:3课时(135分钟)
难度等级:⭐⭐⭐(进阶)
一、开篇引入
1.1 界面就像搭积木
你有没有玩过乐高积木?
- 乐高有各种形状的积木块
- 你可以把它们拼成房子、汽车、飞船
- 不同的拼法,能做出完全不同的东西
做应用界面就像搭积木:
- 我们有各种"界面积木"(组件)
- 把它们组合起来,就能做出漂亮的界面
- 文字积木、按钮积木、图片积木、输入框积木…
1.2 本课我们要做什么?
今天我们要学习:
- 基础组件——文字、按钮、图片、输入框
- 布局容器——怎么排列这些组件
- 样式设置——颜色、大小、间距、圆角
- 实战项目——制作登录页面和个人名片
1.3 预期成果
完成本课后,你能做出这样的界面:
登录页面: 个人名片:
┌─────────────────┐ ┌─────────────────┐
│ │ │ ┌───────┐ │
│ 用户登录 │ │ │ 👤 │ │
│ │ │ └───────┘ │
│ ┌───────────┐ │ │ │
│ │ 请输入用户名│ │ │ 张三 │
│ └───────────┘ │ │ 软件工程师 │
│ │ │ │
│ ┌───────────┐ │ │ 📧 zhang@qq.com│
│ │ 请输入密码 │ │ │ 📱 138****8888 │
│ └───────────┘ │ │ │
│ │ │ [发消息] [关注] │
│ ┌───────────┐ │ │ │
│ │ 登 录 │ │ └─────────────────┘
│ └───────────┘ │
│ │
└─────────────────┘
二、概念讲解
2.1 声明式UI——告诉它要什么
生活例子:去餐厅点菜
命令式(传统方式):
你去厨房,拿一个锅,倒油,
加热到180度,放入鸡蛋,
煎3分钟,翻面,再煎2分钟,
装盘,撒上葱花...
声明式(ArkTS方式):
我要一份煎蛋,两面金黄,撒葱花。
看到了吗?声明式更简单!你只需要说想要什么结果,不用管具体怎么做。
ArkTS的声明式写法
// 声明:我要一个垂直排列的容器
Column() {
// 里面放一个文字,内容是"你好"
Text("你好")
// 文字大小是30
.fontSize(30)
// 文字颜色是红色
.fontColor("#FF0000")
}
你声明了界面应该长什么样,ArkTS会自动帮你实现!
2.2 基础组件——界面积木
组件1:Text(文字)——显示文字
就像纸上的文字,用来显示信息。
Text("这是一段文字")
.fontSize(20) // 字体大小
.fontColor("#333333") // 字体颜色
.fontWeight(FontWeight.Bold) // 字体粗细
.maxLines(2) // 最多显示2行
.textOverflow({ overflow: TextOverflow.Ellipsis }) // 超出显示省略号
常用属性:
| 属性 | 作用 | 例子 |
|---|---|---|
.fontSize(20) |
字体大小 | .fontSize(30) |
.fontColor("#FF0000") |
字体颜色 | .fontColor("#2196F3") |
.fontWeight(FontWeight.Bold) |
字体粗细 | .fontWeight(FontWeight.Normal) |
.lineHeight(30) |
行高 | .lineHeight(40) |
.textAlign(TextAlign.Center) |
文字对齐 | .textAlign(TextAlign.Left) |
组件2:Button(按钮)——点击触发动作
就像电梯按钮、门铃,点击后会做某事。
Button("点我")
.width(200) // 宽度
.height(50) // 高度
.backgroundColor("#2196F3") // 背景颜色
.fontColor("#FFFFFF") // 文字颜色
.fontSize(20) // 文字大小
.onClick(() => { // 点击时做什么
console.log("按钮被点击了!")
})
按钮的类型:
// 普通按钮(默认)
Button("普通按钮")
// 胶囊按钮(圆角)
Button("胶囊按钮", { type: ButtonType.Capsule })
// 圆形按钮
Button("圆", { type: ButtonType.Circle })
.width(60)
.height(60)
组件3:Image(图片)——显示图片
用来显示照片、图标、背景图等。
// 显示网络图片
Image("https://example.com/photo.jpg")
.width(200)
.height(200)
.borderRadius(10) // 圆角
// 显示本地图片(放在resources/rawfile目录)
Image($rawfile("avatar.png"))
.width(100)
.height(100)
// 显示资源图片(放在resources/base/media目录)
Image($r("app.media.icon"))
.width(50)
.height(50)
图片显示模式:
Image("xxx.jpg")
.objectFit(ImageFit.Cover) // 裁剪填满(保持比例)
.objectFit(ImageFit.Contain) // 完整显示(可能有空白)
.objectFit(ImageFit.Fill) // 拉伸填满(可能变形)
组件4:TextInput(输入框)——输入文字
就像表格里的填空处,让用户输入信息。
TextInput({
placeholder: "请输入用户名", // 提示文字
text: "" // 默认内容
})
.width("80%") // 宽度
.height(50) // 高度
.backgroundColor("#F5F5F5") // 背景色
.borderRadius(8) // 圆角
.onChange((value: string) => { // 内容变化时
console.log("输入了:" + value)
})
输入框类型:
// 普通文本
TextInput({ placeholder: "请输入" })
// 密码输入(显示圆点)
TextInput({ placeholder: "请输入密码" })
.type(InputType.Password)
// 数字输入(弹出数字键盘)
TextInput({ placeholder: "请输入年龄" })
.type(InputType.Number)
// 邮箱输入
TextInput({ placeholder: "请输入邮箱" })
.type(InputType.Email)
2.3 布局容器——怎么排列积木
容器1:Column(垂直排列)——一列一列往下排
就像排队做操,一个接一个竖着排。
Column({ space: 20 }) { // space: 子组件之间的间距
Text("第一行")
Text("第二行")
Text("第三行")
Button("按钮")
}
.width("100%") // 容器宽度
.height("100%") // 容器高度
.backgroundColor("#F0F0F0") // 背景色
.padding(20) // 内边距
效果:
第一行
第二行
第三行
[按钮]
容器2:Row(水平排列)——一行一行横着排
就像坐成一排,一个接一个横着排。
Row({ space: 20 }) {
Text("左边")
Text("中间")
Text("右边")
}
.width("100%")
.height(60)
.backgroundColor("#E3F2FD")
效果:
左边 中间 右边
容器3:Stack(层叠排列)——叠在一起
就像一叠纸,一张盖在另一张上面。
Stack({ alignContent: Alignment.Center }) {
// 底层:背景图
Image($r("app.media.bg"))
.width("100%")
.height("100%")
// 上层:文字
Text("叠在上面的文字")
.fontSize(30)
.fontColor("#FFFFFF")
}
.width("100%")
.height(200)
效果:
┌─────────────────┐
│ [背景图片] │
│ │
│ 叠在上面的文字 │
│ │
└─────────────────┘
容器4:Flex(弹性布局)——灵活排列
更灵活的布局方式,可以自动调整子组件的大小和位置。
Flex({
direction: FlexDirection.Row, // 水平排列
justifyContent: FlexAlign.Center, // 水平居中
alignItems: ItemAlign.Center // 垂直居中
}) {
Text("居中文字")
Button("居中按钮")
}
.width("100%")
.height(100)
2.4 样式设置——让界面变漂亮
尺寸设置
组件()
.width(200) // 固定宽度200像素
.width("50%") // 占父容器宽度的50%
.width("100%") // 填满父容器
.height(100) // 固定高度
.height("100%") // 填满父容器
.size({ width: 200, height: 100 }) // 同时设置宽高
边距设置
// 外边距(margin):组件与其他组件的距离
组件()
.margin(20) // 四边都是20
.margin({ top: 10 }) // 只有上边10
.margin({ left: 10, right: 10 }) // 左右各10
.margin({ top: 10, bottom: 10, left: 20, right: 20 })
// 内边距(padding):内容与边框的距离
组件()
.padding(20) // 四边都是20
.padding({ horizontal: 15 }) // 左右15
.padding({ vertical: 10 }) // 上下10
比喻:
- 外边距 = 你和同桌之间的距离
- 内边距 = 书本内容和纸张边缘的距离
边框设置
组件()
.border({
width: 2, // 边框宽度
color: "#2196F3", // 边框颜色
style: BorderStyle.Solid // 边框样式(实线)
})
.borderRadius(10) // 圆角半径
.borderRadius({
topLeft: 10, // 左上圆角
topRight: 20, // 右上圆角
bottomLeft: 10, // 左下圆角
bottomRight: 20 // 右下圆角
})
背景设置
组件()
.backgroundColor("#F5F5F5") // 纯色背景
.backgroundColor("#2196F3") // 蓝色背景
.backgroundColor(0x2196F3) // 另一种写法
.backgroundImage($r("app.media.bg")) // 图片背景
.backgroundImageSize(ImageSize.Cover) // 背景图大小
对齐方式
Column() {
Text("居中")
}
.justifyContent(FlexAlign.Center) // 垂直居中
.alignItems(HorizontalAlign.Center) // 水平居中
Row() {
Text("居中")
}
.justifyContent(FlexAlign.Center) // 水平居中
.alignItems(VerticalAlign.Center) // 垂直居中
三、动手实践
3.1 基础练习:制作登录页面
让我们做一个漂亮的登录页面:
// 完整可运行代码,复制到 Index.ets 即可运行
@Entry
@Component
struct Index {
@State 用户名: string = ""
@State 密码: string = ""
build() {
Column({ space: 20 }) {
// 顶部标题区域
Column({ space: 10 }) {
Text("欢迎回来")
.fontSize(35)
.fontWeight(FontWeight.Bold)
.fontColor("#333333")
Text("请登录您的账号")
.fontSize(16)
.fontColor("#999999")
}
.margin({ top: 80, bottom: 40 })
// 输入区域
Column({ space: 15 }) {
// 用户名输入框
TextInput({
placeholder: "请输入用户名",
text: this.用户名
})
.width("85%")
.height(55)
.backgroundColor("#F5F5F5")
.borderRadius(12)
.padding({ left: 20 })
.onChange((value: string) => {
this.用户名 = value
})
// 密码输入框
TextInput({
placeholder: "请输入密码",
text: this.密码
})
.width("85%")
.height(55)
.backgroundColor("#F5F5F5")
.borderRadius(12)
.padding({ left: 20 })
.type(InputType.Password)
.onChange((value: string) => {
this.密码 = value
})
}
// 登录按钮
Button("登 录", { type: ButtonType.Capsule })
.width("85%")
.height(55)
.backgroundColor("#2196F3")
.fontSize(18)
.fontWeight(FontWeight.Medium)
.margin({ top: 30 })
.onClick(() => {
// 这里写登录逻辑
console.log("用户名:" + this.用户名)
console.log("密码:" + this.密码)
})
// 底部链接
Row({ space: 5 }) {
Text("还没有账号?")
.fontSize(14)
.fontColor("#999999")
Text("立即注册")
.fontSize(14)
.fontColor("#2196F3")
.fontWeight(FontWeight.Bold)
}
.margin({ top: 20 })
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
}
}
3.2 进阶练习:制作个人名片
做一个精美的个人名片:
// 完整可运行代码,复制到 Index.ets 即可运行
@Entry
@Component
struct Index {
@State 姓名: string = "张三"
@State 职位: string = "高级前端工程师"
@State 公司: string = "华为技术有限公司"
@State 邮箱: string = "zhangsan@huawei.com"
@State 电话: string = "138****8888"
build() {
Column() {
// 顶部装饰条
Row()
.width('100%')
.height(120)
.backgroundColor('#2196F3')
// 头像(叠在装饰条下方)
Stack({ alignContent: Alignment.Bottom }) {
Column()
.width(120)
.height(120)
.backgroundColor('#FFFFFF')
.borderRadius(60)
.shadow({ radius: 10, color: '#20000000' })
Text("👤")
.fontSize(60)
.margin({ bottom: 25 })
}
.margin({ top: -60 })
// 姓名和职位
Column({ space: 8 }) {
Text(this.姓名)
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text(this.职位)
.fontSize(16)
.fontColor('#666666')
Text(this.公司)
.fontSize(14)
.fontColor('#999999')
}
.margin({ top: 20 })
// 分隔线
Row()
.width('80%')
.height(1)
.backgroundColor('#EEEEEE')
.margin({ top: 30, bottom: 30 })
// 联系信息
Column({ space: 20 }) {
this.信息项("📧", "邮箱", this.邮箱)
this.信息项("📱", "电话", this.电话)
this.信息项("📍", "地址", "深圳市南山区")
}
.width('85%')
// 操作按钮
Row({ space: 20 }) {
Button("发消息", { type: ButtonType.Capsule })
.width(120)
.height(45)
.backgroundColor('#2196F3')
Button("关注", { type: ButtonType.Capsule })
.width(120)
.height(45)
.backgroundColor('#FFFFFF')
.fontColor('#2196F3')
.border({ width: 1, color: '#2196F3' })
}
.margin({ top: 40 })
// 底部留白
Blank()
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
// 自定义信息项组件
@Builder
信息项(图标: string, 标签: string, 内容: string) {
Row() {
Text(图标)
.fontSize(20)
.width(30)
Text(标签)
.fontSize(14)
.fontColor('#999999')
.width(50)
Text(内容)
.fontSize(16)
.fontColor('#333333')
}
.width('100%')
.height(40)
}
}
3.3 挑战练习:商品展示卡片
做一个电商App的商品卡片:
// 完整可运行代码,复制到 Index.ets 即可运行
@Entry
@Component
struct Index {
@State 商品列表: object[] = [
{
名称: "无线蓝牙耳机",
价格: 299,
原价: 399,
销量: 12580,
标签: ["热销", "包邮"]
},
{
名称: "智能手环 Pro",
价格: 199,
原价: 259,
销量: 8560,
标签: ["新品"]
},
{
名称: "便携充电宝",
价格: 89,
原价: 129,
销量: 23150,
标签: ["爆款", "限时"]
}
]
build() {
Column({ space: 15 }) {
// 标题
Text("热门商品")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, left: 20 })
.alignSelf(ItemAlign.Start)
// 商品列表
List({ space: 15 }) {
ForEach(this.商品列表, (商品: object) => {
ListItem() {
this.商品卡片(商品)
}
})
}
.padding(20)
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
@Builder
商品卡片(商品: object) {
Column() {
// 商品图片区域(用颜色块代替)
Stack({ alignContent: Alignment.TopStart }) {
Row()
.width('100%')
.height(180)
.backgroundColor('#E3F2FD')
Text("📷")
.fontSize(60)
.width('100%')
.textAlign(TextAlign.Center)
.margin({ top: 50 })
// 标签
Row({ space: 5 }) {
ForEach(商品.标签, (标签: string) => {
Text(标签)
.fontSize(12)
.fontColor('#FFFFFF')
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
.backgroundColor('#FF5722')
.borderRadius(4)
})
}
.margin(10)
}
// 商品信息
Column({ space: 8 }) {
Text(商品.名称)
.fontSize(16)
.fontColor('#333333')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row() {
Text(`¥${商品.价格}`)
.fontSize(22)
.fontColor('#F44336')
.fontWeight(FontWeight.Bold)
Text(`¥${商品.原价}`)
.fontSize(14)
.fontColor('#999999')
.decoration({ type: TextDecorationType.LineThrough })
.margin({ left: 10 })
}
Text(`${商品.销量}人已购`)
.fontSize(12)
.fontColor('#999999')
}
.width('100%')
.padding(12)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({ radius: 6, color: '#10000000' })
}
}
四、知识总结
4.1 核心概念回顾
- 声明式UI:告诉系统"要什么",而不是"怎么做"
- 组件:界面的基本 building blocks(积木块)
- 容器:用来排列组件的"盒子"
- 样式:让界面变漂亮的各种设置
4.2 组件速查表
| 组件 | 作用 | 常用属性 |
|---|---|---|
Text |
显示文字 | .fontSize(), .fontColor(), .fontWeight() |
Button |
按钮 | .backgroundColor(), .onClick() |
Image |
显示图片 | .width(), .height(), .borderRadius() |
TextInput |
输入框 | .placeholder, .type(), .onChange() |
Column |
垂直排列 | .space, .justifyContent(), .alignItems() |
Row |
水平排列 | .space, .justifyContent(), .alignItems() |
Stack |
层叠排列 | .alignContent |
4.3 样式速查表
| 样式 | 代码 | 例子 |
|---|---|---|
| 尺寸 | .width(), .height() |
.width("100%") |
| 边距 | .margin(), .padding() |
.margin(20), .padding({ top: 10 }) |
| 边框 | .border(), .borderRadius() |
.borderRadius(10) |
| 背景 | .backgroundColor() |
.backgroundColor("#F5F5F5") |
| 对齐 | .justifyContent(), .alignItems() |
.justifyContent(FlexAlign.Center) |
| 阴影 | .shadow() |
.shadow({ radius: 10, color: '#20000000' }) |
4.4 常见错误提醒
| 错误现象 | 原因 | 解决方法 |
|---|---|---|
| 组件不显示 | 没设置宽高 | 给组件设置.width()和.height() |
| 文字被截断 | 容器太小 | 增大容器或使用.maxLines() |
| 布局错乱 | 百分比计算错误 | 检查父容器是否有固定尺寸 |
| 按钮点不了 | 被其他组件覆盖 | 检查Stack中的层级顺序 |
| 样式不生效 | 写在了错误的位置 | 确保样式跟在组件后面 |
五、课后作业
5.1 巩固练习(必做)
练习1:完善登录页面
给登录页面添加:
- 记住密码复选框
- 第三方登录按钮(微信、QQ图标)
- 忘记密码链接
练习2:制作设置页面
做一个手机设置页面,包含:
- 头像和用户名
- 多个设置项(账号、通知、隐私、关于)
- 每个设置项右边有箭头
练习3:制作计算器界面
只做界面,不用实现功能:
- 显示区域(显示数字)
- 数字按钮(0-9)
- 运算符按钮(+、-、×、÷)
- 等号按钮
5.2 创意编程(选做)
创意1:制作音乐播放器界面
包含:
- 专辑封面(大图)
- 歌曲名和歌手名
- 进度条
- 播放控制按钮(上一首、播放/暂停、下一首)
创意2:制作天气卡片
包含:
- 城市名称
- 当前温度和天气图标
- 未来几天预报
- 空气质量指数
创意3:制作聊天界面
包含:
- 消息列表(左右气泡)
- 输入框和发送按钮
- 对方头像和名字
5.3 下篇预习
下一篇,我们将学习状态管理,让界面能够响应用户的操作。预习问题:
- 怎么让按钮点击后改变文字内容?
- 怎么实现计数器功能(点一下数字加1)?
- 怎么让输入框的内容实时显示在界面上?
附录:更多布局技巧
技巧1:等分布局
Row() {
Text("左")
.layoutWeight(1) // 占1份
.backgroundColor('#FFCDD2')
Text("中")
.layoutWeight(2) // 占2份
.backgroundColor('#C8E6C9')
Text("右")
.layoutWeight(1) // 占1份
.backgroundColor('#BBDEFB')
}
.width('100%')
.height(100)
效果:左占25%,中占50%,右占25%
技巧2:滚动列表
Scroll() {
Column({ space: 10 }) {
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (item: number) => {
Text(`第${item}项`)
.width('100%')
.height(80)
.backgroundColor('#F5F5F5')
})
}
.width('100%')
}
.height('100%')
技巧3:网格布局
Grid() {
ForEach([1, 2, 3, 4, 5, 6], (item: number) => {
GridItem() {
Text(`${item}`)
.width('100%')
.height(100)
.backgroundColor('#E3F2FD')
}
})
}
.columnsTemplate('1fr 1fr 1fr') // 3列等宽
.columnsGap(10) // 列间距
.rowsGap(10) // 行间距
.width('100%')
恭喜你完成了第3篇的学习! 🎉
现在你已经掌握了制作漂亮界面的基本技能。记住,好的界面设计需要多观察、多练习。平时可以多看看优秀的App,学习它们的布局和配色!
下节课,我们将学习如何让界面"动"起来,响应用户的操作!
更多推荐

所有评论(0)