Холст смещается путем установки матрицы идентичности в onDraw пользовательского представления

Я установил матрицу для моего холста в onDraw метод пользовательского просмотра через canvas.setMatrix(matrix); тогда я просто рисую сетку, используя предопределенные краски:

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

и по какой-то причине весь холст смещается примерно в 1,5 раза выше высоты ячейки. Есть идеи, почему это происходит или как это исправить? Матрица не была инициализирована и, согласно документации, кстати, должна быть идентичностью.

Спасибо большое!

2 ответа

Решение

Я сузил это поведение до того, что оригинал Matrix из ViewХолст уже переведен по позиции вида. Это не очевидно, однако, если вы получаете Matrix с помощью Canvas.getMatrix(), или же View.getMatrix(), Вы получите идентификационную матрицу из этих звонков.

Смещение холста, которое вы видите, скорее всего, точно такой же высоты, как Viewсмещение от верхней части экрана (строка состояния, строка заголовка и т. д.).

Вы правильно используете canvas.concat(matrix) вместо canvas.setMatrix(matrix) в этом случае использования, и большинство случаев использования. Если вам действительно нужна оригинальная матрица, которую я сделал при отладке, вы должны преобразовать ее вручную с помощью Viewперевод в своем собственном Window:

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

РЕДАКТИРОВАТЬ, чтобы ответить на дополнительный вопрос в комментариях:

Чтобы преобразовать сенсорные координаты (или любые экранные координаты) в соответствие с координатами Canvasпросто сделайте все преобразования в Matrix вместо этого и Canvas.concat() с этой матрицей каждый кадр перед рисованием. (Или вы можете продолжать делать все преобразования непосредственно в Canvas как вы делаете сейчас, и использовать Canvas.getMatrix(mMyMatrix) получить матрицу после каждого розыгрыша. Это устарело, но это работает.)

Затем матрицу можно использовать для преобразования ваших исходных границ сетки в те, которые нарисованы на экране. По сути, вы делаете то же самое, что и Canvas делает, когда рисует сетку, преобразовывая угловые точки сетки в координаты экрана. Теперь сетка будет в той же системе координат, что и ваши сенсорные события:

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

Или устаревший способ получить матрицу, которая все еще работает и, возможно, потребует меньше изменений:

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

Я нашел решение этой проблемы, если не причина проблемы.

Замена линии canvas.setMatrix(matrix); с canvas.concat(matrix); сделал трюк. Я думаю, что это частично связано с тем фактом, что установка матрицы изменяет происхождение холста, который будет рассчитываться относительно высоты и ширины экрана, а не вида. Но есть загадки, потому что это также волшебным образом решило некоторые другие проблемы, которые у меня были.

РЕДАКТИРОВАТЬ

Оказывается, одно из последствий таких действий заключается в том, что event.getX() а также event.getY() в onTouchEvent(MotionEvent event) метод начать возвращать целые числа, с которыми трудно иметь дело. Мой пользовательский вид предназначен для создания масштабируемой, панорамируемой сетки квадратов, по которой я могу щелкнуть и получить координату квадрата. Используя этот метод, я пытаюсь получить (x,y) координату события клика (MotionEvent.ACTION_UP дело в onTouchEvent метод).

Я пытаюсь получить координату х, например, с помощью

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

Я учел сколько я панорамировал экран (dspl дает общее смещение, которое произошло до сих пор) и сумму, которую я увеличил (scaleFactor дает общее масштабирование, которое произошло). Но это не учитывает моменты, в которых происходило последовательное увеличение.

Любые хорошие идеи о том, как получить правильные координаты?

Другие вопросы по тегам