ArkUI 组件基础与界面布局学习笔记接上一篇 ArkTS 基础语法笔记这一篇整理 ArkUI 的组件基础语法、常用属性、布局方式、条件/循环渲染、状态管理以及Builder最后落地一个歌单列表 交互效果的小案例把知识点串起来。一、组件基础语法ArkUI 采用声明式 UI范式写界面不是一行行命令式地创建控件、设置属性而是用链式调用的方式描述UI 长什么样。一个最基础的组件结构Entry Component struct Index { build() { Column() { Text(Hello ArkUI) .fontSize(24) .fontColor(Color.Black) } .width(100%) .height(100%) } }几个关键点Component声明一个自定义组件必须配合struct使用不是class。Entry标记这是页面的入口组件一个页面只能有一个。build()方法必须实现用于描述 UI 结构里面只能写声明式的构建语句。链式调用组件后面用.连续调用的都是属性方法比如.fontSize()、.width()可以无限链下去。组件之间用花括号{}嵌套表示父子关系比如Column(){ Text(...) }表示 Text 是 Column 的子组件。二、通用属性通用属性指的是几乎所有组件都能用的属性不管是 Text、Image 还是 Button都可以设置Text(通用属性示例) .width(200) .height(50) .backgroundColor(#f5f5f5) .opacity(0.9) .visibility(Visibility.Visible) .onClick(() { console.info(点击了文本); })常见通用属性分类分类常用属性尺寸width/height/size布局padding/margin/align外观backgroundColor/opacity/border事件onClick/onTouch/onAppear三、文本属性TextText组件用来显示文字专属属性主要控制字体样式Text(这是一段文本) .fontSize(18) // 字号 .fontColor(Color.Red) // 字体颜色 .fontWeight(FontWeight.Bold) // 字重 .textAlign(TextAlign.Center) // 对齐方式 .maxLines(2) // 最大行数 .textOverflow({ overflow: TextOverflow.Ellipsis }) // 超出省略号 .lineHeight(24) // 行高需要注意的是如果文本内容较长配合.maxLines()和.textOverflow()使用可以实现类似 App 里常见的文字超出显示省略号效果。四、图像组件ImageImage组件用于加载并展示图片可以是网络图片、本地资源或$r资源引用Image($r(app.media.icon)) // 本地资源 .width(80) .height(80) .borderRadius(8) .objectFit(ImageFit.Cover) // 填充方式类似 CSS 的 object-fit Image(https://example.com/pic.png) // 网络图片 .width(100) .height(100)objectFit是比较关键的属性常见取值ImageFit.Cover等比缩放并裁剪填满容器类似头像常用ImageFit.Contain等比缩放完整展示可能留白ImageFit.Fill拉伸填满可能变形五、内外边距Padding / Margin跟 CSS 盒模型的概念基本一致padding内边距组件内容与自身边框之间的距离。margin外边距组件与外部其他组件之间的距离。Text(内外边距示例) .padding(16) // 四周内边距都是16 .margin({ top: 10, bottom: 10 }) // 只设置上下外边距 .padding({ left: 20, right: 20, top: 8, bottom: 8 }) // 分别设置一个小技巧布局对不齐的时候先检查是不是 padding/margin 设置漏了某个方向这是最常见的界面错位原因之一。六、border 属性border用来给组件设置边框可以整体设置也可以拆开单独控制圆角、宽度、颜色、样式Text(带边框的文本) .padding(10) .border({ width: 1, color: Color.Gray, radius: 8, style: BorderStyle.Solid }) // 只设置圆角常用于卡片、头像 Image($r(app.media.avatar)) .width(60) .height(60) .borderRadius(30) // 设成宽高一半即圆形七、案例歌曲列表把前面 Text、Image、padding/margin、border 串起来做一个简单的歌曲列表项Entry Component struct SongList { build() { Column() { ForEach(this.songs, (item: Song) { Row() { Image(item.cover) .width(50) .height(50) .borderRadius(6) .margin({ right: 12 }) Column() { Text(item.name) .fontSize(16) .fontWeight(FontWeight.Medium) Text(item.singer) .fontSize(12) .fontColor(Color.Gray) .margin({ top: 4 }) } .alignItems(HorizontalAlign.Start) } .width(100%) .padding(12) .border({ width: { bottom: 0.5 }, color: #eee }) }) } } songs: Song[] [ { name: 歌曲一, singer: 歌手A, cover: $r(app.media.cover1) }, { name: 歌曲二, singer: 歌手B, cover: $r(app.media.cover2) } ]; } interface Song { name: string; singer: string; cover: Resource; }这里用到了ForEach循环渲染列表下面会详细展开。八、if 分支语句ArkTS 在build()方法内也可以写普通的if / else用来做条件判断function getLevel(score: number): string { if (score 90) { return 优秀; } else if (score 60) { return 及格; } else { return 不及格; } }需要区分的是这种if/else是普通逻辑代码里的分支跟下面要讲的条件渲染在 UI 结构里用if决定渲染哪个组件是两回事容易混淆。九、条件表达式条件表达式指的是三元运算符? :一般用于给属性动态赋值比这种场景写if/else更简洁Text(this.isVip ? VIP用户 : 普通用户) .fontColor(this.isVip ? Color.Orange : Color.Black) Image(this.isLiked ? $r(app.media.heart_filled) : $r(app.media.heart_empty))在组件属性里非常常用比如根据状态切换图标、颜色、文字内容。十、条件渲染条件渲染是指在build()方法的 UI 结构里用if / else来决定要不要渲染某个组件和上面第八点的普通逻辑判断不同这里的if直接写在组件树里build() { Column() { if (this.isLogin) { Text(欢迎回来) } else { Button(请先登录) .onClick(() { this.isLogin true; }) } } }这种写法非常常见比如登录/未登录状态切换、加载中/加载完成切换不同 UI 等。十一、循环渲染ForEachForEach用于根据数组数据批量渲染组件是列表页面的核心语法ForEach( this.songs, // 数据源数组 (item: Song, index: number) { // itemGenerator生成每一项的 UI Text(item.name) }, (item: Song) item.name // keyGenerator唯一 key用于 diff )三个参数数据源一个数组。itemGenerator每一项如何渲染参数是数组元素和下标。keyGenerator推荐写为每一项生成唯一 key方便框架做高效的 diff 更新类似前端框架里v-for的key或 React 的key。十二、状态管理和事件ArkUI 的响应式更新依赖状态装饰器最基础也是最常用的是StateEntry Component struct Counter { State count: number 0; build() { Column() { Text(当前计数${this.count}) .fontSize(20) Button(加一) .onClick(() { this.count; // 修改 State 变量UI 会自动刷新 }) } } }State组件内部状态一旦被State修饰的变量发生变化界面会自动重新渲染不需要手动操作 DOMArkUI 里也没有 DOM 的概念。事件绑定统一用.onXxx(回调函数)的形式比如.onClick()、.onTouch()、.onChange()回调函数里可以直接修改State变量来驱动界面更新。除了State后续还会接触到Prop父传子、Link双向绑定等属于组件间通信的进阶内容这次先打好State这个最基础的地基。十三、BuilderBuilder用于封装可复用的 UI 片段类似于把一段重复的界面代码抽成一个自定义函数避免每个地方都写一遍Builder function SongCover(cover: Resource, size: number) { Image(cover) .width(size) .height(size) .borderRadius(size / 2) } // 使用 build() { Row() { SongCover(this.songs[0].cover, 50) SongCover(this.songs[1].cover, 40) } }也可以定义成组件内部的方法局部BuilderComponent struct SongList { Builder ItemCover(cover: Resource) { Image(cover).width(50).height(50).borderRadius(25) } build() { Column() { this.ItemCover(this.songs[0].cover) } } }Builder的好处是修改一处所有引用它的地方都同步更新非常适合列表项、卡片这类重复出现的 UI 结构。十四、歌单交互效果结合状态管理给上面的歌曲列表加一个点击收藏的交互效果Entry Component struct SongList { State songs: Song[] [ { name: 歌曲一, singer: 歌手A, cover: $r(app.media.cover1), liked: false }, { name: 歌曲二, singer: 歌手B, cover: $r(app.media.cover2), liked: false } ]; build() { Column() { ForEach( this.songs, (item: Song, index: number) { Row() { Image(item.cover) .width(50) .height(50) .borderRadius(6) .margin({ right: 12 }) Column() { Text(item.name).fontSize(16) Text(item.singer).fontSize(12).fontColor(Color.Gray) } .alignItems(HorizontalAlign.Start) .layoutWeight(1) // 占据剩余空间让收藏按钮靠右 Image(item.liked ? $r(app.media.heart_filled) : $r(app.media.heart_empty)) .width(24) .height(24) .onClick(() { // 点击切换收藏状态 this.songs[index].liked !this.songs[index].liked; }) } .width(100%) .padding(12) }, (item: Song) item.name ) } } } interface Song { name: string; singer: string; cover: Resource; liked: boolean; }这个小案例里综合用到了Row/Column布局、Image通用属性、ForEach循环渲染、State状态管理、条件表达式切换爱心图标、事件绑定点击收藏基本把这一阶段学的知识点都串联了起来。十五、小结这一阶段主要收获是搞懂了 ArkUI 声明式布局的核心思路用嵌套的组件树描述界面用状态驱动界面自动更新而不是手动操作某个具体的 UI 元素。整理下重点脉络静态结构组件语法 → 通用属性 → 文本/图片属性 → padding/margin/border这些解决界面长什么样。动态逻辑if 分支、条件表达式、条件渲染、循环渲染这些解决什么时候显示什么。交互能力状态管理State 事件绑定解决用户操作后界面怎么变。复用能力Builder解决重复 UI 怎么抽象复用。下一步计划继续学习Prop、Link等组件通信方式把单组件的交互扩展到多组件协作的场景。本文为个人学习笔记整理如有理解偏差欢迎指正交流。