【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%')
    }
  }
}

点击加号可以添加货物

相关推荐
HwJack203 分钟前
HarmonyOS响应式布局与窗口监听:让界面像呼吸般灵动的艺术
ubuntu·华为·harmonyos
是翔仔呐3 分钟前
第11章 显示外设驱动:I2C协议OLED屏、SPI协议LCD屏字符/图片/中文显示
c语言·开发语言·stm32·单片机·嵌入式硬件·学习·gitee
_李小白5 分钟前
【AI大模型学习笔记之平台篇】第五篇:Trae常用模型介绍与性能对比
人工智能·笔记·学习
承渊政道24 分钟前
【优选算法】(实战体会位运算的逻辑思维)
数据结构·c++·笔记·学习·算法·leetcode·visual studio
AI-Ming1 小时前
程序员转行学习 AI 大模型: 踩坑记录:服务器内存不够,程序被killed
服务器·人工智能·python·gpt·深度学习·学习·agi
m0_716765231 小时前
C++提高编程--STL常用容器(set/multiset、map/multimap容器)详解
java·开发语言·c++·经验分享·学习·青少年编程·visual studio
2501_945318491 小时前
零基础学习AI的选型指南:CAIE认证与编程型AI认证如何取舍
人工智能·学习
承渊政道1 小时前
【优选算法】(实战推演模拟算法的蕴含深意)
数据结构·c++·笔记·学习·算法·leetcode·排序算法
王码码20351 小时前
Flutter 组件 inappwebview_cookie_manager 适配 鸿蒙Harmony 实战 - 驾驭核心大 Web 容器缓存隧道、构建金融级政企应用绝对防串号跨域大隔离基座
flutter·harmonyos·鸿蒙·openharmony·inappwebview_cookie_manager
左手厨刀右手茼蒿1 小时前
Flutter 组件 ews 的适配 鸿蒙Harmony 实战 - 驾驭企业级 Exchange Web Services 协议、实现鸿蒙端政企办公同步与高安通讯隔离方案
flutter·harmonyos·鸿蒙·openharmony