自定义View中ScaleGestureDetector pinch缩放失效问题排查
自定义View捏合缩放失效问题排查与解决
问题现象
实现了自定义View(SandboxView)并添加捏合缩放功能,但捏合操作无任何效果。参照示例代码实现后,View内部的ScaleGestureDetector无法检测到捏合手势、触发回调;但将ScaleGestureDetector移到父Activity中实现时功能正常。
相关代码
SandboxView类代码
public class SandboxView extends View { float scaleFactor = 1.0f; float canvasPosX = 0, canvasPosY = 0; Paint paint = new Paint(); ArrayList<Rect> effectors = new ArrayList<>(); public SandboxView(Context context) { super(context); onStart(context); } public SandboxView(Context context, AttributeSet attrs) { super(context, attrs); onStart(context); } public SandboxView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); onStart(context); } public void onStart(Context context) { mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); effectors.add(new Rect(0, 0, 10, getHeight())); effectors.add(new Rect(this.getWidth() - 10, 0, this.getWidth(), this.getHeight())); effectors.add(new Rect(0, 0, this.getWidth(), 10)); effectors.add(new Rect(0, this.getHeight() - 10, this.getWidth(), this.getHeight())); } @Override protected void onDraw(Canvas canvas) { canvas.save(); super.onDraw(canvas); canvas.scale(scaleFactor, scaleFactor, this.getWidth() / 2.0f, this.getHeight() / 2.0f); for (Rect r : effectors) { canvas.drawRect(r, paint); } canvas.restore(); } @Override public boolean onTouchEvent(MotionEvent event) { mScaleDetector.onTouchEvent(event); super.onTouchEvent(event); performClick(); return super.onTouchEvent(event); } public void scale(float f_x, float f_y, float factor) { scaleFactor *= factor; scaleFactor = Math.max(0.01f, Math.min(50.0f, scaleFactor)); canvasPosX += f_x * (1 - factor); canvasPosY += f_y * (1 - factor); invalidate(); } private ScaleGestureDetector mScaleDetector; private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { Logger.getGlobal().info(detector.getFocusX() + " " + detector.getFocusY()); scale(detector.getFocusX(), detector.getFocusY(), detector.getScaleFactor()); return true; } } @Override public boolean performClick() { return super.performClick(); } }
布局代码
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <com.example.a50beees.SandboxView android:id="@+id/sandbox_view" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
排查过程
- 将
ScaleGestureDetector移到父Activity中调用时功能正常,但需将Detector设为public,不符合编码规范。 - 对比Activity和View的
onTouchEvent接收事件,发现View仅能收到ACTION_DOWN事件,缩放过程中无法获取ACTION_MOVE事件。
解决方法
自定义View的onTouchEvent方法需要返回true,才能持续接收后续触摸事件(如ACTION_MOVE、ACTION_UP)。修改后的onTouchEvent方法如下:
@Override public boolean onTouchEvent(MotionEvent event) { mScaleDetector.onTouchEvent(event); super.onTouchEvent(event); performClick(); return true; // 改为返回true,而非super.onTouchEvent(event) }
原因:默认情况下,View.onTouchEvent未处理触摸事件时会返回false,系统会判定该View不消费事件,后续触摸事件不再传递给它。返回true表示View消费了事件,系统会继续传递后续事件,ScaleGestureDetector才能完整检测捏合手势的整个流程。
内容的提问来源于stack exchange,提问作者user23903156
相关产品推荐
相关产品推荐

