0

当用户双击屏幕时,我正在构建一个应用程序以在位图上生成圆圈。当用户捏合或张开时,图像会放大和缩小,当他们使用一根手指时,可以拖动图像。

当用户双击时,我编写了代码以在位图上绘制一个圆圈,并且效果很好,除了图像在绘制圆圈后被放大了……我写的手势检测器类有问题(该类位于以下代码的底部:)

public class ZoomInZoomOut extends ImageView {
private static final String TAG = "ZoomableImageView";

private Bitmap imgBitmap = null;

private int containerWidth;
private int containerHeight;

Paint background;

//Matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();

PointF start = new PointF();

float currentScale;
float curX;
float curY;

//We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;

//For animating stuff
float targetX;
float targetY;
float targetScale;
float targetScaleX;
float targetScaleY;
float scaleChange;
float targetRatio;
float transitionalRatio;

float easing = 0.2f;
boolean isAnimating = false;

float scaleDampingFactor = 0.5f;

//For pinch and zoom
float oldDist = 1f;
PointF mid = new PointF();

private Handler mHandler = new Handler();

float minScale;
float maxScale = 8.0f;

float wpRadius = 25.0f;
float wpInnerRadius = 20.0f;

float screenDensity;
Context context;
private GestureDetector gestureDetector;

public static final int DEFAULT_SCALE_FIT_INSIDE = 0;
public static final int DEFAULT_SCALE_ORIGINAL = 1;

private int defaultScale;

public int getDefaultScale() {
    return defaultScale;
}

public void setDefaultScale(int defaultScale) {
    this.defaultScale = defaultScale;
}

public ZoomInZoomOut(Context context) {
    super(context);
    setFocusable(true);
    setFocusableInTouchMode(true);

    screenDensity = context.getResources().getDisplayMetrics().density;

    initPaints();
    gestureDetector = new GestureDetector(new MyGestureDetector());
    this.context=context;
}

public ZoomInZoomOut(Context context, AttributeSet attrs) {
    super(context, attrs);

    screenDensity = context.getResources().getDisplayMetrics().density;
    initPaints();
    gestureDetector = new GestureDetector(new MyGestureDetector());

    defaultScale = ZoomInZoomOut.DEFAULT_SCALE_FIT_INSIDE;
}

private void initPaints() {
    background = new Paint();
}

@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
    super.onSizeChanged(width, height, oldWidth, oldHeight);

    //Reset the width and height. Will draw bitmap and change
    containerWidth = width;
    containerHeight = height;

    if(imgBitmap != null) {
        int imgHeight = imgBitmap.getHeight();
        int imgWidth = imgBitmap.getWidth();

        float scale;
        int initX = 0;
        int initY = 0;

        if(defaultScale == ZoomInZoomOut.DEFAULT_SCALE_FIT_INSIDE) {
            if(imgWidth > containerWidth) {
                scale = (float)containerWidth / imgWidth;
                float newHeight = imgHeight * scale;
                initY = (containerHeight - (int)newHeight)/2;

                matrix.setScale(scale, scale);
                matrix.postTranslate(0, initY);
            }
            else {
                scale = (float)containerHeight / imgHeight;
                float newWidth = imgWidth * scale;
                initX = (containerWidth - (int)newWidth)/2;

                matrix.setScale(scale, scale);
                matrix.postTranslate(initX, 0);
            }

            curX = initX;
            curY = initY;

            currentScale = scale;
            minScale = scale;
        }
        else {
            if(imgWidth > containerWidth) {
                initY = (containerHeight - (int)imgHeight)/2;
                matrix.postTranslate(0, initY);
            }
            else {
                initX = (containerWidth - (int)imgWidth)/2;
                matrix.postTranslate(initX, 0);
            }

            curX = initX;
            curY = initY;

            currentScale = 1.0f;
            minScale = 1.0f;
        }


        invalidate();
    }
}

@Override
protected void onDraw(Canvas canvas) {
    if(imgBitmap != null && canvas != null)
    {
        canvas.drawBitmap(imgBitmap, matrix, background);
    }
}

