How do I correctly translate pixel coordinates to

2019-04-21 20:47发布

问题:

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?

回答1:

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();


回答2:

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];


回答3:

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.



回答4:

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.



回答5:

if you are using scrolling, then the actual y on the canvas is

float y = event.getY() + arg0.getScrollY();


回答6:

What I do in these situations is just:

  1. put a listener on whatever View/ViewGroup whose coordinates you wanna get by click
  2. Make it store them locally
  3. Whoever wants to access them should just request them via helper method.

And translation is done...