Canvas.scale (mScaleFactor, mScaleFactor, detector.getFocusX (), detector.getFocusY ()) drag and drop?

I tried to develop a custom ImageView by doing a google search and here on SO.

Everything works fine, except when I try to scale the canvas with mdetector.getFocusX() and mdetector.getFocusY() .
Scaling works correctly, but drag and drop calculations are confused.

When I use canvas.scale(mScaleFactor,mScaleFactor,gx,gy) in my code below, drag and drop happens everywhere and you can see a black screen. I want to limit snapping to the borders of the screen.

In my code, that related calculation works for Zoom in when canvas.scale(mScaleFactor,mScaleFactor) ie: it works correctly when it is enlarged from (0,0) without using anchor points.

Here is the code:

 import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; 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 ie Amount of Zoom private float mScaleFactor = 1.0f; float gx=0,gy=0; private ScaleGestureDetector mDetector; // Called if used from code public ZoomView(Context context) { super(context); // Intialize ScaleGestureDetector mDetector = new ScaleGestureDetector(context, new ZoomListener()); } //Called if used from XML public ZoomView(Context context, AttributeSet attrs) { super(context, attrs); mDetector = new ScaleGestureDetector(context, new ZoomListener()); } //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::" + mTranslateX); Log.d("Print", " mTranslateX::" + (mScaleFactor - 1) * mWidth); //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 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;*/ //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.restore(); } //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; 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; } /* @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); } } } 
+6
source share
1 answer

I use this:

 public void checkZoom(){ float[] values = new float[9]; matrix.getValues(values); float scaleX = values[Matrix.MSCALE_X]; if(scaleX > MAX_ZOOM) { setZoom(MAX_ZOOM); } else if(scaleX < MIN_ZOOM) { setZoom(MIN_ZOOM); } limitCorners(); } private void limitCorners() { viewWidth = this.getWidth(); viewHeight = this.getHeight(); float []m = new float[9]; matrix.getValues(m); float transX = m[Matrix.MTRANS_X]; float transY = m[Matrix.MTRANS_Y]; float fixTransX = getFixTrans(transX, viewWidth, getImageWidth()); float fixTransY = getFixTrans(transY, viewHeight, getImageHeight()); if (fixTransX != 0 || fixTransY != 0) { matrix.postTranslate(fixTransX, fixTransY); } matrix.getValues(m); if (getImageWidth() < viewWidth) { m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2; } if (getImageHeight() < viewHeight) { m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2; } matrix.setValues(m); } private float getFixTrans(float trans, float viewSize, float contentSize) { float minTrans, maxTrans; if (contentSize <= viewSize) { minTrans = 0; maxTrans = viewSize - contentSize; } else { minTrans = viewSize - contentSize; maxTrans = 0; } if (trans < minTrans) return -trans + minTrans; if (trans > maxTrans) return -trans + maxTrans; return 0; } 

/ ** Get image width (bitmapWidthZoom) /

 float getImageWidth(){ float []m = new float[9]; matrix.getValues(m); return m[Matrix.MSCALE_X]*bitmap.getWidth(); } 

/ ** Get image height (bitmapHeightZoom) /

 float getImageHeight(){ float []m = new float[9]; matrix.getValues(m); return m[Matrix.MSCALE_Y]*bitmap.getHeight(); } 
0
source

Source: https://habr.com/ru/post/912730/


All Articles