安卓.画布缩放和平移 [英] Android. Canvas scale and translate

查看:100
本文介绍了安卓.画布缩放和平移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了自定义视图,您可以在其中进行触摸和缩放. 大部分工作都是在此帖子的帮助下创建的a>

I created custom view where you can touch and scale it. Most of work was created with the help of this post

接下来,我观察到如果我要放大图像,它始终会放大到左上角. 这是我的onDraw()方法:

Next i observe that if i want to zoom in image it is always zooming to top left corner. Here is my onDraw() method:

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

    canvas.save();
    canvas.translate(mPosX, mPosY);
    canvas.scale(mScaleFactor, mScaleFactor);

    // actual drawing

    canvas.getClipBounds(mRect);
    canvas.restore();
}

所以这是canvas.scale(px, py);的正常行为,我希望从我的视角缩放到中心点(稍后我将使用focalXfocalY坐标,但现在只是到中心).我决定更换

So this is normal behaviour for canvas.scale(px, py); I want from my view be scaling to center point(later i will take focalX, focalY coordinates, but now just to center). I decided to replace

canvas.scale(mScaleFactor, mScaleFactor);

canvas.scale(mScaleFactor, mScaleFactor, getWidth()/2-mPosX, getHeight()/2-mPosY);

现在,它可以在图像的中间完美缩放.但这会以某种方式影响我的mPosX,mPosY坐标.我附上一张图片,以便更好地理解.

And now it is scaling perfectly at the mid of the image. But it somehow affects my mPosX, mPosY coordinates. I attach an image for better understanding.

左上角等于mPosX = 0,mPosY = 0,就可以了.

Top left corner is eqivalent mPosX = 0, mPosY=0, and that's OK.

如果我要缩小,则接下来发生:

Next if i am zooming out next happens:

现在,它确定左上角等于mPosX=0,mPosY=0(左上角).但是按照我的逻辑,它应该是某种形式(mPosX = 100,mPosY = 130)或类似的东西.

Now it decides that top left corner is eqivalent to mPosX=0,mPosY=0(top left corner). But in my logic it should be somehow like (mPosX=100, mPosY=130) or something like that.

所以我的问题是我正在从实际内容视图中滑出.我应该怎么做才能防止这种行为?

So my problem is that i am sliding out from my actual content view. What should i do to prevent such behaviour?

推荐答案

我对触摸点计算和引用

I had same question regarding touch points calculation and referring this answer, I solved it my own way using metrics. Above answer provides better pinch zooming and dragging functionality but not correct touch points. Here is the full code and ignore my own variables.

package axis.nbapp;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import java.io.IOException;

public class NewView extends ViewGroup {
    float dx;
    float dy;
    float scaleFac;

    Region r;
    private Path mPath;
    private Paint mPaint;
    private Path mTransformedPath;
    Stencil stencil;
    private Paint cColor;
    boolean scaling;
    Canvas bmcanvas;
    SVG svg = null;
    int viewWidth;
    int viewHeight;
    float svgWidth;
    float svgHeight;

    // States.
    private static final byte NONE = 0;
    private static final byte DRAG = 1;
    private static final byte ZOOM = 2;

    private byte mode = NONE;

    // Matrices used to move and zoom image.
    private Matrix matrix = new Matrix();
    private Matrix matrixInverse = new Matrix();
    private Matrix savedMatrix = new Matrix();


    // Parameters for zooming.
    private PointF start = new PointF();
    private PointF mid = new PointF();
    private float oldDist = 1f;
    private float[] lastEvent = null;
    private long lastDownTime = 0l;

