Android - Pinch zoom ontouch event coordinates

I am trying to get the canvas coordinates for the android application that I am creating. It works fine until I add code to use the focus point (the following two lines):

scalePoint.setX((int) detector.getFocusX()); scalePoint.setY((int) detector.getFocusY()); 

Here is my source code for my view class:

  package us.kniffin.Jigsaw; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; public class TestView extends View { private float mLastTouchX; private float mLastTouchY; private float mPosX; private float mPosY; private Rect rect; private float cX, cY; // circle coords // Scaling objects private ScaleGestureDetector mScaleDetector; private float mScaleFactor = 1.f; // The focus point for the scaling private float scalePointX; private float scalePointY; public TestView(Context context) { super(context); mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint p = new Paint(); p.setColor(Color.RED); rect = canvas.getClipBounds(); canvas.save(); canvas.scale(mScaleFactor, mScaleFactor, scalePointX, scalePointY); canvas.translate(mPosX, mPosY); canvas.drawCircle(cX, cY, 10, p); canvas.restore(); } @Override public boolean onTouchEvent(MotionEvent ev) { // Let the ScaleGestureDetector inspect all events. mScaleDetector.onTouchEvent(ev); final int action = ev.getAction(); switch(action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { final float x = ev.getX()/mScaleFactor;// screen X position final float y = ev.getY()/mScaleFactor;// screen Y position cX = x - (rect.left * mScaleFactor) - mPosX; // canvas X cY = y - (rect.top * mScaleFactor) - mPosY; // canvas Y // Remember where we started mLastTouchX = x; mLastTouchY = y; break; } case MotionEvent.ACTION_MOVE: { final float x = ev.getX()/mScaleFactor; final float y = ev.getY()/mScaleFactor; cX = x - (rect.left * mScaleFactor) - mPosX; // canvas X cY = y - (rect.top * mScaleFactor) - mPosY; // canvas Y // Only move if the ScaleGestureDetector isn't processing a gesture. if (!mScaleDetector.isInProgress()) { final float dx = x - mLastTouchX; // change in X final float dy = y - mLastTouchY; // change in Y mPosX += dx; mPosY += dy; invalidate(); } mLastTouchX = x; mLastTouchY = y; break; } case MotionEvent.ACTION_UP: { mLastTouchX = 0; mLastTouchY = 0; invalidate(); } } return true; } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); scalePointX = detector.getFocusX(); scalePointY = detector.getFocusY(); // Don't let the object get too small or too large. mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f)); invalidate(); return true; } } } 

Any ideas what I need to do to get this to work?

Update: I replaced the sample code with another that has the same problem but is simplified to the basic requirements

Update again: The problem occurs after scaling. The coordinates are correct before scaling, but after that the coordinates are too far to the right and below where you click. It seems that the more you zoom out, the more errors they get.

+4
source share
3 answers

Haha Success! It took me almost the whole day, but I figured it out with a bunch of guesses and checks.

Here is some code that I need:

 case MotionEvent.ACTION_DOWN: { final float x = (ev.getX() - scalePointX)/mScaleFactor; final float y = (ev.getY() - scalePointY)/mScaleFactor; cX = x - mPosX + scalePointX; // canvas X cY = y - mPosY + scalePointY; // canvas Y [snip] } 

And a similar bit of code for ACTION_MOVE

+8
source

Whenever you use translation, scaling, rotation (etc.), you change the canvas matrix. Typically, this matrix is ​​used to tell you every (x, y) where it is on the canvas. When you create a canvas, the matrix is ​​the identity matrix: (1) (1) (1) But whenever you make one of these changes, this matrix also changes. If you want to know the correct coordinates, then you must use canvas.save () before scaling / rotating, and after scaling use canvas.restore ()

+1
source

I recently ran into this problem while doing something very similar, and after some trial and error and a lot of googling, I eventually adapted this answer ( fooobar.com/questions/269597 / ... ):

(e is MotionEvent, so the best place to use this code would be in onTouch or onLongPress)

 mClickCoords = new float[2]; //e is the motionevent that contains the screen touch we //want to translate into a canvas coordinate mClickCoords[0] = e.getX(); mClickCoords[1] = e.getY(); Matrix matrix = new Matrix(); matrix.set(getMatrix()); //this is where you apply any translations/scaling/rotation etc. //Typically you want to apply the same adjustments that you apply //in your onDraw(). matrix.preTranslate(mVirtualX, mVirtualY); matrix.preScale(mScaleFactor, mScaleFactor, mPivotX, mPivotY); // invert the matrix, creating the mapping from screen //coordinates to canvas coordinates matrix.invert(matrix); //apply the mapping matrix.mapPoints(mClickCoords); //mClickCoords[0] is the canvas x coordinate and //mClickCoords[1] is the y coordinate. 

There are some obvious optimizations that can be applied here, but I think this is more understandable.

0
source

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


All Articles