在Android画布上应用缩放效果后,缩放比例位置错误

时间:2016-08-26 14:43:24

标签: android android-canvas android-view android-gesture android-touch-event

首先,这是最初在此处提出的跟进问题Pan, Zoom and Scale a custom View for Canvas drawing in Android

由于还没有答案,我终于使用android-gesture-detectors

解决了我的问题

应用缩放/缩放手势后,我发现,画布绘制坐标仍然指向旧位置(在应用缩放之前),而不是绘制完全相同的触摸坐标。基本上,在缩放或拖动画布后,我无法获得正确的画布坐标。

缩放前,

enter image description here

缩小后,触摸点将绘制在之前的位置。我希望它能够在当前的触摸位置上绘制,

enter image description here

示例代码,

public class DrawingView extends View {

    private void setupDrawing() {

        mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());

        mgd = new MoveGestureDetector(ctx, mgl);
        sgd = new ScaleGestureDetector(ctx, sgl);
        rgd = new RotateGestureDetector(ctx, rgl);

}

class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
            invalidate();
            return true;
        }
    }

    MoveGestureDetector.SimpleOnMoveGestureListener mgl = new MoveGestureDetector.SimpleOnMoveGestureListener() {
        @Override
        public boolean onMove(MoveGestureDetector detector) {
            PointF delta = detector.getFocusDelta();
            matrix.postTranslate(delta.x, delta.y);
            invalidate();
            return true;
        }
    };

    ScaleGestureDetector.SimpleOnScaleGestureListener sgl = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scale = detector.getScaleFactor();
            matrix.postScale(scale, scale, detector.getFocusX(), detector.getFocusY());
            invalidate();
            return true;
        }
    };

    RotateGestureDetector.SimpleOnRotateGestureListener rgl = new RotateGestureDetector.SimpleOnRotateGestureListener() {
        @Override
        public boolean onRotate(RotateGestureDetector detector) {
            matrix.postRotate(-detector.getRotationDegreesDelta(), detector.getFocusX(), detector.getFocusY());
            invalidate();
            return true;
        }
    };

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        //view given size
        super.onSizeChanged(w, h, oldw, oldh);
        canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        drawCanvas = new Canvas(canvasBitmap);
    }

    private void touch_start(float x, float y) {
        undonePaths.clear();
        drawPath.reset();
        drawPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

    private void touch_move(float x, float y, float x2, float y2) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
           /* QUad to curves using a quadratic line (basically an ellipse of some sort).
           LineTo is a straight line. QuadTo will smooth out jaggedies where they turn.
          */
            drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }

    }

    private void touch_up() {

            drawPath.lineTo(mX, mY);
            // commit the path to our offscreen
            drawCanvas.drawPath(drawPath, drawPaint);
            // kill this so we don't double draw
            paths.add(drawPath);
            drawPath = new Path();
            drawPath.reset();
            invalidate();
    }

@Override
    public boolean onTouchEvent(MotionEvent event) {

        if (isZoomable) {
            mgd.onTouchEvent(event);
            sgd.onTouchEvent(event);
            rgd.onTouchEvent(event);
        }

        if (!isTouchable) {
            return super.onTouchEvent(event);
        } else {
            //detect user touch
            float x = event.getX();
            float y = event.getY();

            switch (event.getAction() & MotionEvent.ACTION_MASK) {

                case MotionEvent.ACTION_DOWN:
                    if (!isZoomable) {
                        touch_start(x, y);
                    }
                    invalidate();
                    break;

                case MotionEvent.ACTION_MOVE:
                    if (!isZoomable) {
                        //mPositions.add(new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2));
                        if (isCustomBrush && mBitmapBrushDimensions != null) {
                            mPositions = new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2);
                            touch_move(x, y, x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2);
                        } else {
                            touch_move(x, y, 0, 0);
                        }
                    }
                    invalidate();
                    break;

                case MotionEvent.ACTION_UP:
                    if (!isZoomable) {
                        touch_up();
                    }
                    invalidate();
                    break;
            }
            mScaleDetector.onTouchEvent(event);
            return true;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();

        canvas.setMatrix(matrix);

        for (Path p : paths) {
                canvas.drawPath(p, drawPaint);
                drawPaint.setColor(selectedColor);
                drawPaint.setStrokeWidth(brushSize);
                canvas.drawPath(drawPath, drawPaint);
        }
        canvas.restore();
    }
}

PS: MoveGestureDetector() ScaleGestureDetector()& RotateGestureDetector()是从android-gesture-detectors继承的自定义类

2 个答案:

答案 0 :(得分:2)

这就是我的所作所为。基本上,你必须找到" old"和新点。跳到最重要的线路底部...

@Override
public boolean onScale(ScaleGestureDetector detector) {

    scaleFactor *= detector.getScaleFactor();

    float xDiff = initialFocalPoints[0] - currentFocalPoints[0];
    float yDiff = initialFocalPoints[1] - currentFocalPoints[1];

    transformMatrix.setScale(scaleFactor, scaleFactor, 
                                 currentFocalPoints[0], currentFocalPoints[1]);
    transformMatrix.postTranslate(xDiff, yDiff);    
    child.setImageMatrix(transformMatrix);

    return true;
}

@Override
public boolean onScaleBegin(ScaleGestureDetector detector){

    float startX = detector.getFocusX() + getScrollX();
    float startY = detector.getFocusY() + getScrollY();

    initialFocalPoints = new float[]{startX, startY};

    if(transformMatrix.invert(inverseTransformMatrix))
    inverseTransformMatrix.mapPoints(currentFocalPoints, initialFocalPoints);
    return true;
}

产生差异的线条如下:

float xDiff = initialFocalPoints[0] - currentFocalPoints[0];
float yDiff = initialFocalPoints[1] - currentFocalPoints[1];
transformMatrix.postTranslate(xDiff, yDiff);

答案很简单,只要找出两点之间的差异,并在每次缩放图像时翻译图像视图。

答案 1 :(得分:1)

要应用任何转换,您必须了解数学规则。它适用于2维和3维图形。 也就是说,如果你使用平移(T),旋转(R),缩放(S)矩阵来应用任何变换,你首先要有缩放对象(乘以这个矩阵S的坐标xyz)然后旋转(由R表示)然后通过T移动对象。 因此,在某些点上应用旋转,您必须将对象移动到零并缩放然后返回基点。 也就是说,在你的情况下,在应用比例之前,你必须通过触摸位置移动(减少)所有坐标,然后通过乘法应用比例矩阵然后通过此触摸增加所有位置来移动。