#android #bitmap #zooming #pan #android-canvas
#Android #растровое изображение #масштабирование #панорамирование #android-холст
Вопрос:
Может кто-нибудь, пожалуйста, помочь мне в нескольких вещах, касающихся mapview, который я пытаюсь создать. У меня есть программа визуализации, которая берет холст и рисует на нем растровое изображение. Я создал представление и в конструкторе я создаю:
bitmap = Bitmap.createBitmap(windowWidth, windowHeight, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
drawable = new BitmapDrawable(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
this.Render();
В методе onDraw у меня есть это:
super.onDraw(canvas);
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
drawable.draw(canvas);
canvas.restore();
Теперь я хотел бы реализовать панорамирование и масштабирование (в настоящее время я работаю без сохраненных плиток)
Вот мой onTouch:
mScaleDetector.onTouchEvent(ev) — масштабируемый детектор.onTouchEvent(ev);
final int action = ev.getAction();
switch (action amp; MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
this.hasPanned = false;
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX = dx;
mPosY = dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
if (this.mDownTouchX != this.mLastTouchX amp;amp; this.mDownTouchY != mLastTouchY)
this.hasPanned = true;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() amp; MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
Вот чего бы я хотел, и проблемы, которые у меня есть:
- При панорамировании я хотел бы рассчитать расстояние в пикселях, чтобы я мог сообщить своему средству визуализации, когда оно снова отобразит изображение. Как это достигается? Запоминая координаты в ACTION_DOWN и сравнивая с новыми координатами в ACTION_UP?
- Когда панорамирование завершено (и изображение повторно визуализировано), я хотел бы вернуть холст (изображение) в исходное положение. В настоящее время он остается открытым (из-за canvas.translate?)
- Как мне обрабатывать жесты масштабирования? С помощью этого кода при уменьшении масштаба пальцами изображение «перетаскивается» в верхний левый угол…
Я новичок в Android (и Java), поэтому любая помощь приветствуется.
Комментарии:
1. может ли кто-нибудь, пожалуйста, просто объяснить, почему, когда я увеличиваю масштаб, изображение приклеивается к верхнему левому углу?
2. Не совсем уверен, как это связано с
MapView
… можете ли вы подробнее рассказать об этом?
Ответ №1:
Если ваш код масштабирования увеличивает холст, но перемещает его неправильно, я подозреваю, что вы вызываете неправильный метод Canvas.scale(). Используемый вами метод, похоже, —
scale(float sx, float sy)
и я думаю, вам нужно использовать —
scale(float sx, float sy, float px, float py)
очевидно, что sx и sy — это масштабные коэффициенты x и y (обычно я устанавливаю для них одинаковое значение). px и py — это центральные точки для операции масштабирования, например, средняя точка между событиями движения.ACTION_DOWN и MotionEvent.Координаты события ACTION_POINTER_DOWN.
В моем MotionEvent.Обработчик ACTION_POINTER_DOWN, который я вызываю —
midPoint(mid, event);
mid определяется как —
private PointF mid = new PointF();
и функция средней точки является —
private void midPoint(PointF point, MotionEvent event)
{
float x = event.getX(0) event.getX(1);
float y = event.getY(0) event.getY(1);
point.set(x / 2, y / 2);
}
итак, в моем MotionEvent.Обработчик ACTION_MOVE, который я бы вызвал —
Canvas.scale(sx, sy, mid.x, mid.y);
Комментарии:
1. спасибо ответчику Дейву! Не могли бы вы, пожалуйста, посоветовать мне дополнительную проблему. У меня есть карта на экране. Я знаю центральную точку. Когда я выполняю масштабирование, я знаю среднюю точку. Изображение растягивается или сжимается, а центральная точка перемещается на размер средней точки, а палец, который находится дальше от средней точки, приближается к средней точке или удаляется от нее. Это означает, что мне пришлось бы вычислять центр на основе жеста масштабирования. Я надеюсь, вы понимаете, что я имею в виду. Я проверил карты Google для Android, и они сохраняют центральную точку, которая неверна… Есть предложения?
2. Я думаю, у вас возникли проблемы с масштабированием, влияющим на координаты X, Y события касания. Что вам нужно сделать, это использовать ПЕРЕВЕРНУТУЮ матрицу для отображения событий X, Y в обратном порядке, например —
float[] pts = new float[2]; pts[0] = event.getX(); pts[1] = event.getY(); Matrix invertedMatrix = new Matrix(); transMatrix.invert(invertedMatrix); invertedMatrix.mapPoints(pts); int invX = pts[0]; int invY = pts[1];
, а затем использовать преобразованные X, Y для определения вашей реальной средней точки.
Ответ №2:
1) Да, в ACTION_DOWN просто сделайте
PontF downAt = new PointF(event.getX(), event.getY());
и в ACTION_UP вычислите разницу между пикселями.
2) MapView работает иначе, чем ImageView. Для просмотра карты вы бы использовали
mapView.getController().animateTo(GeoPoint);
но я думаю, вы действительно хотите создать подкласс из ImageView и переопределить метод onDraw (Canvas холст) для прямого доступа к холсту. Затем используйте матрицу для обработки перевода и масштабирования, например
imageView.setScaleType(ImageView.ScaleType.MATRIX);
Matrix imageMatrix = new Matrix();
imageMatrix.postTranslate(xMove, yMove);
imageMatrix.postScale(scaleX, scaleY, midX, midY);
imageView.setImageMatrix(imageMatrix);
3) Для обработки жестов масштабирования вам необходимо обработать события ACTION_POINTER_DOWN, ACTION_MOVE и ACTION_POINTER_UP. Здесь есть действительно хороший учебник —
Как использовать мультитач в Android
Будьте осторожны при МАСШТАБИРОВАНИИ изображения, для любого последующего рисования на нем координаты X, Y также должны быть масштабированы, для этого используйте метод Matrix mapPoints (Pts).
Комментарии:
1. опубликованный мной код (класс MyMapView) расширяет представление. Я пытался использовать код из этого примера, но я не могу заставить его работать в моем классе, который расширяет представление. Масштабирование с помощью моего кода работает, просто изображение «приклеивается» к верхнему левому углу при выполнении масштабирования. После того, как будет отпущен зум (жест ущипка), я снова отрисоваю холст. Также … как мне реализовать двойное нажатие и двухкнопочное нажатие в моем коде? В представлении нет методов, поэтому, я думаю, мне нужно реализовать gesturedetector? Двигаюсь ли я в правильном направлении?
2. Это отдельный вопрос к тому, который вы опубликовали. Пожалуйста, отправьте отдельный вопрос, если хотите получить ответы на события tap.