1# 自定义事件分发 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @jiangtao92--> 5<!--Designer: @piggyguy--> 6<!--Tester: @songyanhong--> 7<!--Adviser: @HelloCrease--> 8 9在处理触屏事件时,ArkUI会在触屏事件触发前进行按压点和组件区域的[触摸测试](../../../ui/arkts-interaction-basic-principles.md#触摸测试),收集需要响应触屏事件的组件,再基于触摸测试结果分发相应的触屏事件。在父节点,可以通过onChildTouchTest决定子节点的触摸测试方式,影响子组件的触摸测试,从而影响后续的触屏事件分发。具体影响参考[TouchTestStrategy](#touchteststrategy11枚举说明)枚举说明。 10 11> **说明:** 12> 13> - 从API version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 14> 15> - onClick和旋转、捏合手势经过自定义事件分发后,可能会因为未命中触摸热区导致事件不响应。 16 17## onChildTouchTest<sup>11+</sup> 18 19onChildTouchTest(event: (value: Array<TouchTestInfo>) => TouchResult): T 20 21当前组件通过设置回调,可自定义触摸测试并控制触摸测试中的子节点行为。 22 23**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 24 25**系统能力:** SystemCapability.ArkUI.ArkUI.Full 26 27**参数:** 28 29| 参数名 | 类型 | 必填 | 说明 | 30| ------ | ------------------------------------------ | ---- | ---------------------- | 31| event | (value: Array<[TouchTestInfo>](#touchtestinfo11)) => TouchResult | 是 | 触摸事件信息。value的值为包含子节点信息的数组。 | 32 33**返回值:** 34 35| 类型 | 说明 | 36| -------- | -------- | 37| T | 返回当前组件。 | 38 39>**说明:** 40> 41>子节点信息数组中仅包含命名节点的信息,即开发者通过id属性设置了id的节点。 42 43## TouchTestInfo<sup>11+</sup> 44 45当前屏幕触点所在组件的坐标系、id和尺寸相关信息。 46 47**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 48 49**系统能力:** SystemCapability.ArkUI.ArkUI.Full 50 51| 名称 | 类型 | 只读 | 可选 | 说明 | 52| ------------- | ------ | ------ | ------ | ---------------------------------------- | 53| windowX | number | 否 | 否| 按压点相对于窗口左上角的x轴坐标。<br />单位:vp | 54| windowY | number| 否 |否|按压点相对于窗口左上角的y轴坐标。<br />单位:vp| 55| parentX | number| 否 |否|按压点相对于父组件左上角的x轴坐标。<br />单位:vp | 56| parentY | number| 否 |否|按压点相对于父组件左上角的y轴坐标。<br />单位:vp | 57| x | number| 否 | 否|按压点相对于子组件左上角的x轴坐标。<br />单位:vp | 58| y | number| 否 |否| 按压点相对于子组件左上角的y轴坐标。<br />单位:vp | 59| rect | [RectResult](#rectresult)| 否 |否|子组件的位置和宽高。 | 60| [id](ts-universal-attributes-component-id.md) | string| 否 | 否|子组件的唯一标识。 | 61 62## RectResult 63 64位置和尺寸类型,用于描述组件的位置和宽高。 65 66**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 67 68**系统能力:** SystemCapability.ArkUI.ArkUI.Full 69 70| 名称 | 类型 | 只读 | 可选 | 说明 | 71| ------- | ------ | ----- | -------- | ---------- | 72| x | number | 否 | 否 | 水平方向横坐标。| 73| y | number | 否 | 否 | 竖直方向纵坐标。| 74| width | number | 否 | 否 | 内容宽度大小。| 75| height | number | 否 | 否 | 内容高度大小。| 76 77## TouchResult<sup>11+</sup> 78 79自定义事件分发结果,开发者通过返回结果来影响事件分发。 80 81**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 82 83**系统能力:** SystemCapability.ArkUI.ArkUI.Full 84 85| 名称 | 类型 | 只读 | 可选 | 说明 | 86| --------- | --------- | ---- |--------------------------------------- | ---- | 87| strategy | [TouchTestStrategy](#touchteststrategy11枚举说明) | 否 | 否 |事件派发策略。 | 88| id | string | 否 | 是 |子组件的唯一标识。<br>当strategy为TouchTestStrategy.DEFAULT时,id是可选的;当strategy是TouchTestStrategy.FORWARD_COMPETITION或TouchTestStrategy.FORWARD时,id是必需的(如果没有返回id,则当成TouchTestStrategy.DEFAULT处理)。 | 89 90## TouchTestStrategy<sup>11+</sup>枚举说明 91 92事件派发策略。 93 94**卡片能力:** 从API version 11开始,该接口支持在ArkTS卡片中使用。 95 96**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 97 98**系统能力:** SystemCapability.ArkUI.ArkUI.Full 99 100| 名称 | 值 |说明 | 101| ------------| ---------| ----------------------------------------- | 102| DEFAULT | 0 | 自定义分发不产生影响,系统按当前节点命中状态分发事件。 | 103| FORWARD_COMPETITION | 1 | 应用指定分发事件到某个子节点,其他兄弟节点是否分发事件交由系统决定。 | 104| FORWARD |2 | 应用指定分发事件到某个子节点,系统不再分发事件到其他兄弟节点。 | 105 106## 示例 107 108### 示例1(设置事件派发策略为FORWARD_COMPETITION) 109 110该示例点击List下方空白区域后拖动,可使List滑动。点击Button按钮时,Button会响应onClick事件。 111 112```ts 113// xxx.ets 114import { PromptAction } from '@kit.ArkUI'; 115 116@Entry 117@Component 118struct ListExample { 119 private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 120 promptAction: PromptAction = this.getUIContext().getPromptAction(); 121 @State text: string = 'Button' 122 123 build() { 124 Column() { 125 List({ space: 12, initialIndex: 0 }) { 126 ForEach(this.arr, (item: number) => { 127 ListItem() { 128 Text('Item ' + item) 129 .width('100%') 130 .height(56) 131 .fontSize(16) 132 .textAlign(TextAlign.Start) 133 }.borderRadius(24) 134 .backgroundColor(Color.White) 135 .padding({ left: 12, right: 12 }) 136 }, (item: number) => item.toString()) 137 } 138 .listDirection(Axis.Vertical) 139 .scrollBar(BarState.Off) 140 .edgeEffect(EdgeEffect.Spring) 141 .onScrollIndex((start: number, end: number) => { 142 console.info('first' + start) 143 console.info('last' + end) 144 }) 145 .onDidScroll((scrollOffset: number, scrollState: ScrollState) => { 146 console.info(`onScroll scrollState = ScrollState` + scrollState.toString() + `, scrollOffset = ` + scrollOffset) 147 }) 148 .width('100%') 149 .height('65%') 150 .id('MyList') 151 152 Button(this.text) 153 .width(312) 154 .height(40) 155 .id('MyButton') 156 .fontSize(16) 157 .fontWeight(FontWeight.Medium) 158 .margin({ top: 80 }) 159 .onClick(() => { 160 this.text = 'click the button' 161 this.promptAction.showToast({ message: 'you click the button.', duration: 3000 }) 162 }) 163 } 164 .width('100%') 165 .height('100%') 166 .backgroundColor(0xF1F3F5) 167 .justifyContent(FlexAlign.End) 168 .padding({ left: 12, right: 12, bottom: 24 }) 169 .onChildTouchTest((touchInfo) => { 170 for (let info of touchInfo) { 171 if (info.id == 'MyList') { 172 return { id: info.id, strategy: TouchTestStrategy.FORWARD_COMPETITION } 173 } 174 } 175 return { strategy: TouchTestStrategy.DEFAULT } 176 }) 177 } 178} 179``` 180 181 182 183### 示例2(设置事件派发策略为FORWARD) 184 185点击List下方空白区域后拖动,可以滑动List。点击Button按钮时,Button不会响应onClick事件。 186 187```ts 188// xxx.ets 189import { PromptAction } from '@kit.ArkUI'; 190 191@Entry 192@Component 193struct ListExample { 194 private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 195 promptAction: PromptAction = this.getUIContext().getPromptAction(); 196 @State text: string = 'Button' 197 198 build() { 199 Column() { 200 List({ space: 12, initialIndex: 0 }) { 201 ForEach(this.arr, (item: number) => { 202 ListItem() { 203 Text('Item ' + item) 204 .width('100%') 205 .height(56) 206 .fontSize(16) 207 .textAlign(TextAlign.Start) 208 }.borderRadius(24) 209 .backgroundColor(Color.White) 210 .padding({ left: 12, right: 12 }) 211 }, (item: number) => item.toString()) 212 } 213 .listDirection(Axis.Vertical) 214 .scrollBar(BarState.Off) 215 .edgeEffect(EdgeEffect.Spring) 216 .onScrollIndex((start: number, end: number) => { 217 console.info('first' + start) 218 console.info('last' + end) 219 }) 220 .onDidScroll((scrollOffset: number, scrollState: ScrollState) => { 221 console.info(`onScroll scrollState = ScrollState` + scrollState + `, scrollOffset = ` + scrollOffset) 222 }) 223 .width('100%') 224 .height('65%') 225 .id('MyList') 226 227 Button(this.text) 228 .width(312) 229 .height(40) 230 .id('MyButton') 231 .fontSize(16) 232 .fontWeight(FontWeight.Medium) 233 .margin({ top: 80 }) 234 .onClick(() => { 235 this.text = 'click the button' 236 this.promptAction.showToast({ message: 'you click the button.', duration: 3000 }) 237 }) 238 } 239 .width('100%') 240 .height('100%') 241 .backgroundColor(0xF1F3F5) 242 .justifyContent(FlexAlign.End) 243 .padding({ left: 12, right: 12, bottom: 24 }) 244 .onChildTouchTest((touchInfo) => { 245 for (let info of touchInfo) { 246 if (info.id == 'MyList') { 247 return { id: info.id, strategy: TouchTestStrategy.FORWARD } 248 } 249 } 250 return { strategy: TouchTestStrategy.DEFAULT } 251 }) 252 } 253} 254``` 255 256 257 258### 示例3(设置事件派发策略为DEFAULT) 259 260点击List下方空白区域后拖动,List不会滑动。点击Button按钮时,Button会响应onClick事件。 261 262```ts 263// xxx.ets 264import { PromptAction } from '@kit.ArkUI'; 265 266@Entry 267@Component 268struct ListExample { 269 private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 270 promptAction: PromptAction = this.getUIContext().getPromptAction(); 271 @State text: string = 'Button' 272 273 build() { 274 Column() { 275 List({ space: 12, initialIndex: 0 }) { 276 ForEach(this.arr, (item: number) => { 277 ListItem() { 278 Text('Item ' + item) 279 .width('100%') 280 .height(56) 281 .fontSize(16) 282 .textAlign(TextAlign.Start) 283 }.borderRadius(24) 284 .backgroundColor(Color.White) 285 .padding({ left: 12, right: 12 }) 286 }, (item: number) => item.toString()) 287 } 288 .listDirection(Axis.Vertical) 289 .scrollBar(BarState.Off) 290 .edgeEffect(EdgeEffect.Spring) 291 .onScrollIndex((start: number, end: number) => { 292 console.info('first' + start) 293 console.info('last' + end) 294 }) 295 .onDidScroll((scrollOffset: number, scrollState: ScrollState) => { 296 console.info(`onScroll scrollState = ScrollState` + scrollState.toString() + `, scrollOffset = ` + scrollOffset) 297 }) 298 .width('100%') 299 .height('65%') 300 .id('MyList') 301 302 Button(this.text) 303 .width(312) 304 .height(40) 305 .id('MyButton') 306 .fontSize(16) 307 .fontWeight(FontWeight.Medium) 308 .margin({ top: 80 }) 309 .onClick(() => { 310 this.text = 'click the button' 311 this.promptAction.showToast({ message: 'you click the button.', duration: 3000 }) 312 }) 313 } 314 .width('100%') 315 .height('100%') 316 .backgroundColor(0xF1F3F5) 317 .justifyContent(FlexAlign.End) 318 .padding({ left: 12, right: 12, bottom: 24 }) 319 .onChildTouchTest((touchInfo) => { 320 return { strategy: TouchTestStrategy.DEFAULT } 321 }) 322 } 323} 324``` 325 326