I am capturing a MotionEvent
for a long click in an Android SurfaceView
using a GestureListener
. I then need to translate the coordinates of the MotionEvent
to canvas coordinates, from which I can generate custom map coordinates (not Google Maps).
From what I have read, I take that given MotionEvent e
, e.getX()
and e.getY()
get pixel coordinates. How can I convert these coordinates to the SurfaceView
's canvas coordinates?
Here is my GestureListener
for listening for long clicks:
/**
* Overrides touch gestures not handled by the default touch listener
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public void onLongPress(MotionEvent e) {
Point p = new Point();
p.x = (int) e.getX();
p.y = (int) e.getY();
//TODO translate p to canvas coordinates
}
}
Thanks in advance!
Edit:
Does this have to do with screen size/resolution/depth and the canvas' Rect object?
If i understand correctly you have a canvas View inside surfaceview. If so
try VIEW.getLeft() | getTop()
that returns the left | top position of the view relative to it's parent.
float x= e.getX() - canvasView.getLeft();
float y= e.getY() - canvasView.getTop();
You might try using the MotionEvent.getRawX()/getRawY()
methods instead of the getX()/getY()
.
// get the surfaceView's location on screen
int[] loc = new int[2];
surfaceView.getLocationOnScreen(loc);
// calculate delta
int left = e.getRawX()-loc[0];
int top = e.getRawY()-loc[1];
Well, you have the screen px from the display in x,y, and you can call the canvas px via:
Canvas c = new Canvas();
int cx = c.getWidth();
int cy = c.getHeight();
...
Display display = getWindowManager().getDefaultDisplay();
int sx = display.getWidth();
int sy = display.getHeight();
Then, you can do the math to map the screen touch view , with the given px in both the view and the screen.
canvasCoordX = p.x*((float)cx/(float)sx);
canvasCoordY = p.y*((float)cy/(float)sy);
See http://developer.android.com/reference/android/view/WindowManager.html for more info on the screen manager. I think it needs to be initialized inside an activity to work.
I recently came upon this problem while doing something very similiar and after some trial and error and lots of googling I ended up adapting this answer (https://stackoverflow.com/a/9945896/1131180) :
(e is a 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 it is more clear this way.
if you are using scrolling, then the actual y on the canvas is
float y = event.getY() + arg0.getScrollY();
What I do in these situations is just:
- put a listener on whatever View/ViewGroup whose coordinates you wanna get by click
- Make it store them locally
- Whoever wants to access them should just request them via helper method.
And translation is done...