我如何在画布上检索信息

时间:2018-04-24 16:10:55

标签: java android canvas drawing zoom

我想做一个“zoomable paint”,我的意思是一个颜色,我可以缩放/缩小并平移/拖动画布,然后在上面绘画。

我有一个我无法解决的问题:当我在画布缩放时绘制时,我检索X和Y坐标并在画布上有效地绘制它。但是由于缩放的画布,这些坐标不正确。

我试图纠正这些(乘以(zoomHeigh / screenHeight)),但我找不到一种方法来检索我必须在原始/无缩放屏幕上绘制的位置

这是我的代码:

public class PaintView extends View {

public static int BRUSH_SIZE = 20;
public static final int DEFAULT_COLOR = Color.BLACK;
public static final int DEFAULT_BG_COLOR = Color.WHITE;
private static final float TOUCH_TOLERANCE = 4;
private float mX, mY;
private SerializablePath mPath;
private Paint mPaint;
private ArrayList<FingerPath> paths = new ArrayList<>();
private ArrayList<FingerPath> tempPaths = new ArrayList<>();

private int currentColor;
private int backgroundColor = DEFAULT_BG_COLOR;
private int strokeWidth;
private boolean emboss;
private boolean blur;
private boolean eraser;
private MaskFilter mEmboss;
private MaskFilter mBlur;
private Bitmap mBitmap;
private Canvas mCanvas;
private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);

public boolean zoomViewActivated = false;
//These two constants specify the minimum and maximum zoom
private static float MIN_ZOOM = 1f;
private static float MAX_ZOOM = 5f;

private float scaleFactor = 1.f;
private ScaleGestureDetector detector;

//These constants specify the mode that we're in
private static int NONE = 0;
private static int DRAG = 1;
private static int ZOOM = 2;

private int mode;

//These two variables keep track of the X and Y coordinate of the finger when it first
//touches the screen
private float startX = 0f;
private float startY = 0f;

//These two variables keep track of the amount we need to translate the canvas along the X
//and the Y coordinate
private float translateX = 0f;
private float translateY = 0f;

//These two variables keep track of the amount we translated the X and Y coordinates, the last time we
//panned.
private float previousTranslateX = 0f;
private float previousTranslateY = 0f;

int currentPositionX = 0;
int currentPositionY = 0;

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scaleFactor *= detector.getScaleFactor();
        scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
        return true;
    }
}


public ArrayList<FingerPath> getDividedPaths(float i){

    for(FingerPath p : this.paths){
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale(i, i);
        p.path.transform(scaleMatrix);
    }

    return this.paths;
}

public ArrayList<FingerPath> getPaths() {
    return paths;
}

public void dividePath(float i) {

    for(FingerPath p : this.paths){
        Matrix scaleMatrix = new Matrix();
        scaleMatrix.setScale(i, i);
        p.path.transform(scaleMatrix);
    }

}

public void setPaths(ArrayList<FingerPath> paths){
    this.paths = paths;
}

public void setStrokeWidth(int value){
    strokeWidth = value;
}

public PaintView(Context context) {
    this(context, null);
    detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}

public PaintView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setDither(true);
    mPaint.setColor(DEFAULT_COLOR);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setXfermode(null);
    mPaint.setAlpha(0xff);

    mEmboss = new EmbossMaskFilter(new float[] {1, 1, 1}, 0.4f, 6, 3.5f);
    mBlur = new BlurMaskFilter(5, BlurMaskFilter.Blur.NORMAL);

    detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}

public void init(DisplayMetrics metrics) {
    int height = metrics.heightPixels;
    int width = metrics.widthPixels;

    mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    mCanvas = new Canvas(mBitmap);

    currentColor = DEFAULT_COLOR;
    strokeWidth = BRUSH_SIZE;
}

public void normal() {
    emboss = false;
    blur = false;
    eraser = false;
}

public void emboss() {
    emboss = true;
    blur = false;
    eraser = false;
}

public void blur() {
    emboss = false;
    blur = true;
    eraser = false;
}

public void eraser() {
    eraser = true;
}

public void cancel(){
    if(paths.size() != 0){
        tempPaths.add(paths.get(paths.size()-1));
        paths.remove(paths.size()-1);
        invalidate();
    }
}

public void redo(){
    if(tempPaths.size() != 0){
        paths.add(tempPaths.get(tempPaths.size()-1));
        tempPaths.remove(tempPaths.size()-1);
        invalidate();
    }
}

