自定义可缩放滚动ViewGroup中子View的onTouchEvent坐标修正问询
解决方案:修正触摸坐标并优化缩放滚动实现
核心问题分析
你当前的实现仅在draw()中对Canvas做了缩放平移变换,但该变换仅作用于绘制层面,ViewGroup的触摸事件分发仍基于原始坐标系,导致子View收到的MotionEvent坐标和实际绘制位置不匹配。
一、触摸坐标修正(无需修改子View)
通过维护统一的变换矩阵,在触摸事件分发时对坐标做逆变换,让子View收到正确的触摸位置:
1. 维护变换矩阵及逆矩阵
private Matrix mTransformMatrix = new Matrix(); private Matrix mInverseMatrix = new Matrix(); // 更新变换逻辑,统一管理缩放和平移 private void updateTransform() { mTransformMatrix.reset(); // 先缩放,再平移(顺序需与draw中的操作一致) mTransformMatrix.postScale(mScaleFactor, mScaleFactor, mScaleCenter.x, mScaleCenter.y); mTransformMatrix.postTranslate(mTranslation.x * mScaleFactor, mTranslation.y * mScaleFactor); // 计算逆矩阵,用于触摸坐标转换 mTransformMatrix.invert(mInverseMatrix); }
2. 重构draw()方法,使用矩阵变换
@Override public void draw(Canvas canvas) { canvas.save(); canvas.concat(mTransformMatrix); // 用concat替代单独的scale和translate,统一应用矩阵 super.draw(canvas); canvas.restore(); }
3. 覆写dispatchTouchEvent转换坐标
遍历所有触摸指针,用逆矩阵转换坐标后再分发给子View:
@Override public boolean dispatchTouchEvent(MotionEvent event) { MotionEvent transformedEvent = MotionEvent.obtain(event); float[] coords = new float[2]; // 处理多手指坐标转换 for (int i = 0; i < transformedEvent.getPointerCount(); i++) { coords[0] = transformedEvent.getX(i); coords[1] = transformedEvent.getY(i); mInverseMatrix.mapPoints(coords); // 应用逆变换 transformedEvent.setLocation(coords[0], coords[1], i); // 更新当前指针的坐标 } boolean result = super.dispatchTouchEvent(transformedEvent); transformedEvent.recycle(); // 回收事件,避免内存泄漏 return result; }
二、多手指手势拦截
在onInterceptTouchEvent中检测多手指按下事件,直接拦截:
@Override public boolean onInterceptTouchEvent(MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_POINTER_DOWN: // 第二根手指按下,拦截后续触摸事件,不让子View处理 return true; case MotionEvent.ACTION_MOVE: // 已有两根及以上手指时,拦截滑动 if (event.getPointerCount() >= 2) { return true; } break; } return super.onInterceptTouchEvent(event); }
三、更优实现思路
如果后续需要扩展更多触摸交互(比如双击缩放、边界限制),可以基于GestureDetector和ScaleGestureDetector封装触摸逻辑,将矩阵变换、手势识别、事件拦截统一管理,代码会更易维护。
内容的提问来源于stack exchange,提问作者Aorlinn
相关产品推荐
相关产品推荐

