Java 如何在画布上检索信息

Java 如何在画布上检索信息,java,android,canvas,drawing,zooming,Java,Android,Canvas,Drawing,Zooming,我想做一个“可缩放的绘画”,我指的是一个我可以缩放、平移、拖动画布,然后在画布上画画的绘画 我有一个无法解决的问题:当我在缩放画布时绘制时,我检索X和Y坐标,并有效地在画布上绘制它。但由于画布缩放,这些坐标不正确 我试图纠正这些错误(乘以(缩放权重/屏幕高度)),但我找不到一种方法来检索必须在原始/未缩放屏幕上绘制的位置 这是我的代码: public class PaintView extends View { public static int BRUSH_SIZE = 20; public

我想做一个“可缩放的绘画”,我指的是一个我可以缩放、平移、拖动画布,然后在画布上画画的绘画

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

我试图纠正这些错误(乘以(缩放权重/屏幕高度)),但我找不到一种方法来检索必须在原始/未缩放屏幕上绘制的位置

这是我的代码:

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;
}
public类PaintView扩展视图{
公共静态int笔刷大小=20;
公共静态最终整型默认颜色=COLOR.BLACK;
公共静态最终整数默认值\u BG\u COLOR=COLOR.WHITE;
专用静态最终浮动接触公差=4;
私人浮动mX,我的;
私有序列化路径mPath;
私人油漆;
私有ArrayList路径=新ArrayList();
private ArrayList tempPath=new ArrayList();
私人色彩;
private int backgroundColor=默认背景颜色;
私有内冲程宽度;
私有布尔浮雕;
私有布尔模糊;
专用布尔橡皮擦;
私人MaskFilter mEmboss;
私人MaskFilter mBlur;
私有位图mBitmap;
私人帆布mCanvas;
专用油漆mBitmapPaint=新油漆(油漆抖动标志);
公共布尔值zoomViewActivated=false;
//这两个常量指定最小和最大缩放
专用静态浮动最小缩放=1f;
专用静态浮动最大缩放=5f;
私有浮点scaleFactor=1.f;
专用电子秤检测器;
//这些常量指定了我们所处的模式
私有静态int NONE=0;
私有静态int-DRAG=1;
私有静态int-ZOOM=2;
私有int模式;
//这两个变量在第一次移动手指时跟踪手指的X和Y坐标
//触摸屏幕
专用浮点数startX=0f;
私人浮动起点=0f;
//这两个变量跟踪沿X轴平移画布所需的量
//和Y坐标
私有浮动translateX=0f;
私有浮点translateY=0f;
//这两个变量跟踪我们上次转换X和Y坐标的量
//淘金。
private float previousTranslateX=0f;
private float previousTranslateY=0f;
int currentPositionX=0;
int currentPositionY=0;
私有类ScaleListener扩展了scalegestruedetector.SimpleOnScalegestrueListener{
@凌驾
公共布尔标度(scalegestruedetector检测器){
scaleFactor*=检测器。getScaleFactor();
scaleFactor=Math.max(最小缩放,数学最小(scaleFactor,最大缩放));
返回true;
}
}
公共阵列列表GetDividedPath(浮点i){
对于(FingerPath p:this.paths){
矩阵scaleMatrix=新矩阵();
刻度矩阵。设置刻度(i,i);
p、 路径变换(scaleMatrix);
}
返回此路径;
}
公共ArrayList getpath(){
返回路径;
}
公共无效分割路径(浮动i){
对于(FingerPath p:this.paths){
矩阵scaleMatrix=新矩阵();
刻度矩阵。设置刻度(i,i);
p、 路径变换(scaleMatrix);
}
}
公共void集合路径(ArrayList路径){
这个路径=路径;
}
公共无效设置行程宽度(int值){
冲程宽度=值;
}
公共PaintView(上下文){
这个(上下文,空);
检测器=新的scalegestruedetector(getContext(),newscaleListener());
}
公共画图视图(上下文、属性集属性){
超级(上下文,attrs);
mPaint=新油漆();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(默认颜色);
mPaint.setStyle(油漆、样式、笔划);
mPaint.setStrokeJoin(油漆.连接.圆形);
mPaint.setStrokeCap(油漆盖圆形);
mPaint.setXfermode(null);
mPaint.setAlpha(0xff);
mEmboss=新的浮雕maskfilter(新的float[]{1,1,1},0.4f,6,3.5f);
mBlur=新的模糊过滤器(5,BlurMaskFilter.Blur.NORMAL);
检测器=新的scalegestruedetector(getContext(),newscaleListener());
}
公共void init(DisplayMetrics){
int height=metrics.heightPixels;
int width=metrics.widthPixels;
mBitmap=Bitmap.createBitmap(宽度、高度、Bitmap.Config.ARGB_8888);
mCanvas=新画布(mBitmap);
currentColor=默认颜色;
笔划宽度=画笔大小;
}
公共无效正常值(){
浮雕=假;
模糊=错误;
橡皮擦=假;
}
公共虚空浮雕(){
浮雕=真;
模糊=错误;
橡皮擦=假;
}
公共空间模糊(){
浮雕=假;
模糊=真;
橡皮擦=假;
}
公共无效橡皮擦(){
橡皮擦=真;
}
公开作废取消(){
if(path.size()!=0){
add(path.get(path.size()-1));
path.remove(path.size()-1);
使无效();
}
}
公共无效重做(){
if(tempPath.size()!=0){
add(tempPath.get(tempPath.size()-1));
tempPaths.remove(tempPaths.size()-1);
使无效();
}
}
公共空间清除(){
backgroundColor=默认的背景颜色;
清除路径();
正常();
使无效();
}
@凌驾
受保护的void onDraw(画布){
super.onDraw(帆布);
canvas.save();
//我们将以相同的比例缩放X和Y坐标
scale(scaleFactor,scaleFactor);
//如果translateX乘以-1小于零,我们将其设置为零。这将考虑左边界
如果((translateX*-1)<0){
translateX=0;
}
//这就是我们处理右边界的地方。我们将translateX乘以-1与(scaleFactor-1)*displayWidth进行比较。
//如果translateX大于该值,那么我们知道我们已经超过了界限。所以我们设置
//translateX to(1-scaleFactor)乘以显示宽度。请注意,术语是互换的;它是相同的
//正在执行-1*(缩放因子-1)*显示宽度
else if((translateX*-1)>(scaleFactor-1)*getWidth(){
translateX=(1-scaleFactor)*getWidth();
}
if(translateY*-1<0){