缩放和平移画布后更正触摸事件的坐标

Zoe*_*Zoe -2 android zooming android-canvas

使用缩放时,MotionEvent 坐标可以通过除以 ScaleFactor 来校正。

此外,在缩放和平移时,除以比例因子并减去偏移量。

然而,在处理缩放时,就没有那么容易了。除法确实得到了正确的相对坐标,但因为涉及平移,0 不是 0。0 可以是 -2000 的偏移量。

那么如何在缩放和平移后更正 TouchEvents 以提供正确的坐标?

代码:

飞涨:

class Scaler extends ScaleGestureDetector {
    public Scaler(Context context, OnScaleGestureListener listener) {
        super(context, listener);
    }

    @Override
    public float getScaleFactor() {
        return super.getScaleFactor();
    }
}
class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener{

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scaleFactor *= detector.getScaleFactor();

        if(scaleFactor > 2) scaleFactor = 2;
        else if(scaleFactor < 0.3f) scaleFactor = 0.3f;
        scaleFactor = ((float)((int)(scaleFactor * 100))) / 100;//jitter-protection
        scaleMatrix.setScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {return true;}

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {

        System.out.println("ScaleFactor: " + scaleFactor);
    }
}
Run Code Online (Sandbox Code Playgroud)

触摸事件:

@Override
public boolean onTouchEvent(MotionEvent ev) {

    int pointers = ev.getPointerCount();

    if(pointers == 2 ) {
        zoom = true;
        s.onTouchEvent(ev);
    }else if(pointers == 1 && zoom){
        if(ev.getAction() == MotionEvent.ACTION_UP)
            zoom = false;
        return true;
    }else {

        if (ev.getAction() == MotionEvent.ACTION_DOWN) {

            //scaled physical coordinates
            x = ev.getX() /*/ mScaleFactorX*/;//unscaled
            y = ev.getY() /*/ mScaleFactorY*/;
            sx = ev.getX() / scaleFactor;//scaled
            sy = ev.getY() / scaleFactor;
            //////////////////////////////////////////
            tox = toy = true;


        } else if (ev.getAction() == MotionEvent.ACTION_UP) {

            if (tox && toy) {
                x = ev.getX() /*/ mScaleFactorX*/;
                y = ev.getY() /*/ mScaleFactorY*/;
                sx = ev.getX() / scaleFactor;
                sy = ev.getY() / scaleFactor;
                System.out.println("XY: " + sx + "/" + sy);
                Rect cursor = new Rect((int) x, (int) y, (int) x + 1, (int) y + 1);
                Rect scaledCursor = new Rect((int)sx, (int)sy, (int)sx+1, (int)sy+1);
                ...
            }

        } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
            //This is where the pan happens. 

            float currX = ev.getX() / scaleFactor;
            float currY = ev.getY() / scaleFactor;

            float newOffsetX = (sx - currX),
                    newOffsetY = (sy - currY);

            if (newOffsetY < Maths.convertDpToPixel(1, c) && newOffsetY > -Maths.convertDpToPixel(1, c))
                newOffsetY = 0;
            else tox = false;

            if (newOffsetX < Maths.convertDpToPixel(1, c) && newOffsetX > -Maths.convertDpToPixel(1, c))
                newOffsetX = 0;
            else toy = false;
            this.newOffsetX = newOffsetX;
            this.newOffsetY = newOffsetY;
            offsetX += newOffsetX;
            offsetY += newOffsetY;
            sx = ev.getX() / scaleFactor;
            sy = ev.getY() / scaleFactor;
        }

    }
    return true;

}
Run Code Online (Sandbox Code Playgroud)

缩放矩阵的实现:

Matrix scaleMatrix = new Matrix();
public void render(Canvas c) {
    super.draw(c);

    if (c != null) {
        backgroundRender(c);
        c.setMatrix(scaleMatrix);
        //Example rendering:
        c.drawRect(0 - offsetX,0 - offsetY,10 - offsetX,10 - offsetY,paint);
        c.setMatrix(null);//null the matrix to allow for unscaled rendering after this line. For UI objects.

    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,缩放 0 时会发生变化,但对象的坐标不会发生变化。意思是在例如 -2500、-2500 渲染的对象将显示为超过 0,0。它们的坐标与 TouchEvent 不同。那么如何纠正触摸事件呢?

我尝试过的:

这会导致延迟缩放和物体飞走。ev = onTouchEvent 中的 MotionEvent。不修正坐标

Matrix invert = new Matrix(scaleMatrix);
invert.invert(invert);
ev.transform();
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为与对象相比坐标是错误的。坐标 < 0 的对象显示超过 0 意味着 MotionEvents 无论如何都是错误的。

int sx = ev.getX() / scaleFactor;//same with y, but ev.getY()
Run Code Online (Sandbox Code Playgroud)

Zoe*_*Zoe 5

经过大量研究后找到了解决方案

每当获取缩放坐标时,获取画布的 clipBounds 并将顶部和左侧坐标添加到 X/Y 坐标:

sx = ev.getX() / scaleFactor + clip.left;
sy = ev.getY() / scaleFactor + clip.top ;
Run Code Online (Sandbox Code Playgroud)

clip 是一个 Rect,定义为 Canvas 的 clipBounds。

public void render(Canvas c) {
        super.draw(c);

    if (c != null) {
        c.setMatrix(scaleMatrix);
        clip = c.getClipBounds();
        (...)
    }
}
Run Code Online (Sandbox Code Playgroud)