写点什么

HarmonyOS Next V2 状态管理实战

作者:万少
  • 2024-12-15
    江苏
  • 本文字数:6709 字

    阅读完需:约 22 分钟

HarmonyOS Next V2 状态管理实战

HarmonyOS Next V2 状态管理实战

介绍

以下案例适合刚开始手鸿蒙开发的小伙伴,有大量的最新逻辑锻炼、鸿蒙核心语法、使用最新鸿蒙的 @Local、@Computed 等装饰器来完成。


另外,考虑在学习知识的知识时候,优先关注核心功能,所以提供的布局都会适当简化,但是能保证把核心功能展示出来。


每一个案例会点出终点和核心知识,让学习者可以练习完毕,可以得到什么。

学习的路线

  1. 先看效果

  2. 复现效果

  3. 如果有对代码产生的疑问,可以在评论区内直接提出,有疑问,必回复

  4. 如果能帮助到你,就很好了。😄

点击高亮

  1. 练习基本的线性布局

  2. 练习基本的数组使用

  3. 练习列表渲染语法 ForEach

  4. 练习布局中的状态切换 三元表达式

  5. 掌握通用的点击高亮



@Entry@ComponentV2struct Index {  @Local  list: string[] = ["小明", "小红", "小黑", "小黄"]  // 声明一个数字 表示你当前选中的按钮的下标  @Local  select: number = 1
build() { Column() {

ForEach(this.list, (item: string, index: number) => { Button(item + " " + (this.select == index)) .backgroundColor(this.select == index ? "#ffcd43" : "#007dfe") .onClick(() => { this.select = index })
}) } .width("100%") .height("100%") .padding({ top: 100 })
}}
复制代码

待办列表

  1. 新手上手新的编程语言的必做案例 crud - 增删该查

  2. 练习 V2 装饰器、@Local、@Computed、事件等

  3. 打通 状态 -> UI 、 UI-> 状态 的一些交互



@ObservedV2class Task {  @Trace task: string = ""  @Trace isFinished: boolean = false}

