【HarmonyOS】HarmonyOS NEXT学习日记:五、交互与状态管理

【HarmonyOS】HarmonyOS NEXT学习日记:五、交互与状态管理

在之前我们已经学习了页面布局相关的知识,绘制静态页面已经问题不大。那么今天来学习一下如何让页面动起来、并且结合所学完成一个代码实例。

交互

如果是为移动端开发应用,那么交互上用的最多的就是触屏事件。当然ArkUI也提供了键鼠事件、焦点事件、拖拽事件等。不过我认为用到的时候再看文档就行,事件这个东西一通百通,所以这里只介绍几个常用的事件。

onClick

点击事件是指通过手指或手写笔做出一次完整的按下和抬起动作。当发生点击事件时,会触发以下回调函数

用法:onClick(event: (event?: ClickEvent) => void)

event参数提供点击事件相对于窗口或组件的坐标位置,以及发生点击的事件源

我们写一个示例,点击按钮操作一个数字

ts 复制代码
  @State num: number = 0

  build() {
    Column({space: 10}){
      Text(`数字:${this.num}`)
      Button('数字+1').onClick(()=>{
        this.num++
      })
      Button('数字-1').onClick(()=>{
        this.num--
      })
    }
    .width('100%')
    .alignItems(HorizontalAlign.Center)
    }

点击数字+1按钮则数字变大1

触摸事件

当手指或手写笔在组件上触碰时,会触发不同动作所对应的事件响应,包括按下(Down)、滑动(Move)、抬起(Up)事件

用法:onTouch(event: (event?: TouchEvent) => void)

  • event.type为TouchType.Down:表示手指按下。
  • event.type为TouchType.Up:表示手指抬起。
  • event.type为TouchType.Move:表示手指按住移动。
ts 复制代码
@State eventType: string = ''

  build() {
    Column({space: 10}){
      Text(`eventType:${this.eventType}`)
      Button('触摸我').onTouch((event) => {
        if(event) {
          if (event.type === TouchType.Down) {
            this.eventType = 'Down';
          }
          if (event.type === TouchType.Up) {
            this.eventType = 'Up';
          }
          if (event.type === TouchType.Move) {
            this.eventType = 'Move';
          }
        }
      })
    }
    .width('100%')
    .alignItems(HorizontalAlign.Center)
    }

手在按钮上按下时

手在按钮上移动时

手松开按钮后

状态管理

但如果希望构建一个动态的、有交互的界面,就需要引入"状态"的概念。

事实上之前我们已经多次用到了这个概念,点击按钮改变一个字符串并让他展示在页面上,这个字符串就是一个状态。

即:点击交互触发了文本状态变更,状态变更引起了UI渲染

我们将写组件时用到的变量分为以下两种

  • 普通变量:只能在初始化时渲染,后续将不会再刷新。
  • 状态变量:需要装饰器装饰,改变会引起 UI 的渲染刷新 (必须设置类型 和初始值)

不论是哪种变量,只要是定义在组件内,在使用的时候,都需要通过 this 访问。

bash 复制代码
@Entry
@Component
struct Index {
  @State str1: string = 'str1'
  str2: string = 'str2'

  build() {
    Column() {
      Text(this.str1)
      Text(this.str2)
    }
  }
}

实践-购物车

结合前面学的布局知识,和今天的交互、状态管理实现一个移动端商城的购物车。可以点击商品的+、-来为购物车添加商品。

typescript 复制代码
interface Commodity {
  img: Resource,
  name: string,
  introduce: string,
  oldPrice: number,
  price: number,
  num: number,
}

@Entry
@Component
struct Index {
  @State Dog:Commodity={
    img: $r('app.media.test2'), // 商品图片资源
    name: '狗头', // 商品名称
    introduce: '这是一个滑稽的狗头', // 商品介绍
    oldPrice: 99, // 商品原价
    price: 9.9, // 商品现价
    num: 0, // 商品数量
  }

