当用户双击屏幕时,我正在构建一个应用程序以在位图上生成圆圈。当用户捏合或张开时,图像会放大和缩小,当他们使用一根手指时,可以拖动图像。
当用户双击时,我编写了代码以在位图上绘制一个圆圈,并且效果很好,除了图像在绘制圆圈后被放大了……我写的手势检测器类有问题(该类位于以下代码的底部:)
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;
}
}
}