【HarmonyOS】HarmonyOS NEXT学习日记:五、交互与状态管理
在之前我们已经学习了页面布局相关的知识,绘制静态页面已经问题不大。那么今天来学习一下如何让页面动起来、并且结合所学完成一个代码实例。
交互
如果是为移动端开发应用,那么交互上用的最多的就是触屏事件。当然ArkUI也提供了键鼠事件、焦点事件、拖拽事件等。不过我认为用到的时候再看文档就行,事件这个东西一通百通,所以这里只介绍几个常用的事件。
onClick
点击事件是指通过手指或手写笔做出一次完整的按下和抬起动作。当发生点击事件时,会触发以下回调函数
用法:onClick(event: (event?: ClickEvent) => void)
event参数提供点击事件相对于窗口或组件的坐标位置,以及发生点击的事件源
我们写一个示例,点击按钮操作一个数字
@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:表示手指按住移动。
@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 访问。
@Entry
@Component
struct Index {
@State str1: string = 'str1'
str2: string = 'str2'
build() {
Column() {
Text(this.str1)
Text(this.str2)
}
}
}
实践-购物车
结合前面学的布局知识,和今天的交互、状态管理实现一个移动端商城的购物车。可以点击商品的+、-来为购物车添加商品。
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%')
}
}
}
点击加号可以添加货物