文章目录
- 前言
- 应用场景
- onTouch 和 Gesture 的区别
- 代码详解
- 触控点接口
- 状态变量
- 信息面板
- 触控区域
- 触控点可视化
- 手指指示点
- 常见问题
- 进阶技巧
- 写在最后
前言
大部分手势 API(PanGesture、PinchGesture等)都是对底层触控事件的高层封装。但当你需要知道"用户到底用了几根手指""每根手指在哪"时,就得直接操作onTouch事件了。onTouch提供了最原始的触控数据——TouchEvent对象,今天来把这个底层 API 搞明白。
应用场景
直接用onTouch的场景通常是比较"非标准"的交互:
- 多指游戏:钢琴块、节奏大师等需要多指同时操作
- 绘图应用:多指同时绘画或擦除
- 自定义手势识别:框架提供的手势不够用,需要自己分析触控数据
- 触控点可视化:教学或演示场景,实时显示手指位置
onTouch 和 Gesture 的区别
| 特性 | onTouch(底层) | Gesture(高层) |
|------|----------------|-----------------|
| 数据粒度 | 每帧、每根手指 | 手势级别 |
| 事件类型 | Down/Move/Up/Cancel | onActionStart/Update/End |
| 手指数量 | 可获取所有触控点 | 按手势定义 |
| 适用场景 | 自定义交互 | 标准手势 |
| 开发成本 | 高 | 低 |
简单说:能用Gesture解决的别用onTouch,解决不了的再往下走一层。
代码详解
触控点接口
interfaceTouchCircleData{x:numbery:numberid:number}每个触控点记录坐标和手指 ID。id是系统分配的唯一标识,用于区分不同的手指。
状态变量
@Entry@Componentstruct MultiTouchTracker{@StatefingerCount:number=0@StatemaxFingers:number=0@StatetouchAction:string='等待触控...'@Statecircles:TouchCircleData[]=[]// ...}fingerCount:当前屏幕上的手指数maxFingers:本次会话中最多同时触摸的手指数(记录峰值)touchAction:当前触控状态的文字描述circles:触控点的可视化数据
信息面板
Row(){Text(`当前手指:${this.fingerCount}`).fontSize(15).fontWeight(FontWeight.Bold).fontColor('#4D96FF')Text('').layoutWeight(1)Text(`最多:${this.maxFingers}`).fontSize(13).fontColor('#999999')}.width('100%').margin({bottom:12})左边显示当前手指数,右边显示峰值记录。
触控区域
Stack(){Column(){Text('多指触控区域').fontSize(15).fontColor('#FFFFFF')Text(`${this.fingerCount>0?`${this.fingerCount}指触控`:'请用多指触摸'}`).fontSize(12).fontColor('#FFFFFF').opacity(0.8)}.width('100%').height('100%').backgroundColor('#4ECDC4').borderRadius(16).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).onTouch((event:TouchEvent)=>{if(event.type===TouchType.Down||event.type===TouchType.Move){this.fingerCount=event.touches.lengthif(this.fingerCount>this.maxFingers){this.maxFingers=this.fingerCount}this.touchAction=`${this.fingerCount}指触控中`}elseif(event.type===TouchType.Up){this.touchAction=`松手 (最后${this.fingerCount}指)`this.fingerCount=0}})}.width('100%').height(200)核心是.onTouch回调。TouchEvent的关键属性:
| 属性 | 类型 | 说明 |
|---|---|---|
event.type | TouchType | 事件类型:Down/Move/Up/Cancel |
event.touches | Touch[] | 当前屏幕上所有触控点的数组 |
event.changedTouches | Touch[] | 本次变化涉及的触控点 |
每个Touch对象包含x、y(坐标)和id(手指标识)。
Down / Move 处理
if(event.type===TouchType.Down||event.type===TouchType.Move){this.fingerCount=event.touches.lengthif(this.fingerCount>this.maxFingers){this.maxFingers=this.fingerCount}this.touchAction=`${this.fingerCount}指触控中`}event.touches.length就是当前屏幕上的手指数。Down 和 Move 都会更新这个数字。
Up 处理
elseif(event.type===TouchType.Up){this.touchAction=`松手 (最后${this.fingerCount}指)`this.fingerCount=0}手指抬起时重置计数。注意:多指场景中,Up事件可能只抬起一根手指,event.touches里还有其他手指。这里的实现做了简化。
触控点可视化
ForEach(this.circles,(item:TouchCircleData)=>{Circle({width:20,height:20}).fill('#FFFFFF50').position({x:item.x-10,y:item.y-10})})在每个触控点位置画一个半透明白色圆点。x - 10, y - 10是因为position指定的是左上角,需要减去半径来居中。
手指指示点
Row(){this.fingerDot(1,this.fingerCount>=1)this.fingerDot(2,this.fingerCount>=2)this.fingerDot(3,this.fingerCount>=3)this.fingerDot(4,this.fingerCount>=4)this.fingerDot(5,this.fingerCount>=5)}.width('100%').justifyContent(FlexAlign.SpaceEvenly).margin({top:16})@BuilderfingerDot(index:number,active:boolean){Circle({width:20,height:20}).fill(active?'#4D96FF':'#E0E0E0')}底部 5 个圆点指示当前手指数。fingerCount >= N的方式让前 N 个点亮,很直观的进度表示。
@Builder装饰器允许把一部分 UI 抽取为可复用的构建函数,这里用来生成手指指示点。
常见问题
Q:onTouch 和 onClick 会冲突吗?
会。如果同时绑定了onTouch和onClick,onTouch的 Down 事件先触发。如果在onTouch里处理了事件(返回true),onClick可能不会触发。
Q:最多能追踪几根手指?
HarmonyOS 支持最多 10 个触控点(10 指触控),但大多数设备硬件上支持 5 指。
Q:TouchEvent 的坐标是相对还是绝对?
event.touches[i].x/y是相对于组件左上角的坐标,不是屏幕绝对坐标。
多指触控的调试比较麻烦——DevEco Studio 的模拟器只支持单指。测试多指功能必须用真机,没有替代方案。
进阶技巧
追踪每根手指的轨迹:用
id做 key,维护一个Map<number, Path>来分别记录每根手指的移动路径。自定义手势识别:分析多指触控的模式(比如三指下滑、四指捏合),实现框架没有内置的手势。
触控热力图:记录所有触控点的坐标,绘制热力图来分析用户的操作习惯。
写在最后
onTouch是 ArkUI 触控系统的"底层 API",提供了最原始的手指位置、数量、状态信息。大多数时候我们用不上它,但在需要精细控制触控行为的场景里,它是不可替代的。理解了TouchEvent的数据结构,不管是做多指游戏、自定义手势还是触控可视化,都有了基础。