1# 实现组件被点击后样式的动态变化
2
3## 场景说明
4用户在使用应用时,可通过点击、滑动、按压等方式与应用进行交互,例如通过点击选中文件、跳转页面、开关其他组件。而用户在完成点击操作时,组件样式不改变,用户可能无法确定是否点击,或无法确定实际点击的是哪个组件。因此需要在用户进行点击操作时对组件被点击组件的样式进行变化,提升用户的交互感知。
5
6本案例将通过,点击回弹效果、触摸事件、多态样式的方式实现对图片进行点击后改变图片大小,实现用户交互感知的提升。
7
8## 效果呈现
9本例最终效果图如下:
10
11![](./figures/component-pressed-UI-dynamic-change.gif)
12
13
14## 环境要求
15本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
16
17- IDE: DevEco Studio 4.0 Release
18- SDK: Ohos_sdk_public 4.0.10.13 (API Version 10 Release)
19
20## 实现思路
21本例的包含的关键操作及其实现方案如下:
22- 通过组件通用属性中的点击回弹效果属性实现。
23- 通过通用事件中的触摸事件实现。在用户手指按下时,将Image组件的宽度值变小,用户手指抬起时,将Image组件的宽度恢复到初始值。此外通过对组件添加属性动画,可实现渐变过渡效果,提升用户体验。
24- 通过UI范式中的多态样式来实现。在多态样式中,可根据组件的状态来设置不同样式。在按压态下,设置Image组件的width属性值为180,在正常态下设置组件的width属性值为200。此外通过对组件添加属性动画,可实现渐变过渡效果,提升用户体验。
25
26
27## 开发步骤
28本例详细开发步骤如下,开发步骤中仅展示相关步骤代码,全量代码请参考完整代码章节的内容:
29- 通过组件通用属性中的点击回弹效果属性实现:
30
31    该方法是组件的通用属性,组件设置该属性后,点击组件会有先缩小再恢复到初始大小的效果,可通过属性中参数设置缩放比例。
32
33    示例代码如下:
34
35    ```ts
36    Image($r('app.media.ic_preview'))
37      .width(200)
38      .margin({ top: 5, bottom: 5 })
39      .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 })
40    ```
41
42- 通用事件中的触摸事件实现:
43
44    当用户手指在组件上按下、滑动、抬起时,会触发触发触摸事件onTouch。根据事件返回的TouchEvent对象,可以确定返回触摸事件的类型,触摸手指位置相关信息等(详情查询参考中官方文档),开发者可通过不同的触摸类型,对组件属性中的参数值进行修改,以达到组件UI样式发生变化的效果。此外还可以在不同触摸类型中,调用不同的能力接口,如在抬起时,发起路由跳转。
45
46    本文中主要介绍如何在通过触摸事件改变图片大小,实现类似点击回弹的效果。因此最终实现是通过在用户在组件上按下时,改小组件的尺寸;抬起时,使得尺寸恢复到初始大小。但是如果直接改变组件尺寸大小后,组件尺寸会直接变化,用户体验不好。可通过设置组件的属性动画animation,使得组件的尺寸变化时有过渡动画效果。
47
48    示例代码如下:
49
50    ```ts
51    Image($r('app.media.ic_preview'))
52      .width(this.widthNumber)
53      .animation({ duration: 300, curve: Curve.Ease })
54      .renderFit(RenderFit.TOP_LEFT)
55      .margin({ top: 5, bottom: 5 })
56      .onTouch((event?: TouchEvent) => {
57        if (event) {
58          if (event.type === TouchType.Down) {
59            this.widthNumber = 180
60          }
61          if (event.type === TouchType.Up) {
62            this.widthNumber = 200
63          }
64        }
65      })
66    ```
67
68- 通过UI范式中的多态样式来实现:
69
70    组件的UI范式中的stateStyles多态样式,定义了组件的六种状态:focused(获焦态),normal(正常态),pressed(按压态),disable(不可用态),clicked(点击状态),selected(选中态)。开发者可以根据组件的内部状态不同 ,快速设置不同样式。需要注意的是,该方式仅可以改变组件样式,无法在该属性中调用其他能力接口。
71
72    本文中主要介绍如何在通过stateStyles多态样式改变图片大小,实现类似点击回弹的效果。设置按压态时,组件宽度为180,正常态时,组件宽度为200。此外为了提升用户体验,通过设置组件的属性动画,使得组件的尺寸变化时有过度效果。
73
74    示例代码如下:
75
76    ```ts
77    Image($r('app.media.ic_preview'))
78      .stateStyles({
79        pressed: {
80          .width(180)
81        },
82        normal: {
83          .width(200)
84        }
85      })
86      .animation({ duration: 300, curve: Curve.Ease })
87      .renderFit(RenderFit.TOP_LEFT)
88      .margin({ top: 5, bottom: 5 })
89    ```
90
91    > ![icon-danger.gif](../device-dev/public_sys-resources/icon-danger.gif) **注意:** 开发者在实际使用时需要注意将animation属性放置在stateStyles后边,不然属性动画会失效。因为本质上讲stateStyles是直接给组件设置样式,而animation只会对在其之上的属性变化产生动画效果。
92
93
94
95## 完整代码
96完整代码如下:
97```ts
98@Entry
99@Component
100struct Index {
101  @State widthNumber: number = 200
102
103  build() {
104    Column() {
105      Column() {
106        Text("通用属性: 点击回弹效果").margin({ top: 5, bottom: 5 }).fontSize(18)
107        Image($r('app.media.ic_preview'))
108          .width(200)
109          .margin({ top: 5, bottom: 5 })
110          .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 })
111      }.margin({ top: 20, bottom: 20 }).height("20%").width("100%")
112
113      Column() {
114        Text("通用事件: 触摸事件").margin({ top: 5, bottom: 5 }).fontSize(18)
115        Column() {
116          Image($r('app.media.ic_preview'))
117            .width(this.widthNumber)
118            .renderFit(RenderFit.TOP_LEFT)
119            .animation({ duration: 300, curve: Curve.Ease })
120            .margin({ top: 5, bottom: 5 })
121            .onTouch((event?: TouchEvent) => {
122              if (event) {
123                if (event.type === TouchType.Down) {
124                  this.widthNumber = 180
125                }
126                if (event.type === TouchType.Up) {
127                  this.widthNumber = 200
128                }
129                event.stopPropagation
130              }
131            })
132        }.height("20%").width("100%").justifyContent(FlexAlign.Center)
133      }.margin({ top: 20, bottom: 20 })
134
135      Column() {
136        Text("UI范式: stateStyles:多态样式").margin({ top: 5, bottom: 5 }).fontSize(18)
137        Column() {
138          Image($r('app.media.ic_preview'))
139            .stateStyles({
140              pressed: {
141                .width(180)
142              },
143              normal: {
144                .width(200)
145              }
146            })
147            .animation({ duration: 300, curve: Curve.Ease })
148            .renderFit(RenderFit.TOP_LEFT)
149            .margin({ top: 5, bottom: 5 })
150        }.height("20%").width("100%").justifyContent(FlexAlign.Center)
151      }.margin({ top: 20, bottom: 20 })
152    }.height("100%").width("100%").justifyContent(FlexAlign.Center)
153  }
154}
155```
156
157## 参考
158- [点击回弹效果](../application-dev/reference/apis-arkui/arkui-ts/ts-universal-attributes-click-effect.md)
159- [触摸事件](../application-dev/reference/apis-arkui/arkui-ts/ts-universal-events-touch.md)
160- [属性动画概述](../application-dev/ui/arkts-attribute-animation-overview.md)
161- [stateStyles:多态样式](../application-dev/quick-start/arkts-statestyles.md)