1# 如何为组件的不同触摸区域添加不同交互动作
2
3## 场景说明
4应用开发中经常需要给同一个组件划分不同的触摸热区,并且不同热区触发的操作也不同,比如阅读应用通常包含左右两个触摸热区,用户触摸左侧触发向后翻页,触摸右侧触发向前翻页;同样的,视频应用中,长按视频播放器的左侧触发快退播放,长按右侧触发快进播放等等。
5当前OpenHarmony提供的热区设置属性(responseRegion)只能在不同的触摸热区中触发相同的事件,那么如何实现不同热区不同事件呢,本例即以上述视频播放场景为例进行说明。
6
7## 效果呈现
8效果说明:开始时视频以正常速度播放,播放到5秒时,长按播放器右侧触发快进播放,播放到14秒时长按播放器左侧触发快退播放。
9
10![touch-target](figures/touch-target.gif)
11
12## 环境要求
13本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
14
15- IDE: DevEco Studio 4.0 Release
16- SDK: Ohos_sdk_public 4.0.10.13 (API Version 10 Release)
17
18## 实现思路
19几乎组件的所有触摸事件都会返回event,通过返回的event可以获取到触摸点的坐标位置,那么就可以根据坐标位置为不同的组件区域添加不同的交互动作。如图:假设有一个长度为200vp的组件,我们希望点击组件的左侧时触发事件A,点击组件的右侧时触发事件B,那么就可以通过触摸点的坐标来判断,当触摸点的x坐标<=100时,触发事件A,反之触发事件B。
20
21![touch-target-zone](figures/touch-target-zone.png)
22
23本例即采用上述思路为Video组件的左右两侧添加不同的交互动作。从而实现长按视频播放器的左侧触发后退播放,长按右侧触发快进播放。
24
25## 开发步骤
26本例详细开发步骤如下,开发步骤中仅展示相关步骤代码,全量代码请参考完整代码章节的内容。
271. 通过Video组件创建视频播放器,并添加触摸手势,通过触摸控制视频的播放、暂停。
28    ```ts
29    @Component
30    export struct VideoPlayer{
31      //...
32      private isPlaying:boolean = true
33      private updateTime: number
34      private videoController:VideoController = new VideoController()
35
36      build(){
37        // 添加视频组件
38        Video({src:this.videoSrc, controller:this.videoController, previewUri:this.videoPreviewer,currentProgressRate:this.playRate})
39          //...
40          // 获取当前进度条的时间点
41          .onUpdate((e)=>{
42            this.updateTime = e.time
43          })
44          .gesture(
45            // 添加触摸手势
46            TapGesture({count:1})
47              .onAction((event:GestureEvent)=>{
48                if (this.isPlaying){
49                  // 触摸触发播放
50                  this.videoController.start()
51                  this.isPlaying = !this.isPlaying
52                } else {
53                  // 再次触摸触发暂停
54                  this.videoController.pause()
55                  this.isPlaying =!this.isPlaying
56                }
57              })
58          )
59      }
60    }
61    ```
622. 为Video组件添加长按手势,通过长按手势触发播放的快退和快进动作。由于触摸手势和长按手势需要互斥,即一次只能触发一种手势,所以通过GestureGroup来实现手势的互斥。
63    ```ts
64    .gesture(
65      // 通过GestureGroup的GestureMode.Exclusive参数控制手势互斥
66      GestureGroup(GestureMode.Exclusive,
67        // 添加触摸手势
68        TapGesture({count:1})
69          .onAction((event:GestureEvent)=>{
70            if (this.isPlaying){
71              // 触摸触发播放
72              this.videoController.start()
73              this.isPlaying = !this.isPlaying
74            } else {
75              // 再次触摸触发暂停
76              this.videoController.pause()
77              this.isPlaying =!this.isPlaying
78            }
79          }),
80        // 添加长按手势
81        LongPressGesture({repeat:true})
82          // 长按时触发快进或快退
83          .onAction((event)=>{
84            //添加快进和快退的逻辑,通过event获取手势坐标进行判断。
85          })
86          // 长按结束后播放速度回归正常
87          .onActionEnd(()=>{
88           // 添加回归正常播放的逻辑
89          })
90      )
91    )
92    ```
933. 补充长按手势中的业务逻辑:通过event获取到触摸点的x坐标:localX,当localX>=200时,说明触摸点在组件的右侧,触发快进播放;当localX<200时,说明触摸点在左侧,触发快退播放。当触摸停止时,回归正常播放速度。
94    ```ts
95    // 添加长按手势
96    LongPressGesture({repeat:true})
97    // 长按时触发快进或快退
98    .onAction((event)=>{
99      // 获取到触摸点x坐标localX,当localX>=200时,说明触摸点在组件的右侧,触发快进播放
100      if (event.fingerList[0].localX>=200){
101        // 播放速度变为2倍速
102        this.playRate = PlaybackSpeed.Speed_Forward_2_00_X
103      }
104      // 当localX<200时,说明触摸点在左侧,触发快退播放
105      if (event.fingerList[0].localX<200){
106        if (this.intervalCount===0){
107          // 通过进度时间减小来达到快退的目的,通过setInterval来控制后退的速度,否则会连续触发后退,瞬间后退到播放起点
108          this.seekBack = setInterval(()=>{
109            this.updateTime -= 1
110            this.videoController.setCurrentTime(this.updateTime)
111          },500)
112        }
113        this.intervalCount = 1
114      }
115    })
116    // 长按结束后播放速度回归正常
117    .onActionEnd(()=>{
118      // 播放回归到1倍速
119      this.playRate = PlaybackSpeed.Speed_Forward_1_00_X
120      // 清空计时器
121      clearInterval(this.seekBack)
122      this.intervalCount = 0
123    })
124    ```
125
126
127## 完整代码
128本例完整代码如下:
129> ![icon-note.gif](../device-dev/public_sys-resources/icon-note.gif) **说明:**
130> 本例中使用的视频等资源需要替换为开发者自己的资源
131
132```ts
133@Entry
134@Component
135export struct VideoPlayer{
136  private videoSrc:Resource = $rawfile('video_1.mp4')
137  private isPlaying:boolean = true
138  private updateTime: number = 0
139  private videoPreviewer:Resource = $r('app.media.wandering_previewer')
140  private videoController:VideoController = new VideoController()
141  @State playRate:number = 1
142  private seekBack:number = 0
143  private intervalCount:number = 0
144
145  build(){
146    Column(){
147      // 添加视频组件
148      Video({src:this.videoSrc, controller:this.videoController, previewUri:this.videoPreviewer,currentProgressRate:this.playRate})
149        .width('100%')
150        .height('30%')
151        .backgroundColor('#fffff0')
152        .controls(true)
153        .objectFit(ImageFit.Contain)
154        // 获取当前进度条的时间点
155        .onUpdate((e)=>{
156          this.updateTime = e.time
157        })
158        .gesture(
159          // 通过GestureGroup的GestureMode.Exclusive参数控制手势互斥
160          GestureGroup(GestureMode.Exclusive,
161            // 添加触摸手势
162            TapGesture({count:1})
163              .onAction((event:GestureEvent)=>{
164                if (this.isPlaying){
165                  // 触摸触发播放
166                  this.videoController.start()
167                  this.isPlaying = !this.isPlaying
168                } else {
169                  // 再次触摸触发暂停
170                  this.videoController.pause()
171                  this.isPlaying =!this.isPlaying
172                }
173              }),
174            // 添加长按手势
175            LongPressGesture({repeat:true})
176              // 长按时触发快进或快退
177              .onAction((event)=>{
178                // 获取到触摸点x坐标localX,当localX>=200时,说明触摸点在组件的右侧,触发快进播放
179                if (event.fingerList[0].localX>=200){
180                  // 播放速度变为2倍速
181                  this.playRate = PlaybackSpeed.Speed_Forward_2_00_X
182                }
183                // 当localX<200时,说明触摸点在左侧,触发快退播放
184                if (event.fingerList[0].localX<200){
185                  if (this.intervalCount===0){
186                    // 通过进度时间减小来达到快退的目的,通过setInterval来控制后退的速度,否则会连续触发后退,瞬间后退到播放起点
187                    this.seekBack = setInterval(()=>{
188                      this.updateTime -= 1
189                      this.videoController.setCurrentTime(this.updateTime)
190                    },500)
191                  }
192                  this.intervalCount = 1
193                }
194              })
195                // 长按结束后播放速度回归正常
196              .onActionEnd(()=>{
197                // 播放回归到1倍速
198                this.playRate = PlaybackSpeed.Speed_Forward_1_00_X
199                // 清空计时器
200                clearInterval(this.seekBack)
201                this.intervalCount = 0
202              })
203          )
204        )
205    }
206    .height('100%')
207    .width('100%')
208  }
209}
210```
211
212## 参考
213- [Video组件指导](../application-dev/reference/apis-arkui/arkui-ts/ts-media-components-video.md)
214- [绑定手势指导](../application-dev/reference/apis-arkui/arkui-ts/ts-gesture-settings.md)
215- [组合手势的使用指导](../application-dev/reference/apis-arkui/arkui-ts/ts-combined-gestures.md)