第二章 ArkUI基础组件与布局:常用组件与布局容器实战
大家好,我是你们的老朋友。今天咱们来聊聊ArkUI里最基础、也最常用的那些组件和布局方式。说实话,这部分内容看着简单,但我在项目里见过太多人在这上面栽跟头了。你想想看,一个App好不好看、好不好用,很大程度上就取决于这些基础组件和布局用得怎么样。
2.1 常用组件:Text、Image、Button
这三个组件,可以说是鸿蒙应用开发的"三驾马车"。几乎每个页面都离不开它们。我个人习惯先把它们玩透了,再去碰那些花里胡哨的高级组件。
2.1.1 Text组件
Text组件就是用来显示文字的。嗯,这里要注意,它不只是显示纯文本那么简单。
Text('Hello HarmonyOS')
.fontSize(20)
.fontColor(Color.Blue)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
.lineHeight(30)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
我曾经在做一个新闻列表时,就遇到过文字超长把布局撑变形的问题。后来用了maxLines和textOverflow,问题就解决了。说白了,就是给文字加个"笼子",不让它乱跑。
Span组件嵌套在Text里。比如一段文字里既有红色又有蓝色,用Span就能轻松搞定。
2.1.2 Image组件
Image组件用来显示图片。我记得刚开始用的时候,总以为直接给个路径就行了。其实没那么简单。
Image($r('app.media.icon'))
.width(100)
.height(100)
.borderRadius(10)
.objectFit(ImageFit.Cover)
.interpolation(ImageInterpolation.High)
这里有个坑,我必须要说。图片的objectFit属性,很多人搞不清楚。它决定了图片在容器里怎么缩放。我建议你记住这几个:
- Cover:裁剪填充,保证图片填满容器,可能会被裁掉一部分
- Contain:完整显示,保证图片全部可见,可能会有留白
- Fill:拉伸填充,图片会变形
.alt()属性设置占位图。
2.1.3 Button组件
Button组件,说白了就是用户点击交互的入口。但它的样式定制,比你想的要灵活得多。
Button('点击我')
.type(ButtonType.Capsule)
.backgroundColor('#007AFF')
.fontColor(Color.White)
.fontSize(16)
.width(200)
.height(48)
.borderRadius(24)
.onClick(() => {
console.log('按钮被点击了')
})
我个人习惯用ButtonType.Capsule胶囊样式,看起来更圆润、更现代。如果你想要完全自定义的按钮,可以用Button包裹其他组件,比如:
Button() {
Row() {
Image($r('app.media.icon'))
.width(20)
.height(20)
Text('带图标的按钮')
.fontSize(14)
}
}
2.2 布局容器:Row、Column、Stack
布局容器,就是用来"装"组件的容器。它们决定了组件在屏幕上的排列方式。你想想看,没有布局容器,所有组件都堆在一起,那画面太美不敢看。
2.2.1 Row(水平布局)
Row让子组件水平排列。我经常用它来做导航栏、标签栏。
Row() {
Text('首页')
Text('发现')
Text('我的')
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
.alignItems(VerticalAlign.Center)
这里justifyContent控制水平方向的对齐,alignItems控制垂直方向。我建议你多试试不同的组合,感受一下效果。
2.2.2 Column(垂直布局)
Column让子组件垂直排列。表单页面、列表页面,基本都靠它。
Column() {
Text('用户名')
TextInput({ placeholder: '请输入用户名' })
Text('密码')
TextInput({ placeholder: '请输入密码' })
Button('登录')
}
.width('100%')
.padding(20)
.alignItems(HorizontalAlign.Start)
嗯,这里要注意,Column默认是居中对齐的。如果你想让子组件左对齐,记得设置alignItems(HorizontalAlign.Start)。
2.2.3 Stack(层叠布局)
Stack允许子组件重叠。说白了,就像叠罗汉一样。我经常用它来做头像上的角标、图片上的文字水印。
Stack() {
Image($r('app.media.avatar'))
.width(100)
.height(100)
.borderRadius(50)
Text('VIP')
.fontSize(12)
.fontColor(Color.White)
.backgroundColor(Color.Red)
.borderRadius(4)
.padding({ left: 4, right: 4 })
.position({ x: 70, y: 70 })
}
position属性可以精确控制子组件的位置。但要注意,它是相对于Stack容器本身的。我建议你多用alignContent和alignItems来做相对定位,这样适配性更好。
2.3 Flex布局实战
Flex布局,可以说是布局里的"瑞士军刀"。它比Row和Column更灵活,能应对更复杂的场景。
2.3.1 Flex基础
Flex布局的核心思想,就是让子组件"弹性"地填充空间。我刚开始学的时候,觉得它很抽象。后来做多了,发现其实就是几个关键属性的事。
Flex({
direction: FlexDirection.Row,
wrap: FlexWrap.Wrap,
justifyContent: FlexAlign.SpaceBetween,
alignItems: ItemAlign.Center
}) {
Text('项目1').width(80).height(40).backgroundColor('#FF6B6B')
Text('项目2').width(80).height(40).backgroundColor('#4ECDC4')
Text('项目3').width(80).height(40).backgroundColor('#45B7D1')
Text('项目4').width(80).height(40).backgroundColor('#96CEB4')
}
2.3.2 实战:商品列表
我记得有一次做电商项目,需要实现一个商品列表。每行显示两个商品,还要自适应屏幕宽度。用Flex布局,几行代码就搞定了。
Flex({
direction: FlexDirection.Row,
wrap: FlexWrap.Wrap,
justifyContent: FlexAlign.SpaceBetween
}) {
ForEach(this.productList, (item) => {
Column() {
Image(item.image)
.width('100%')
.height(150)
.objectFit(ImageFit.Cover)
Text(item.name)
.fontSize(14)
.margin({ top: 8 })
Text('¥' + item.price)
.fontSize(16)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
}
.width('48%')
.backgroundColor(Color.White)
.borderRadius(8)
.padding(10)
.margin({ bottom: 10 })
})
}
width为百分比。比如两列就设48%,留出间距。我曾经直接设50%,结果因为间距问题,第二行就错位了。
2.3.3 Flex的伸缩性
Flex最强大的地方,在于它的伸缩性。你可以让某个子组件"吃掉"剩余的空间。
Row() {
Text('固定宽度').width(100).backgroundColor('#FF6B6B')
Text('弹性伸缩').layoutWeight(1).backgroundColor('#4ECDC4')
Text('固定宽度').width(100).backgroundColor('#45B7D1')
}
.width('100%')
.height(50)
这里layoutWeight(1)就是关键。它告诉系统:剩下的空间都给我。你想想看,这在做自适应布局时有多方便。
layoutWeight完全不起作用。记住,Flex的伸缩性需要父容器有明确的尺寸约束。
2.4 布局实战:个人中心页面
好了,理论说完了。咱们来做个实战。一个简单的个人中心页面,把今天学的都用上。
Column() {
// 头像区域
Stack() {
Image($r('app.media.avatar'))
.width(80)
.height(80)
.borderRadius(40)
Text('编辑')
.fontSize(12)
.fontColor(Color.White)
.backgroundColor('rgba(0,0,0,0.5)')
.borderRadius(4)
.padding({ left: 8, right: 8 })
.position({ x: 20, y: 60 })
}
.width(80)
.height(80)
.margin({ top: 30 })
// 用户名
Text('张三')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 10 })
// 统计信息
Row() {
Column() {
Text('128')
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text('关注')
.fontSize(12)
.fontColor('#666')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
Column() {
Text('256')
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text('粉丝')
.fontSize(12)
.fontColor('#666')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
Column() {
Text('64')
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text('动态')
.fontSize(12)
.fontColor('#666')
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.padding(20)
// 功能列表
Column() {
Row() {
Text('我的订单')
Text('>')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding(15)
Row() {
Text('我的收藏')
Text('>')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding(15)
Row() {
Text('设置')
Text('>')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding(15)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(10)
.padding(10)
.margin({ top: 20 })
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.alignItems(HorizontalAlign.Center)
这个页面用到了我们今天学的所有内容:Text、Image、Button、Row、Column、Stack、Flex。你仔细看看,是不是每个组件都各司其职?
好了,今天的课就到这里。记住,布局这东西,光看不行,一定要动手练。我建议你把这个个人中心页面自己敲一遍,然后试着改改样式、加加功能。下节课我们聊状态管理和数据绑定,到时候见。