Подтвердить что ты не робот

Как найти абсолютную позицию щелчка при увеличении

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

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

Проблема на практике:

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

Вниз Я сохраняю центральную точку масштабирования (которая находится в координатах 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 начального холст.

Мне нужно найти способ получить абсолютное положение B.

4b9b3361

Ответ 1

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

Solution Description

B - Точка, центр операции масштабирования

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

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

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

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

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

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

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

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 для хранения предыдущих масштабированных/переведенных значений.

Ответ 3

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