Как найти абсолютную позицию щелчка при увеличении
Обратитесь к каждому разделу ниже, чтобы описать мою проблему, описанную тремя разными способами. Надеюсь, это поможет людям ответить.
Проблема: Как вы находите пару координат, выраженных в холсте/пользовательском пространстве, когда у вас есть только координата, выраженная в увеличенном изображении, учитывая исходную масштабную шкалу и масштабный коэффициент?
Проблема на практике:
В настоящее время я пытаюсь реплицировать функции масштабирования, используемые в таких приложениях, как галерея/карты, когда вы можете ущипнуть, чтобы масштабировать/уменьшать масштаб, когда зум приближается к середине пинча.
Вниз Я сохраняю центральную точку масштабирования (которая находится в координатах 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.
Ответы
Ответ 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/, чтобы делать то, что вы хотите (я думаю - мне пришлось читать после). Надеюсь это поможет. У меня будет хороший взгляд сегодня, если это не поможет, и посмотрим, смогу ли я помочь дальше.