    private float[] mDispatchTouchEventWorkingArray = new float[2];
    private float[] mOnTouchEventWorkingArray = new float[2];


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        mDispatchTouchEventWorkingArray[0] = ev.getX();
        mDispatchTouchEventWorkingArray[1] = ev.getY();
        mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
        ev.setLocation(mDispatchTouchEventWorkingArray[0], mDispatchTouchEventWorkingArray[1]);
        return super.dispatchTouchEvent(ev);
    }

    public NewView(Context context) {
        super(context);
        init(context);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);


    }

    public NewView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public NewView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }


    private void init(Context context) {

    }


    /**
     * Determine the space between the first two fingers
     */
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    /**
     * Calculate the mid point of the first two fingers
     */
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }

    private float[] scaledPointsToScreenPoints(float[] a) {
        matrix.mapPoints(a);
        return a;
    }

    private float[] screenPointsToScaledPoints(float[] a) {
        matrixInverse.mapPoints(a);
        return a;
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        float[] values = new float[9];
        matrix.getValues(values);
        canvas.save();
        canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]);
        canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);



        super.dispatchDraw(canvas);
        canvas.restore();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // handle touch events here
        mOnTouchEventWorkingArray[0] = event.getX();
        mOnTouchEventWorkingArray[1] = event.getY();

        mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);

        event.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);

        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                savedMatrix.set(matrix);
                mode = DRAG;
                lastEvent = null;
                long downTime = event.getDownTime();
                if (downTime - lastDownTime < 300l) {
                    float density = getResources().getDisplayMetrics().density;
                    if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) < 40.f * density) {
                        savedMatrix.set(matrix);
                        mid.set(event.getX(), event.getY());
                        mode = ZOOM;
                        lastEvent = new float[4];
                        lastEvent[0] = lastEvent[1] = event.getX();
                        lastEvent[2] = lastEvent[3] = event.getY();
                    }
                    lastDownTime = 0l;
                } else {
                    lastDownTime = downTime;
                }
                start.set(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    savedMatrix.set(matrix);
                    midPoint(mid, event);
                    mode = ZOOM;
                }
                lastEvent = new float[4];
                lastEvent[0] = event.getX(0);
                lastEvent[1] = event.getX(1);
                lastEvent[2] = event.getY(0);
                lastEvent[3] = event.getY(1);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_MOVE:
                final float density = getResources().getDisplayMetrics().density;
                if (mode == DRAG) {
                    matrix.set(savedMatrix);
                    dx = event.getX() - start.x;
                    dy = event.getY() - start.y;
                    matrix.postTranslate(dx, dy);
                    matrix.invert(matrixInverse);
                    if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) > 20.f * density) {
                        lastDownTime = 0l;
                    }
                } else if (mode == ZOOM) {
                    if (event.getPointerCount() > 1) {
                        float newDist = spacing(event);
                        if (newDist > 10f * density) {
                            matrix.set(savedMatrix);
                            float scale = (newDist / oldDist);
                            scaleFac = newDist;
                            matrix.postScale(scale, scale, mid.x, mid.y);
                            matrix.invert(matrixInverse);
                        }
                    } else {
                        matrix.set(savedMatrix);
                        float scale = event.getY() / start.y;
                        scaleFac = scale;
                        matrix.postScale(scale, scale, mid.x, mid.y);
                        matrix.invert(matrixInverse);
                    }
                }

                break;
        }


        float[] values = new float[9];
        matrix.getValues(values);


        //int x = ((int)(event.getX() - values[2]*values[Matrix.MSCALE_X]))/(int)values[0];
        //int y = ((int)(event.getY() - values[5]*values[Matrix.MSCALE_Y]))/(int)values[4];

        int x = ((int)(event.getX() / values[Matrix.MSCALE_X] - (values[Matrix.MTRANS_X]/values[Matrix.MSCALE_X])));
        int y = ((int)(event.getY() / values[Matrix.MSCALE_Y] - (values[Matrix.MTRANS_Y]/values[Matrix.MSCALE_Y])));


        invalidate();
        return true;
    }


}

选中onTouchEvent,您可以从以下计算中找到转换的x,y接触点.

Check onTouchEvent you can find transformed x, y touch points from following calculation.

 int x = ((int)(event.getX() / values[Matrix.MSCALE_X] - (values[Matrix.MTRANS_X]/values[Matrix.MSCALE_X])));
 int y = ((int)(event.getY() / values[Matrix.MSCALE_Y] - (values[Matrix.MTRANS_Y]/values[Matrix.MSCALE_Y])));

这篇关于安卓.画布缩放和平移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