//Checks and sets the target image x and y co-ordinates if out of bounds
private void checkImageConstraints() {
    if(imgBitmap == null) {
        return;
    }

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

    currentScale = mvals[0];

    if(currentScale < minScale) {
        float deltaScale = minScale / currentScale;
        float px = containerWidth/2;
        float py = containerHeight/2;
        matrix.postScale(deltaScale, deltaScale, px, py);
        invalidate();
    }

    matrix.getValues(mvals);
    currentScale = mvals[0];
    curX = mvals[2];
    curY = mvals[5];

    int rangeLimitX = containerWidth - (int)(imgBitmap.getWidth() * currentScale);
    int rangeLimitY = containerHeight - (int)(imgBitmap.getHeight() * currentScale);


    boolean toMoveX = false;
    boolean toMoveY = false;

    if(rangeLimitX < 0) {
        if(curX > 0) {
            targetX = 0;
            toMoveX = true;
        }
        else if(curX < rangeLimitX) {
            targetX = rangeLimitX;
            toMoveX = true;
        }
    }
    else {
        targetX = rangeLimitX / 2;
        toMoveX = true;
    }

    if(rangeLimitY < 0) {
        if(curY > 0) {
            targetY = 0;
            toMoveY = true;
        }
        else if(curY < rangeLimitY) {
            targetY = rangeLimitY;
            toMoveY = true;
        }
    }
    else {
        targetY = rangeLimitY / 2;
        toMoveY = true;
    }

    if(toMoveX == true || toMoveY == true) {
        if(toMoveY == false) {
            targetY = curY;
        }
        if(toMoveX == false) {
            targetX = curX;
        }

        //Disable touch event actions
        isAnimating = true;
        //Initialize timer
        mHandler.removeCallbacks(mUpdateImagePositionTask);
        mHandler.postDelayed(mUpdateImagePositionTask, 100);
    }
}


@Override
public boolean onTouchEvent(MotionEvent event) {
    float oldX = 0, newX = 0, sens = 5;
    if(gestureDetector.onTouchEvent(event)) {
        return true;
    }

    if(isAnimating == true) {
        return true;
    }

    //Handle touch events here
    float[] mvals = new float[9];
    switch(event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            if(isAnimating == false) {
                savedMatrix.set(matrix);
                oldX = event.getX();
                start.set(event.getX(), event.getY());
                mode = DRAG;
            }
            break;

        case MotionEvent.ACTION_POINTER_DOWN:
            oldDist = spacing(event);
            if(oldDist > 10f) {
                savedMatrix.set(matrix);
                midPoint(mid, event);
                mode = ZOOM;
            }
            break;
        case MotionEvent.ACTION_UP:
            newX = event.getX();
            if (Math.abs(oldX - newX) < sens) {
                Toast.makeText(context, "Hello", Toast.LENGTH_LONG).show();
                return true;
            }
            oldX = 0;
            newX = 0;
            break;
        case MotionEvent.ACTION_POINTER_UP:
            mode = NONE;

            matrix.getValues(mvals);
            curX = mvals[2];
            curY = mvals[5];
            currentScale = mvals[0];

            if(isAnimating == false) {
                checkImageConstraints();
            }
            break;

        case MotionEvent.ACTION_MOVE:
            if(mode == DRAG && isAnimating == false) {
                matrix.set(savedMatrix);
                float diffX = event.getX() - start.x;
                float diffY = event.getY() - start.y;

                matrix.postTranslate(diffX, diffY);

                matrix.getValues(mvals);
                curX = mvals[2];
                curY = mvals[5];
                currentScale = mvals[0];
            }
            else if(mode == ZOOM && isAnimating == false) {
                float newDist = spacing(event);
                if(newDist > 10f) {
                    matrix.set(savedMatrix);
                    float scale = newDist / oldDist;
                    matrix.getValues(mvals);
                    currentScale = mvals[0];

                    if(currentScale * scale <= minScale) {
                        matrix.postScale(minScale/currentScale, minScale/currentScale, mid.x, mid.y);
                    }
                    else if(currentScale * scale >= maxScale) {
                        matrix.postScale(maxScale/currentScale, maxScale/currentScale, mid.x, mid.y);
                    }
                    else {
                        matrix.postScale(scale, scale, mid.x, mid.y);
                    }


                    matrix.getValues(mvals);
                    curX = mvals[2];
                    curY = mvals[5];
                    currentScale = mvals[0];
                }
            }

            break;
    }

    //Calculate the transformations and then invalidate
    invalidate();
    return true;
}

private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return FloatMath.sqrt(x * x + y * y);
}

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);
}

public void setImageBitmap(Bitmap b) {
    if(b != null) {
        imgBitmap = b;

        containerWidth = getWidth();
        containerHeight = getHeight();

        int imgHeight = imgBitmap.getHeight();
        int imgWidth = imgBitmap.getWidth();

        float scale;
        int initX = 0;
        int initY = 0;

        matrix.reset();

        if(defaultScale == ZoomInZoomOut.DEFAULT_SCALE_FIT_INSIDE) {
            if(imgWidth > containerWidth) {
                scale = (float)containerWidth / imgWidth;
                float newHeight = imgHeight * scale;
                initY = (containerHeight - (int)newHeight)/2;

                matrix.setScale(scale, scale);
                matrix.postTranslate(0, initY);
            }
            else {
                scale = (float)containerHeight / imgHeight;
                float newWidth = imgWidth * scale;
                initX = (containerWidth - (int)newWidth)/2;

                matrix.setScale(scale, scale);
                matrix.postTranslate(initX, 0);
            }

            curX = initX;
            curY = initY;

            currentScale = scale;
            minScale = scale;
        }
        else {
            if(imgWidth > containerWidth) {
                initX = 0;
                if(imgHeight > containerHeight) {
                    initY = 0;
                }
                else {
                    initY = (containerHeight - (int)imgHeight)/2;
                }

                matrix.postTranslate(0, initY);
            }
            else {
                initX = (containerWidth - (int)imgWidth)/2;
                if(imgHeight > containerHeight) {
                    initY = 0;
                }
                else {
                    initY = (containerHeight - (int)imgHeight)/2;
                }
                matrix.postTranslate(initX, 0);
            }

            curX = initX;
            curY = initY;

            currentScale = 1.0f;
            minScale = 1.0f;
        }

        invalidate();
    }
    else {
        Log.d(TAG, "bitmap is null");
    }
}

