The canvas is compensated by setting the identity matrix in onDraw a custom view

I set the matrix to my canvas in the onDraw method of the user view via canvas.setMatrix(matrix); then I just draw a grid using predefined paints:

 canvas.drawRect(0,0,viewWidth,viewHeight, background); for(int i=0; i <= nRows; i++){ canvas.drawLine(0,i*cellHeight, nCols*cellWidth,i*cellHeight,lines); canvas.drawLine(i*cellWidth, 0, i*cellWidth, nRows*cellHeight, lines); if(i != nRows) canvas.drawText(""+i, i*cellWidth, (i+1)*cellHeight, text); } 

and for some reason, the entire canvas is offset about 1.5 times the height of the cell. Any idea why this is happening or how to fix it? The matrix was not initialized and, according to the documentation, supposedly should be an identity, by the way.

Many thanks!

+1
source share
2 answers

I narrowed down this behavior to the point where the original Matrix View canvas View already translated by the position of the view. However, this is not obvious if you get Matrix using Canvas.getMatrix() or View.getMatrix() . You will receive an identification matrix from these calls.

The shift of the canvas that you see most likely has the same height as the offset of the View from the top of the screen (status bar, title bar, etc.).

You are correctly using canvas.concat(matrix) instead of canvas.setMatrix(matrix) in this case and in most cases of use. If you really need the original matrix, I did this when debugging, you have to convert it manually by converting the View into your own Window :

 int[] viewLocation = new int[2]; mView.getLocationInWindow(viewLocation); mOriginalMatrix.setTranslate(viewLocation[0], viewLocation[1]); 

EDIT to answer an additional question in the comments:

To convert touch coordinates (or any screen coordinates) to Canvas characters, just do all the transformations in Matrix , and Canvas.concat() with this matrix every frame before drawing. (Or you can continue to do all the transformations directly to Canvas , as you are doing now, and use Canvas.getMatrix(mMyMatrix) to retrieve the matrix after every draw. This is deprecated, but it works.)

Then the matrix can be used to convert the original grid boundaries to those that are displayed on the screen. You do the same thing as Canvas when it draws a grid, converting the corner points of the grid into screen coordinates. Now the grid will be in the same coordinate system as your touch events:

 private final Matrix mMyMatrix = new Matrix(); // Assumes that the grid covers the whole View. private final float[] mOriginalGridCorners = new float[] { 0, 0, // top left (x, y) getWidth(), getHeight() // bottom right (x, y) }; private final float[] mTransformedGridCorners = new float[4]; @Override public boolean onTouchEvent(MotionEvent event) { if (/* User pans the screen */) { mMyMatrix.postTranslate(deltaX, deltaY); } if (/* User zooms the screen */) { mMyMatrix.postScale(deltaScale, deltaScale); } if (/* User taps the grid */) { // Transform the original grid corners to where they // are on the screen (panned and zoomed). mMyMatrix.mapPoints(mTransformedGridCorners, mOriginalGridCorners); float gridWidth = mTransformedGridCorners[2] - mTransformedGridCorners[0]; float gridHeight = mTransformedGridCorners[3] - mTransformedGridCorners[1]; // Get the x and y coordinate of the tap inside the // grid, between 0 and 1. float x = (event.getX() - mTransformedGridCorners[0]) / gridWidth; float y = (event.getY() - mTransformedGridCorners[1]) / gridHeight; // To get the tapped grid cell. int column = (int)(x * nbrColumns); int row = (int)(y * nbrRows); // Or to get the tapped exact pixel in the original grid. int pixelX = (int)(x * getWidth()); int pixelY = (int)(y * getHeight()); } return true; } @Override protected void onDraw(Canvas canvas) { // Each frame, transform your canvas with the matrix. canvas.save(); canvas.concat(mMyMatrix); // Draw grid. grid.draw(canvas); canvas.restore(); } 

Or an obsolete way to get a matrix that is still working and might require fewer changes:

 @Override protected void onDraw(Canvas canvas) { canvas.save(); // Transform canvas and draw the grid. grid.draw(canvas); // Get the matrix from canvas. Can be used to transform // corners on the next touch event. canvas.getMatrix(mMyMatrix); canvas.restore(); } 
+6
source

I found a solution to this problem, if not the cause of the problem.

Replacing the string canvas.setMatrix(matrix); on canvas.concat(matrix); did the trick. I think this is partly due to the fact that the installation of the matrix changes the origin of the canvas, which is calculated in relation to the height and width of the screen, and not to the view. But there are secrets, because it also magically solves some other problems that I had.

EDIT

It turns out that one of the consequences of this is that event.getX() and event.getY() in the onTouchEvent(MotionEvent event) method start to return integers that are hard to handle. My custom view is for creating a scalable grid of squares, which I can click to get the coordinate of the square. Using this method, I am trying to get the coordinate (x, y) of the click event ( MotionEvent.ACTION_UP case in the onTouchEvent method).

I am trying to get the x coordinate, for example via

 int x = (int) ((start.x - dspl.x)/(cellWidth*scaleFactor)); 

I take into account how much I tinted the screen ( dspl gives the total offset that has occurred so far) and the amount I increased ( scaleFactor gives the total zoom that happened). But this does not take into account the points at which successive scaling occurred.

Any good ideas on how to get the right coordinates?

+1
source

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


All Articles