我制作了可以缩放和平移的自定义 ImageView。问题在于平移/拖动边界我试图通过限制左上边缘来限制边界,如果它低于零或负值。当我这样做时,它会一直工作,直到出现其中一个条件,例如 X 变为 0 或 Y 变为 0,如果两者都变为 0,它会变得急躁,并且即使存在小于零的条件,也会发生一些奇怪的行为,它超出了界限..
代码带有易于理解的注释请帮助
这是代码
package com.example.customView;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.Camera.PreviewCallback;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
/* @description : Custom View Zoom
*
*/
public class ZoomView extends ImageView {
// Maximum and Minimum Zoom
private static float MIN_ZOOM = 1.0f;
private static float MAX_ZOOM = 3.0f;
//Different Operation to be used
private final int NONE_OPERATION=0;
private final int DRAG_OPERATION=1;
private final int ZOOM_OPERATION=2;
private float mWidth= 1047;
private float mHeight=800;
private boolean dragged=true;
// Mode to select the operation
private int mode;
//Track X and Y coordinate of the finger when it first touches the screen
private float mInitialX = 0f;
private float mInitialY = 0f;
//Track the amount to translate(Drag) the canvas along the X and the Y coordinate
private float mTranslateX = 0f;
private float mTranslateY = 0f;
//Track the last translated X and the Y coordinate while panning so that canvas does not get the jerk (Issue was happening when we change the position again and again )
private float mPreviousTranslateX = 0f;
private float mPreviousTranslateY = 0f;
//ScalingFactor i.e. Amount of Zoom
private float mScaleFactor = 1.0f;
float gx=0,gy=0;
private Rect rect ;
private Matrix matrix;
private ScaleGestureDetector mDetector;
// Called if used from code
public ZoomView(Context context) {
super(context);
// Intialize ScaleGestureDetector
mDetector = new ScaleGestureDetector(context, new ZoomListener());
rect= new Rect();
Matrix matrix= new Matrix();
}
//Called if used from XML
public ZoomView(Context context, AttributeSet attrs) {
super(context, attrs);
mDetector = new ScaleGestureDetector(context, new ZoomListener());
rect= new Rect();
Matrix matrix= new Matrix();
}
//handle the touch event of the view with the detector to get the scalingFactor and also keep the track of
// the touch events like drag and zoom event using booleans
@Override
public boolean onTouchEvent(MotionEvent event) {
// Handles all type of motion-events possible
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// Event occurs when the first finger is pressed on the Screen
// setting the mode to Drag Operation
mode = DRAG_OPERATION;
// Store the initial X and Y of the first finger when touches on the Screen. Take the difference with the previous translation so as to avoid the jerk in canvas.
//Initial difference will be X and Y since previousTranslation will be ZERO.
mInitialX = event.getX() - mPreviousTranslateX;
mInitialY = event.getY() - mPreviousTranslateY;
break;
case MotionEvent.ACTION_MOVE:
// Event occurs when the finger move across the screen and also when the finger is kept pressed on the screen
// Update the translate value constantly as the event is occured at every move
mTranslateX = event.getX() - mInitialX; // Translate value is calculated by diff from current and initial
mTranslateY = event.getY() - mInitialY;
/* float [] matrixValues = new float[9];
matrix.getValues(matrixValues);
if (mode == DRAG_OPERATION) {
if(rect.left<0 || matrixValues[Matrix.MTRANS_X]<0)
mTranslateX = mPreviousTranslateX;
else
mTranslateX = event.getX() - mInitialX; // Translate value is calculated by diff from current and initial
if(rect.top<0 || matrixValues[Matrix.MTRANS_Y]<0 )
mTranslateY=mPreviousTranslateY;
else
mTranslateY = event.getY() - mInitialY;
}*/
// Log.d("Print", " TranslateX::" + mTranslateX + " Translate Y::" + mTranslateY);
// If finger is kept pressed it will still consider the move so to avoid that use this value
//Initial X and Initial Y can not be used directly because they were adjusted using the previous translation values. So need to add those
// values to InitialX and InitialY so that the actual coordinates of the finger are retrieved.
// Using distance Forumla
double distance = Math.sqrt(Math.pow(event.getX() - (mInitialX + mPreviousTranslateX), 2) + Math.pow(event.getY() - (mInitialY + mPreviousTranslateY), 2));
if(distance > 0) {
dragged = true;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
//Event occurs when the second finger is pressed down
// If second finger is pressed on the screen with the first set the Mode to Zoom operation
mode=ZOOM_OPERATION;
break;
case MotionEvent.ACTION_UP:
//Event occurs when all the finger are taken of the screen
//If all the fingers are taken up there will be no operation
mode = NONE_OPERATION;
dragged= false;
// All the operations are done.Store the previousTranslate value here. ( Might not need at the time of second finger down ??)
mPreviousTranslateX = mTranslateX;
mPreviousTranslateY = mTranslateY;
break;
case MotionEvent.ACTION_POINTER_UP:
// Event occurs when the second finger is taken of the screen while first finger is pressed
// Second finger is taken up stop zooming and again Drag Operation
mode=DRAG_OPERATION;
break;
}
// give the event to the mDetector to get the scaling Factor
mDetector.onTouchEvent(event);
//We need to invalidate the canvas to redraw itself for the changes.Here we need to invalidate only when zoom is done and drag operation has happened
//or else for the Zoom which was happening in the onScale function
if((mode==DRAG_OPERATION && mScaleFactor!=1f && dragged ) || mode==ZOOM_OPERATION)
{
invalidate();
}
// we are handling the touch event
return true;
}
//Everything that is going to reflect on the screen will happen in on draw
@Override
protected void onDraw(Canvas canvas) {
//Save the canvas to set the scaling factor returned from detector
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor,gx,gy);
Log.d("Print", "mScaleFactor::" + (mScaleFactor)); //- 1) * mWidth );
Log.d("Print", " mTranslateX::" + mTranslateX + " mTranslateY::" + mTranslateY);
Log.d("Print", " ::" + mTranslateX/mScaleFactor + " mTranslateY::" + mTranslateY/mScaleFactor);
float[] matrixValues = new float[9] ;
//Check the bound that we never pan past the top of left edge of the
/* if((mTranslateX) < 0) {
mTranslateX=0;
}
////Check the right bound.
// eg : Height of display is 1280. When it is zoom by 2 it is 1280 . when it is zoom by 3 it is 2560
// Compare translateX times -1 to (scaleFactor - 1) * displayWidth.
//If translateX is greater than that value, then it has 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((mTranslateX) > (mScaleFactor - 1) * mWidth){
mTranslateX=(mScaleFactor - 1 )* mWidth;
Log.d("Print", " InDraw mTranslateX::" + mTranslateX);
}
if((mTranslateY)< 0)
mTranslateY=0;
else if((mTranslateY) > (mScaleFactor - 1) * mHeight)
mTranslateY= (mScaleFactor-1)* mHeight;*/
if(rect.left<0 || matrixValues[Matrix.MTRANS_X]<0)
mTranslateX = mPreviousTranslateX;
else
mTranslateX = mTranslateX; // Translate value is calculated by diff from current and initial
if(rect.top<0 || matrixValues[Matrix.MTRANS_Y]<0 )
mTranslateY=mPreviousTranslateY;
else
mTranslateY = mPreviousTranslateY;
//divide by the scale factor here,
//otherwise it will end up with excessive panning based on our zoom level since the translation amount also gets scaled according to how much we've zoomed into the canvas.
canvas.translate(mTranslateX / mScaleFactor, mTranslateY / mScaleFactor);
// Draw anything more if needed here ....
// Restore the canvas to balance the save Canvas which removes all the last modification before save.
super.onDraw(canvas);
canvas.getClipBounds(rect);
Log.d("Print", " Canvas X::" + rect.left + " CanvasY::" + rect.top );
matrix = canvas.getMatrix();
Matrix m = new Matrix();
m = canvas.getMatrix();
float[] arr = new float[9] ;
m.getValues(arr );
Log.d("Print", " CanvasMatrixX::" + arr[Matrix.MTRANS_X] + " CanvasMatrixY::" + arr[Matrix.MTRANS_Y] );
canvas.restore();
}
/* @name : ZoomListener
* @description : Class which defines the listener for ScaleGestureDetector while extending abstract
*
*/
private class ZoomListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
/*
* ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
*
* @description: Method gives the scaleFactor from the detector
*
* ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
*/
@Override
public boolean onScale(ScaleGestureDetector detector) {
// getting the scaleFactor from the detector
mScaleFactor *= detector.getScaleFactor(); // gives the scaling factor from the previous scaling to the current
Log.d("Print", "detector scaling Factor" + mScaleFactor);
gx = detector.getFocusX();
gy = detector.getFocusY();
// Limit the scale factor in the MIN and MAX bound
mScaleFactor= Math.max(Math.min(mScaleFactor, MAX_ZOOM),MIN_ZOOM);
Log.d("Print", "Bounded scaling Factor" + mScaleFactor);
/*//Force canvas to redraw itself only if the one event is to happen (say Zooming only ) else do not invalidate here for multi operations
As what we de for scrolling or panning will not reflect here. So we will add this in onDraw method
invalidate();*/
// we have handle the onScale
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
super.onScaleEnd(detector);
}
}
}