HarmonyOS Next V2 状态管理实战

2024-11-08 21:59:14
121次阅读
0个评论

HarmonyOS Next V2 状态管理实战

介绍

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

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

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

学习的路线

  1. 先看效果
  2. 复现效果
  3. 如果有对代码产生的疑问,可以在评论区内直接提出,有疑问,必回复
  4. 如果能帮助到你,就很好了。😄

点击高亮

  1. 练习基本的线性布局
  2. 练习基本的数组使用
  3. 练习列表渲染语法 ForEach
  4. 练习布局中的状态切换 三元表达式
  5. 掌握通用的点击高亮

image

@Entry
@ComponentV2
struct 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-> 状态 的一些交互

4

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


@Entry
@ComponentV2
struct 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. 练习条件渲染

3

@Entry
@ComponentV2
struct 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. 简单的渲染

2

// 二级目录
interface SubContent {
  subTitle: string
  subContent: string
}

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


@Entry
@ComponentV2
struct 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. 练习一点关于时间处理的逻辑功能

1

@Entry
@ComponentV2
struct 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. 练习数组+随机数实现随机获取元素

5

@Entry
@ComponentV2
struct 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

5

@Entry
@ComponentV2
struct 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. 练习逻辑能力

7

@Entry
@ComponentV2
struct 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. 练习复杂的逻辑能力

6

@Entry
@ComponentV2
struct 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()
          })
      }
    }

  }
}

小结

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

作者

作者:万少

链接:https://www.nutpi.net/

來源:坚果派 著作权归作者所有。

商业转载请联系作者获得授权,非商业转载请注明出处。

收藏00

登录 后评论。没有帐号? 注册 一个。

万少

  • 0回答
  • 0粉丝
  • 0关注