@Entry@ComponentV2struct Index { // 任务列表 @Local list: Task[] = [ // { task: "数组学习", isFinished: true }, // { task: "函数学习", isFinished: false } ] // 输入框输入的内容 @Local inpValue: string = ""
// 未完成数量
@Computed get statistics() { let undoneNum = this.list.filter(v =>!v.isFinished).length let doneNum = this.list.length - undoneNum return [undoneNum, doneNum] }
// 清理已经完成的任务 onClear = () => { // 筛选 留下未完成 this.list = this.list.filter((v =>!v.isFinished)) } // 删除 onDelete = (index: number) => {
this.list.splice(index, 1)
}
build() { Column() { Row() { Button("清理已完成") .onClick(this.onClear) }
Row() { Text(`未完成的数量 ${this.statistics[0]}`) Text(`完成的数量 ${this.statistics[1]}`) } .width("80%") .justifyContent(FlexAlign.SpaceAround)
Row() { TextInput() .width(200) .onChange(value => {
this.inpValue = value }) Button("确认") .onClick(() => { // 熟练 探囊取物!! // 先判断当前任务有没有出现过 const isExit = this.list.some(element => element.task == this.inpValue) if (!isExit) { const p = new Task() p.task = this.inpValue this.list.push(p) } }) }
ForEach(this.list, (item: Task, index: number) => { Row() { Text(item.task) .fontColor(item.isFinished ? "#666" : "#000") .decoration({ type: item.isFinished ? TextDecorationType.LineThrough : TextDecorationType.None }) .fontStyle(item.isFinished ? FontStyle.Italic : FontStyle.Normal) .onClick(() => { this.onDelete(index) })
Button(item.isFinished ? "继续" : "完成") .backgroundColor(item.isFinished ? "#ffa601" : "#007dfe") .onClick(() => {
this.list[index].isFinished = !this.list[index].isFinished
}) } })
} .width("100%") .height("100%") .padding({ top: 100 })
}}
复制代码

B 站显示更多

  1. 练习 Flex 布局的换行

  2. 练习 Scroll 布局的水平滚动

  3. 练习绝对定位-水平居中

  4. 练习条件渲染



@Entry@ComponentV2struct Index {  @Local  list: string[] =    ["首页", "动画", "番剧", "国创", "音乐", "舞蹈", "游戏", "知识", "科技", "运动", "汽车", "生活", "美食", "动物圈",      "鬼畜", "时尚", "娱乐", "影视", "纪录片", "电影", "电视剧", "直播", "课堂"]  // 是否显示更多  @Local  isShowMore: boolean = false
build() { Column() { Row({ space: 5 }) { Scroll() { // true 换行 Flex({ wrap: this.isShowMore ? FlexWrap.Wrap : FlexWrap.NoWrap }) { ForEach(this.list, (item: string) => { Text(item) .margin(10) }) } }
.scrollable(ScrollDirection.Horizontal) .layoutWeight(1) // .backgroundColor(Color.Yellow) .padding({ bottom: this.isShowMore ? 30 : 0 })
if (this.isShowMore) { Image($r("app.media.app_icon")) .width(20) .position({ left: "50%", bottom: 0 }) .translate({ x: -10 }) .onClick(() => { this.isShowMore = !this.isShowMore }) } else { Image($r("app.media.app_icon")) .width(20) .onClick(() => { this.isShowMore = !this.isShowMore }) }
} .width("100%") .backgroundColor(Color.Red)
} .width("100%") .height("100%")
}}
复制代码

仿考研日程

  1. 练习如何根据需求来拆分数据

  2. 简单的渲染



// 二级目录interface SubContent {  subTitle: string  subContent: string}
// 一级目录interface OneContent { title: string content: SubContent[]}

@Entry@ComponentV2struct Index { @Local list: OneContent[] = [ { title: "统考", content: [ { subTitle: "国家线", subContent: "2024。。。。" }, { subTitle: "考研复试流程图", subContent: "" } ] }, { title: "统考22", content: [ { subTitle: "国家线22", subContent: "2024。。。。22" }, { subTitle: "考研复试流程图22", subContent: "" } ] } ] // 选中标题的下标 @Local current: number = 0
build() {
Column() { // 1 标题 Row({ space: 10 }) { ForEach(this.list, (item: OneContent, index: number) => { Text(item.title) .fontColor(this.current == index ? "#0094ff" : "#000") .onClick(() => { this.current = index }) }) }
// 2 内容 Column() { ForEach(this.list[this.current].content, (item: SubContent) => { Column() { Text(item.subTitle) Text(item.subContent) } }) }
} .width("100%") .height("100%") .backgroundColor("#eee")
}}
复制代码

仿 vantUI -倒计时

  1. 练习定时器

  2. 练习一点关于时间处理的逻辑功能



@Entry@ComponentV2struct Index {  @Local  str: string = ""  // 时间 毫秒  time: number = 5 * 60 * 60 * 1000  tid: number = -1
build() {
Column() { Button("开始倒计时") .onClick(() => { // setInterval 时间间隔最少 是10ms this.tid = setInterval(() => { this.time -= 10 // 计算小时 整数 parseInt Math.floor() // parseInt 只能传递字符串类型 const hour = Math.floor(this.time / 1000 / 60 / 60) const minute = Math.floor(this.time / 1000 / 60 % 60) const seconde = Math.floor(this.time / 1000 % 60) const milliSeconde = this.time % 1000 this.str = `${hour}:${minute}:${seconde}.${milliSeconde}`

}, 10) })
Button("暂停") .onClick(() => { clearInterval(this.tid) }) Text(this.str) .fontSize(30) } }}
复制代码

仿掘金抽奖

  1. 练习 flex 布局-换行

  2. 练习随机数

  3. 练习数组+随机数实现随机获取元素



@Entry@ComponentV2struct Index {  @Local  list: string[] = [    "4090",    "4399",    "大彩电",    "iphone16",    "meta70",    "Mac",    "小牛电动车",    "迪拜7日游",    "北京房子一套"  ]  @Local  current: number = 0
// 根据下标设置奖品高亮 setHighline(index: number) { this.current = index }
build() { Column() { Flex({ wrap: FlexWrap.Wrap }) { ForEach(this.list, (item: string, index: number) => { Text(item) .width("33.33%") .padding({ top: 20, bottom: 20 }) .border({ width: 1 }) .backgroundColor(this.current == index ? "#e37815" : "#fff") }) }
Button("开始抽啦") .onClick(() => {
// 开始抽奖 let tid = setInterval(() => { // 等于 数组长度范围内的随机数 const index = Math.floor(Math.random() * this.list.length)
this.setHighline(index)
}, 10)
// 开启5s种延时器 - 停止定时器 setTimeout(() => { clearInterval(tid) }, 5000) }) }
}}
复制代码

仿掘金抽奖 - 不重复抽奖

  1. 加强逻辑处理,如何实现不重复抽奖

  2. 练习一些数组的方法

  3. 练习使用 @Computed



@Entry@ComponentV2struct Index {  @Local  list: string[] = [    "4090",    "4399",    "大彩电",    "iphone16",    "meta70",    "Mac",    "小牛电动车",    "迪拜7日游",    "北京房子一套"  ]  @Local  selectedList: number[] = []  @Local  current: number = 0
// 奖池 @Computed get newList() { const newList: number[] = [] for (let index = 0; index < this.list.length; index++) { let item = this.selectedList.find(v => v === index)
if (!item) { newList.push(index) } } return newList }
// 根据下标设置奖品高亮 setHighline(index: number) { this.current = index }
build() { Column() { Flex({ wrap: FlexWrap.Wrap }) { ForEach(this.list, (item: string, index: number) => { Text(item) .width("33.33%") .padding({ top: 20, bottom: 20 }) .border({ width: 1 }) .backgroundColor( this.selectedList.includes(index) ? "#e37815" : (this.current == index ? "#e37815" : "#fff")) }) }
Button("开始抽啦") .onClick(() => {

// 开始抽奖 let tid = setInterval(() => { // 等于 数组长度范围内的随机数
const index = Math.floor(Math.random() * this.newList.length)

this.setHighline(this.newList[index])
}, 10)
// 开启5s种延时器 - 停止定时器 setTimeout(() => { clearInterval(tid) this.selectedList.push(this.current) }, 1000) }) }
}}
复制代码

仿 vantUI-分页组件-简单版本

  1. 练习基本的鸿蒙线性布局

  2. 练习条件渲染

  3. 练习逻辑能力



@Entry@ComponentV2struct Index {  @Local  list: string[] = ['1', '2', '3', '4', '5']  @Local  current: number = 4
build() { Column() { Row({ space: 2 }) { Button("上一页") .enabled(this.current != 0) .backgroundColor("#fff") .fontColor("#0094ff") .stateStyles({ disabled: { .backgroundColor("#eee") } }) .onClick(() => { this.current--
}) ForEach(this.list, (item: string, index: number) => { Button(item) .backgroundColor(this.current == index ? "#0094ff" : "#fff") .fontColor(this.current == index ? "#fff" : "#0094ff") .onClick(() => { this.current = index }) }) Button("下一页") .enabled(this.current != this.list.length - 1) .backgroundColor("#fff") .stateStyles({ disabled: { .backgroundColor("#eee") } })
.fontColor("#0094ff") .onClick(() => { this.current++ }) } } .width("100%") .height("100%") .padding({ top: 100 }) .backgroundColor("#eee") }}
复制代码

仿 vantUI-分页组件-复杂版本

  1. 练习基本的鸿蒙线性布局

  2. 练习条件渲染

  3. 练习复杂的逻辑能力



@Entry@ComponentV2struct Index {  @Local list: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]  @Local  test: number = 1  @Local  showList: number[] = [1, 2, 3]
change() { if (this.test > 1 && this.test < this.list.length) { this.showList = [this.test - 1, this.test, this.test + 1] } }
previous() { this.test-- this.change() }
next() { this.test++ this.change() }
build() {
Column() { Row() { Button("上") .enabled(this.test != 1) .backgroundColor("#ccc") .onClick(() => { this.previous() }) if (this.test > 2) { Button("...") .backgroundColor("#ccc") .onClick(() => { this.previous() }) }
ForEach(this.showList, (item: number) => { Button(item.toString()) .backgroundColor(this.test == item ? Color.Blue : "#ccc") .onClick(() => { this.test = item this.change() }) })

if (this.test < this.list[this.list.length-1] - 1) { Button("...") .backgroundColor("#ccc") .onClick(() => { this.next() }) }
Button("下") .enabled(this.test != this.list[this.list.length-1]) .backgroundColor("#ccc") .onClick(() => { this.next() }) } }
}}
复制代码

小结

如果部分内容中的图片不存在,自己随机替换即可。


发布于: 刚刚阅读数: 6
用户头像

万少

关注

还未添加个人签名 2021-12-02 加入

还未添加个人简介

评论

发布
暂无评论
HarmonyOS Next V2 状态管理实战_鸿蒙_万少_InfoQ写作社区