You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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>

排查过程

  1. 将ScaleGestureDetector移到父Activity中调用时功能正常,但需将Detector设为public,不符合编码规范。
  2. 对比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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:17:09