  build() {
    Column() {
      // 滚动视图
      Scroll(){
        Row(){
          // 商品图片
          Image(this.Dog.img)
            .size({
              width: 120,
              height: 80
            })
            .borderRadius(10)
            .margin({
              right: 10
            })
          
          // 商品信息列
          Column(){
            // 商品名称
            Row(){
              Text(this.Dog.name)
                .fontSize(18)
                .fontColor('#333')
            }
            // 商品介绍
            Row(){
              Text(this.Dog.introduce)
                .fontSize(16)
                .fontColor('#aaa')
                .lineHeight(30)
            }
            // 商品价格与操作
            Row(){
              Text(`¥${this.Dog.price}`)
                .fontSize(22)
                .fontWeight(FontWeight.Bold)
                .fontColor(Color.Red)
              
              Text(`¥${this.Dog.oldPrice}`)
                .fontSize(18)
                .fontColor('#999')
                .margin({left: 10})
                .decoration({
                  type: TextDecorationType.LineThrough
                })
              
              // 增加商品数量按钮
              Text('+')
                .width(15)
                .height(20)
                .margin({
                  left:30
                })
                .border({
                  width: 1,
                  color: '#aaa',
                  style: BorderStyle.Solid,
                  radius: {
                    topLeft:3,
                    bottomLeft:3
                  }
                })
                .fontColor('#333')
                .fontSize(16)
                .fontWeight(FontWeight.Bold)
                .textAlign(TextAlign.Center)
                .onClick(()=>{
                  this.Dog.num++
                })
              
              // 显示商品数量
              Text(this.Dog.num+'')
                .height(20)
                .border({
                  width: 1,
                  color: '#aaa',
                  style: BorderStyle.Solid,
                })
                .padding({
                  left: 5,
                  right: 5
                })
              
              // 减少商品数量按钮
              Text('-')
                .width(15)
                .height(20)
                .border({
                  width: 1,
                  color: '#aaa',
                  style: BorderStyle.Solid,
                  radius: {
                    topRight:3,
                    bottomRight:3
                  }
                })
                .fontColor('#333')
                .fontSize(16)
                .fontWeight(FontWeight.Bold)
                .textAlign(TextAlign.Center)
                .onClick(()=>{
                  if(this.Dog.num >= 1)
                    this.Dog.num--
                })
            }
          }
          .alignItems(HorizontalAlign.Start)
          .layoutWeight(1) // 设置列布局的权重
        }
        .alignItems(VerticalAlign.Top)
        .width('100%')
        .backgroundColor('#ddd')
        .padding(20)
      }
      .backgroundColor('#eee')
      .align(Alignment.Top)
      .layoutWeight(1) // 设置滚动视图的布局权重
      
      // 底部结算栏
      Row(){
        Column(){
          // 选购数量与总价
          Row(){
            Text(`已选${this.Dog.num}}件,`)
              .fontColor('#666')
            Text('合计:')
            Text(`¥${this.Dog.num * this.Dog.price}`)
              .fontColor(Color.Red)
          }
          // 显示优惠金额
          Row(){
            Text(`共减¥${(this.Dog.oldPrice - this.Dog.price) * this.Dog.num}`)
              .fontSize(14)
              .lineHeight(18)
              .fontColor(Color.Red)
          }
        }
        .alignItems(HorizontalAlign.End)
        .layoutWeight(1)
        
        // 结算按钮
        Button('结算外卖')
          .margin({
            left: 20
          })
      }
      .height(100)
      .padding(20)
      .backgroundColor(Color.White)
      .width('100%')
    }
  }
}

点击加号可以添加货物

相关推荐
陈无左耳、3 小时前
HarmonyOS学习第2天: 解锁语言与框架的无限可能
学习·华为·harmonyos
HMS Core3 小时前
【FAQ】HarmonyOS SDK 闭源开放能力 —Live View Kit (1)
华为·harmonyos
Huang兄3 小时前
鸿蒙-如何发布一个三方库
华为·harmonyos
敢嗣先锋3 小时前
鸿蒙5.0实战案例:基于自定义注解和代码生成实现路由框架
移动开发·harmonyos·arkui·组件化·鸿蒙开发·路由框架
朝九晚五ฺ3 小时前
【Linux探索学习】第三十弹——线程互斥与同步(上):深入理解线程保证安全的机制
linux·运维·学习
Huang兄3 小时前
鸿蒙-canvas-画时钟
harmonyos
柃歌3 小时前
【UCB CS 61B SP24】Lecture 5 - Lists 3: DLLists and Arrays学习笔记
java·数据结构·笔记·学习·算法
二川bro3 小时前
HarmonyOS 开发套件 介绍 ——上篇
华为·harmonyos
剑走偏锋o.O4 小时前
MyBatis框架详解与核心配置解读
java·学习·mybatis
2025年一定要上岸4 小时前
Java EE初阶-计算机导论
学习·java-ee