Как найти абсолютную позицию клика при увеличении - PullRequest
29 голосов
/ 18 ноября 2011

Пожалуйста, смотрите каждый раздел ниже для описания моей проблемы, описанной тремя отдельными способами.Надеюсь, это должно помочь людям ответить.

Проблема: Как найти пару координат, выраженную в холсте / пространстве пользователя, когда у вас есть только координата, выраженная в виде увеличенного изображения, учитываяисходная точка шкалы и коэффициент масштабирования?

Практическая проблема:

В настоящее время я пытаюсь воспроизвести функцию масштабирования, используемую в таких приложениях, как галерея / карты, когдаВы можете ущипнуть, чтобы увеличить / уменьшить масштаб, когда зум перемещается к средней точке пинча.

Вниз я сохраняю центральную точку увеличения (которая находится в координатах X, Y на основе текущего экрана).Затем я получаю эту функцию, когда обнаруживается жест «масштаба»:

class ImageScaleGestureDetector extends SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if(mScaleAllowed) 
            mCustomImageView.scale(detector.getScaleFactor(), mCenterX, mCenterY);
        return true;
    }
}   

Функция масштабирования CustomImageView выглядит следующим образом:

public boolean scale(float scaleFactor, float focusX, float focusY) {
    mScaleFactor *= scaleFactor;

    // Don't let the object get too small or too large.
    mScaleFactor = Math.max(MINIMUM_SCALE_VALUE, Math.min(mScaleFactor, 5.0f));

    mCenterScaleX = focusX;
    mCenterScaleY = focusY;

    invalidate();

    return true;
}

Рисунок масштабированного изображениядостигается путем переопределения метода onDraw, который масштабирует холст вокруг центра и рисует на нем изображение.

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    canvas.translate(mCenterScaleX, mCenterScaleY);
    canvas.scale(mScaleFactor, mScaleFactor);
    canvas.translate(-mCenterScaleX, -mCenterScaleY);
    mIcon.draw(canvas);
    canvas.restore();
}

Все это прекрасно работает при масштабировании из ScaleFactor 1, это потому, что начальные mCenterX и mCenterY являются координатами, которыеоснованы на экране устройства.10, 10 на устройстве - 10, 10 на холсте.

После того как вы уже увеличили масштаб, затем в следующий раз, когда вы щелкнете позицию 10, 10, он больше не будет соответствовать 10, 10 на холсте из-замасштабирование и преобразование, которое уже было выполнено.

Проблема в абстракции:

На рисунке ниже приведен пример операции масштабирования вокруг центральной точки A. Каждое поле представляетположение и размер представления при этом масштабном коэффициенте (1, 2, 3, 4, 5).

Example

В примере, если вы масштабировали с коэффициентом 2вокруг A, затем вы нажали на позицию B, X, Y, сообщенные как B, будут основаны на позиции экрана, а не на позиции относительно 0,0 исходного холста.

Мне нужно найти способполучения абсолютной позиции Б.

Ответы [ 3 ]

14 голосов
/ 24 ноября 2011

Итак, после перерисовки проблемы я нашел решение, которое искал. Прошло несколько итераций, но вот как я с этим справился:

Solution Description

B - Точка, центр операции шкалы

A1, A2, A3 - Точки, равные в пространстве пользователя, но разные в пространстве холста.

Вам известны значения Bx и By, потому что они всегда постоянны независимо от масштабного коэффициента (это значение известно как в пространстве холста, так и в пространстве пользователя).

Вы знаете Ax & Ay в пространстве пользователя, поэтому вы можете найти расстояние между Ax до Bx и Ay до By. Это измерение находится в пространстве пользователя, чтобы преобразовать его в измерение пространства холста, просто разделите его на коэффициент масштабирования. (После преобразования в пространство холста эти строки отображаются красным, оранжевым и желтым).

Поскольку точка B постоянна, расстояние между ней и краями является постоянным (они представлены синими линиями). Это значение равно в пространстве пользователя и пространстве холста.

Вы знаете ширину холста в пространстве холста, поэтому вычитая эти два измерения пространства холста (Ax до Bx и Bx до Edge) из общей ширины, которую вы оставили, с координатами для точка А в пространстве холста:

public float[] getAbsolutePosition(float Ax, float Ay) {

    float fromAxToBxInCanvasSpace = (mCenterScaleX - Ax) / mScaleFactor;
    float fromBxToCanvasEdge = mCanvasWidth - Bx;
    float x = mCanvasWidth - fromAxToBxInCanvasSpace - fromBxToCanvasEdge;

    float fromAyToByInCanvasSpace = (mCenterScaleY - Ay) / mScaleFactor;
    float fromByToCanvasEdge = mCanvasHeight - By;
    float y = mCanvasHeight - fromAyToByInCanvasSpace - fromByToCanvasEdge;

    return new float[] { x, y };
}

Приведенный выше код и изображение описывают, когда вы нажимаете в верхнем левом углу исходного центра. Я использовал ту же логику, чтобы найти A независимо от того, в каком квадранте он был расположен и рефакторинг для следующего:

public float[] getAbsolutePosition(float Ax, float Ay) {

    float x = getAbsolutePosition(mBx, Ax);
    float y = getAbsolutePosition(mBy, Ay); 

    return new float[] { x, y };
}

private float getAbsolutePosition(float oldCenter, float newCenter, float mScaleFactor) {
    if(newCenter > oldCenter) {
        return oldCenter + ((newCenter - oldCenter) / mScaleFactor);
    } else {
        return oldCenter - ((oldCenter - newCenter) / mScaleFactor);
    }
}
2 голосов
/ 30 апреля 2013

Вот мое решение, основанное на ответе Грэма:

public float[] getAbsolutePosition(float Ax, float Ay) {
    MatrixContext.drawMatrix.getValues(mMatrixValues);

    float x = mWidth - ((mMatrixValues[Matrix.MTRANS_X] - Ax) / mMatrixValues[Matrix.MSCALE_X])
            - (mWidth - getTranslationX());

    float y = mHeight - ((mMatrixValues[Matrix.MTRANS_Y] - Ay) / mMatrixValues[Matrix.MSCALE_X])
            - (mHeight - getTranslationY());

    return new float[] { x, y };
}

параметры Ax и Ay - это точки, которые пользователь трогает через onTouch (), я владел своим экземпляром статической матрицы в классе MatrixContext для хранения предыдущегомасштабированные / переведенные значения.

1 голос
/ 18 ноября 2011

Очень жаль, что это короткий ответ, в спешке. Но я тоже недавно смотрел на это - я нашел http://code.google.com/p/android-multitouch-controller/, чтобы делать то, что вы хотите (я думаю - мне пришлось просмотреть ваше сообщение). Надеюсь это поможет. Я посмотрю сегодня вечером, если это не поможет, и посмотрю, смогу ли я помочь дальше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...