public Bitmap getPhotoBitmap() {
    return imgBitmap;
}


private Runnable mUpdateImagePositionTask = new Runnable() {
    public void run() {
        float[] mvals;

        if(Math.abs(targetX - curX) < 5 && Math.abs(targetY - curY) < 5) {
            isAnimating = false;
            mHandler.removeCallbacks(mUpdateImagePositionTask);

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

            currentScale = mvals[0];
            curX = mvals[2];
            curY = mvals[5];

            //Set the image parameters and invalidate display
            float diffX = (targetX - curX);
            float diffY = (targetY - curY);

            matrix.postTranslate(diffX, diffY);
        }
        else {
            isAnimating = true;
            mvals = new float[9];
            matrix.getValues(mvals);

            currentScale = mvals[0];
            curX = mvals[2];
            curY = mvals[5];

            //Set the image parameters and invalidate display
            float diffX = (targetX - curX) * 0.3f;
            float diffY = (targetY - curY) * 0.3f;

            matrix.postTranslate(diffX, diffY);
            mHandler.postDelayed(this, 25);
        }

        invalidate();
    }
};

private Runnable mUpdateImageScale = new Runnable() {
    public void run() {
        float transitionalRatio = targetScale / currentScale;
        float dx;
        if(Math.abs(transitionalRatio - 1) > 0.05) {
            isAnimating = true;
            if(targetScale > currentScale) {
                dx = transitionalRatio - 1;
                scaleChange = 1 + dx * 0.2f;

                currentScale *= scaleChange;

                if(currentScale > targetScale) {
                    currentScale = currentScale / scaleChange;
                    scaleChange = 1;
                }
            }
            else {
                dx = 1 - transitionalRatio;
                scaleChange = 1 - dx * 0.5f;
                currentScale *= scaleChange;

                if(currentScale < targetScale) {
                    currentScale = currentScale / scaleChange;
                    scaleChange = 1;
                }
            }


            if(scaleChange != 1) {
                matrix.postScale(scaleChange, scaleChange, targetScaleX, targetScaleY);
                mHandler.postDelayed(mUpdateImageScale, 15);
                invalidate();
            }
            else {
                isAnimating = false;
                scaleChange = 1;
                matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY);
                currentScale = targetScale;
                mHandler.removeCallbacks(mUpdateImageScale);
                invalidate();
                checkImageConstraints();
            }
        }
        else {
            isAnimating = false;
            scaleChange = 1;
            matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY);
            currentScale = targetScale;
            mHandler.removeCallbacks(mUpdateImageScale);
            invalidate();
            checkImageConstraints();
        }
    }
};

/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
    String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
    StringBuilder sb = new StringBuilder();
    int action = event.getAction();
    int actionCode = action & MotionEvent.ACTION_MASK;
    sb.append("event ACTION_").append(names[actionCode]);
    if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
        sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
        sb.append(")");
    }
    sb.append("[");

    for (int i = 0; i < event.getPointerCount(); i++) {
        sb.append("#").append(i);
        sb.append("(pid ").append(event.getPointerId(i));
        sb.append(")=").append((int) event.getX(i));
        sb.append(",").append((int) event.getY(i));
        if (i + 1 < event.getPointerCount())
            sb.append(";");
    }
    sb.append("]");
}

class MyGestureDetector extends SimpleOnGestureListener {



    @Override
    public boolean onDoubleTap(MotionEvent event) {


        Bitmap bmOverlay = Bitmap.createBitmap(imgBitmap.getWidth(),
               imgBitmap.getHeight(),
                imgBitmap.getConfig());
        Canvas canvas = new Canvas(bmOverlay);
        Paint p = new Paint();
        p.setAntiAlias(true);
        p.setColor(Color.RED);
        p.setStrokeWidth(2);
        p.setStyle(Paint.Style.STROKE);
        canvas.drawBitmap(imgBitmap, matrix, null);
        canvas.drawCircle((int)event.getX(),(int)event.getY(),
                100, p);

        setImageBitmap(bmOverlay);
        return true;
    }


    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }
}

}

4

1 回答 1

1

您正在设置图像位图。setImageBitmap 调用 matrix.reset。因此,当您这样做时,您将失去缩放系数。您需要摆弄您的代码/架构以防止该调用。

于 2015-03-20T22:09:06.810 回答