public void clear() {
    backgroundColor = DEFAULT_BG_COLOR;
    paths.clear();
    normal();
    invalidate();
}

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

        //We're going to scale the X and Y coordinates by the same amount
        canvas.scale(scaleFactor, scaleFactor);

        //If translateX times -1 is lesser than zero, let's set it to zero. This takes care of the left bound
        if((translateX * -1) < 0) {
            translateX = 0;
        }

        //This is where we take care of the right bound. We compare translateX times -1 to (scaleFactor - 1) * displayWidth.
        //If translateX is greater than that value, then we know that we've gone over the bound. So we set the value of
        //translateX to (1 - scaleFactor) times the display width. Notice that the terms are interchanged; it's the same
        //as doing -1 * (scaleFactor - 1) * displayWidth
        else if((translateX * -1) > (scaleFactor - 1) * getWidth()) {
            translateX = (1 - scaleFactor) * getWidth();
        }

        if(translateY * -1 < 0) {
            translateY = 0;
        }

        //We do the exact same thing for the bottom bound, except in this case we use the height of the display
        else if((translateY * -1) > (scaleFactor - 1) * getHeight()) {
            translateY = (1 - scaleFactor) * getHeight();
        }

        //We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level
        //because the translation amount also gets scaled according to how much we've zoomed into the canvas.
        canvas.translate(translateX / scaleFactor, translateY / scaleFactor);

        /* The rest of your canvas-drawing code */



    mCanvas.drawColor(backgroundColor);

    if(paths != null){
        for (FingerPath fp : paths) {
            mPaint.setColor(fp.color);
            mPaint.setStrokeWidth(fp.strokeWidth);
            mPaint.setMaskFilter(null);

            if (fp.emboss)
                mPaint.setMaskFilter(mEmboss);
            else if (fp.blur)
                mPaint.setMaskFilter(mBlur);
            if(fp.eraser) {
                mPaint.setColor(DEFAULT_BG_COLOR);
            }

            mCanvas.drawPath(fp.path, mPaint);

        }
    }

    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
    canvas.restore();
}

private void touchStart(float x, float y) {
    mPath = new SerializablePath();
    FingerPath fp = new FingerPath(currentColor, emboss, blur, eraser, strokeWidth, mPath);
    paths.add(fp);
    tempPaths = new ArrayList<>();

    mPath.reset();
    mPath.moveTo(x, y);
    mX = x;
    mY = y;
}

private void touchMove(float x, float y) {
    float dx = Math.abs(x - mX);
    float dy = Math.abs(y - mY);

    if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
        mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
        mX = x;
        mY = y;
    }
}

private void touchUp() {
    mPath.lineTo(mX, mY);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if(zoomViewActivated){
        boolean dragged = false;
        switch (event.getAction() & MotionEvent.ACTION_MASK) {

            case MotionEvent.ACTION_DOWN:
                mode = DRAG;

                //We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
                //amount for each coordinates This works even when we are translating the first time because the initial
                //values for these two variables is zero.
                startX = event.getX() - previousTranslateX;
                startY = event.getY() - previousTranslateY;
                break;

            case MotionEvent.ACTION_MOVE:
                translateX = event.getX() - startX;
                translateY = event.getY() - startY;

                //We cannot use startX and startY directly because we have adjusted their values using the previous translation values.
                //This is why we need to add those values to startX and startY so that we can get the actual coordinates of the finger.
                double distance = Math.sqrt(Math.pow(event.getX() - (startX + previousTranslateX), 2) +
                        Math.pow(event.getY() - (startY + previousTranslateY), 2)
                );

                if(distance > 0) {
                    dragged = true;
                }

                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                mode = ZOOM;
                break;

            case MotionEvent.ACTION_UP:
                mode = NONE;
                dragged = false;

                //All fingers went up, so let's save the value of translateX and translateY into previousTranslateX and
                //previousTranslate
                previousTranslateX = translateX;
                previousTranslateY = translateY;

                currentPositionX += previousTranslateX;
                currentPositionY += previousTranslateY;

                break;

            case MotionEvent.ACTION_POINTER_UP:
                mode = DRAG;

                //This is not strictly necessary; we save the value of translateX and translateY into previousTranslateX
                //and previousTranslateY when the second finger goes up
                previousTranslateX = translateX;
                previousTranslateY = translateY;
                break;
        }

        detector.onTouchEvent(event);

        //We redraw the canvas only in the following cases:
        //
        // o The mode is ZOOM
        //        OR
        // o The mode is DRAG and the scale factor is not equal to 1 (meaning we have zoomed) and dragged is
        //   set to true (meaning the finger has actually moved)
        if ((mode == DRAG && scaleFactor != 1f && dragged) || mode == ZOOM) {
            invalidate();
        }

    }else{
        float x = event.getX()*(getHeight()/scaleFactor)/getHeight()+currentPositionX;
        float y = event.getY()*(getWidth()/scaleFactor)/getWidth()+currentPositionY;



        switch(event.getAction()) {
            case MotionEvent.ACTION_DOWN :
                touchStart(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE :
                touchMove(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP :
                touchUp();
                invalidate();
                break;
        }

    }
    return true;
}

}

0 个答案:

没有答案