鸿蒙开发 W4 手把手入门:状态管理与交互
本文是《鸿蒙开发小白入门学习计划.md》中"状态管理与交互"周(W4)的单独扩写版,沿用"第一行代码"式手把手写法。
主计划表保持精简,本周的详细操作步骤、代码示例与参考链接集中放在本文,便于单独查阅与 CSDN 发布。
前置:已完成 W1(环境跑通)+ W2(ArkTS 语法)+ W3(ArkUI 组件与布局)。本周在 W3 项目里加"交互与状态"。
一、状态管理与交互(手把手操作步骤)
本节采用"第一行代码"式的手把手写法:每条都给可运行代码 + 验证方法。跟着敲,约 4–5 小时能吃透状态装饰器与基本交互。
W4 是 ArkUI 的"分水岭"——学会@State之后,你的页面才真正"活"起来(数据变了 UI 自动变)。
学习目标
- 理解"状态驱动 UI":数据变 → 框架自动重绘,不用手动
setText - 掌握 5 组状态装饰器(
@State/@Prop/@Link/@Provide-@Consume/@Observed-@ObjectLink)的选用 - 会用
onClick/onChange/onTouch处理交互 - 会用
if/else控制显隐、ForEach渲染列表、@Builder抽复用 UI
本周工具说明(无需新装)
- 继续在 W1 项目的
entry/src/main/ets/pages/Index.ets改build()。 - 仍推荐 Previewer(预览器) 实时看效果;改状态逻辑后预览器会自动重绘。
- 关键认知:ArkUI 是声明式 + 状态驱动。你只声明"UI 长什么样(依赖哪些状态)",数据一变框架重算并刷新——这和 Android 老的
findViewById().setText()思路完全不同,先转过弯来。
步骤一、认识 @State:让数据驱动 UI
@State 是组件内私有、可变的 UI 状态。它一变,用到它的 UI 自动重绘。
// Index.ets
@Entry
@Component
struct Index {
@State count: number = 0 // ① 用 @State 声明状态
build() {
Column() {
Text(`当前计数:${this.count}`) // ② 绑定状态
.fontSize(20)
Button('加一')
.onClick(() => { // ③ 点击事件改状态
this.count++ // ④ 直接改,UI 自动刷新
})
.margin({ top: 12 })
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Center)
}
}
验证:预览器点"加一",数字实时 +1。若数字不变,检查 count 是否加了 @State(漏写就不会重绘,这是最高频坑)。
步骤二、事件与交互三件套
| 事件 | 触发时机 | 典型用法 |
|---|---|---|
onClick | 点击 | 按钮、卡片点击 |
onChange | 输入变化 | TextInput 实时取值 |
onTouch | 触摸(按下/移动/抬起) | 自定义手势、拖拽 |
TextInput({ placeholder: '请输入姓名' })
.onChange((value: string) => { // 每次输入变化都回调
console.log('输入了:' + value)
})
Button('长按区')
.onTouch((e) => {
if (e.type === TouchType.Down) { console.log('按下了') }
})
步骤三、@Prop:父传子(单向)
父组件把值传给子组件,子组件用 @Prop 接收;子改 @Prop 不影响父。
// 子组件
@Component
struct Child {
@Prop title: string = '' // 父传来的,子可改但只改自己
build() {
Text(this.title).fontSize(18)
}
}
// 父组件
@Component
struct Parent {
@State msg: string = '你好'
build() {
Column() {
Child({ title: this.msg }) // 传值
Button('父改值').onClick(() => { this.msg = '已改' })
}
}
}
坑:@Prop 是单向——父改会传下去,子改不回传。需要双向用 @Link。
步骤四、@Link:父子双向共享
父子指向同一份状态,任一方改,双方都变。
@Component
struct Child {
@Link count: number // 注意:无初始值,靠父传入引用
build() {
Button(`子改:${this.count}`)
.onClick(() => { this.count++ })
}
}
@Component
struct Parent {
@State num: number = 0
build() {
Column() {
Text(`父显示:${this.num}`)
Child({ count: this.num }) // 用 $ 传引用(语法:$变量名)
}
}
}
验证:点子组件按钮,父的"父显示"同步 +1——证明双向。传参必须写 Child({ count: this.$num })(缺 $ 会报错)。
步骤五、@Provide / @Consume:跨层级免传参
祖孙多层嵌套时,不想逐层 @Prop 传,用 @Provide(祖先发)配 @Consume(后代收),按变量名自动匹配。
@Component
struct GrandChild {
@Consume theme: string // 自动找祖先的 @Provide('theme')
build() { Text('主题:' + this.theme) }
}
@Entry
@Component
struct Index {
@Provide theme: string = '浅色' // 祖先提供
build() {
Column() {
GrandChild()
Button('换主题').onClick(() => { this.theme = '深色' })
}
}
}
适用:全局设置(主题、登录用户、语言)这类"深层都要用"的数据。
步骤六、@Observed / @ObjectLink:监听嵌套对象
普通 @State 只能监听"整个对象替换",监听不到对象内部属性变化。嵌套对象用 @Observed(类)+ @ObjectLink(子组件)解决。
@Observed
class User {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
@Component
struct Card {
@ObjectLink user: User // 接收嵌套对象
build() {
Column() {
Text(this.user.name)
Button('涨一岁').onClick(() => { this.user.age++ }) // 改内部属性,UI 会刷新
}
}
}
@Entry
@Component
struct Index {
@State u: User = new User('张三', 18)
build() {
Column() { Card({ user: this.u }) }
}
}
坑:没加 @Observed 时改 user.age 不会重绘——这是对象状态最常见的"为什么没刷新"。
步骤七、条件与循环渲染
if/else控制显隐(注意:条件渲染的组件不能带状态变量做兄弟,结构要稳定):
@State show: boolean = true
build() {
Column() {
if (this.show) {
Text('显示了')
} else {
Text('隐藏了')
}
Button('切换').onClick(() => { this.show = !this.show })
}
}
ForEach渲染列表(第二个参数是 key 生成函数,key 要唯一稳定):
@State list: string[] = ['苹果', '香蕉']
build() {
Column() {
ForEach(this.list, (item: string, index: number) => {
Text(`${index + 1}. ${item}`).fontSize(16)
}, (item: string) => item) // key 生成器
}
}
步骤八、@Builder:抽取复用 UI 片段
多处重复的 UI 用 @Builder 抽成方法,减少复制粘贴。
@Builder
itemCell(text: string) { // 定义可复用片段
Row() {
Text(text).fontSize(16)
Blank()
Image($r('app.media.arrow')).width(16).height(16)
}
.width('100%').padding(12)
}
build() {
Column() {
this.itemCell('我的订单') // 像方法一样调用
this.itemCell('我的收藏')
}
}
步骤九、综合练习(验收用)
做两个小东西,覆盖本周核心:
- 计数器:
@State+onClick自增/自减(步骤一升级版,加"减一"按钮)。 - 待办列表雏形:
@Entry
@Component
struct Index {
@State tasks: string[] = []
@State input: string = ''
build() {
Column() {
// 输入 + 添加
Row() {
TextInput({ placeholder: '输入待办', text: this.input })
.onChange((v) => { this.input = v })
.layoutWeight(1)
Button('添加').onClick(() => {
if (this.input.trim() !== '') {
this.tasks.push(this.input) // 改数组,ForEach 自动刷新
this.input = ''
}
})
}.width('100%').margin({ bottom: 12 })
// 列表渲染
ForEach(this.tasks, (t: string) => {
Text('• ' + t).fontSize(16).padding(8)
}, (t: string) => t)
}
.width('100%').height('100%').padding(16)
}
}
验收:① 点按钮数字变化并即时重绘;② 输入框打字、点"添加"后内容进列表并显示;③ 空输入不添加(trim 判断)。
必须记住的点
- ArkUI = 声明式 + 状态驱动:改
@State数据 → 框架自动重绘,别再想findViewById().setText()。 - 装饰器选型口诀:自己用→
@State;父传子单向→@Prop;父子双向→@Link;跨层全局→@Provide/@Consume;嵌套对象内部变化→@Observed/@ObjectLink。 ForEach必须给唯一稳定的 key,否则列表增删会错乱。@Link传参要写$变量名,@Prop不需要。
常见坑(提前避)
- 改了数据 UI 不变 → 90% 是没加
@State/ 忘了@Observed(嵌套对象)。 @Link传参漏写$(应为Child({ count: this.$num })),编译报错"类型不匹配"。ForEach的 key 用了数组下标index——数据增删时 UI 错位,要用内容当 key。if/else条件块里放带@State的组件做兄弟,结构不稳定导致渲染异常;保持条件分支内结构清晰。TextInput不绑定@State就取不到值——用text+onChange双向绑。@Builder里若用到外部状态,注意它默认不感知组件状态,复杂场景用@BuilderParam或抽成子组件。
实操任务:完成上面 9 步 → 做出"计数器"(加减)和"待办列表雏形"(输入+添加+列表渲染)。
验收:计数器点按即时变化;待办输入能进列表显示、空输入不添加;能把"为什么 UI 会自动刷新"讲清楚(状态驱动)。
二、参考学习链接
1. 官方下载
- DevEco Studio 官方下载(唯一地址):https://developer.huawei.com/consumer/cn/deveco-studio/
- 华为开发者账号注册:https://developer.huawei.com/consumer/cn/
- DevEco Studio 下载与安装官方说明:https://device.harmonyos.com/cn/docs/apiref/harmonyos-guides/ide-software-install
2. 官方文档
- HarmonyOS 应用开发指南(harmonyos-guides,认准 NEXT/Stage):https://developer.huawei.com/consumer/cn/doc/harmonyos-guides
- ArkUI 状态管理文档(在指南站内检索 “状态管理” / “状态装饰器” 定位 @State/@Prop/@Link/@Provide/@Observed 章节):https://developer.huawei.com/consumer/cn/doc/harmonyos-guides
- 仓颉编程语言官网:https://cangjie-lang.cn/
3. 社区与踩坑
- 华为开发者论坛:https://developer.huawei.com/consumer/cn/forum/
- CSDN 鸿蒙专区 / 51CTO 鸿蒙开发者社区:检索"ArkUI 状态管理"“@State 不刷新”"ForEach key"等
4. Markdown 写作参考(CSDN 发布用)
- Markdown 基础语法(CSDN 文章排版参考):https://blog.csdn.net/weixin_45494811/article/details/104307670
更多推荐




所有评论(0)