Итак, я расширяю пользовательский SurfaceView
и пытаюсь сделать так, чтобы он имел возможность масштабирования и прокрутки.
Как прокручивать:
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// subtract scrolled amount
matrix.postTranslate(-distanceX, -distanceY);
rebound();
invalidate();
// handled
return true;
}
Как увеличить:
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (detector.isInProgress()) {
// get scale
float factor = detector.getScaleFactor();
// Don't let the object get too small or too large.
if (factor * scale > 1f) {
factor = 1f / scale;
} else if (factor * scale < minScale) {
factor = minScale / scale;
}
// store local scale
scale *= factor;
// do the scale
matrix.preScale(factor, factor, detector.getFocusX(), detector.getFocusY());
rebound();
invalidate();
}
return true;
}
(для справки я использую этот код для onDraw
:)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.setMatrix(matrix);
// [...] stuff drawn with matrix settings
canvas.restore();
// [...] stuff drawn without matrix settings, as an overlay
}
В настоящее время оба эти метода работают хорошо.Масштабирование останавливается при минимальном (между 0f и 1f) и максимальном (в настоящее время всегда 1f) значении масштаба.
Используемые глобальные поля:
drawW
, drawH
= float
, размер в пикселях данных холста в масштабе = 1. parentW
, parentH
= float
, размер в пикселях видимого вида. matrix
= android.graphics.Matrix
.
Проблема в том, что метод * rebound()
(возможно, нуждается в более подходящем имени, хе), который я пытаюсь реализовать, будет автоматически вызыватьсодержимое, чтобы оставаться в поле зрения.
Я пробовал различные методы, чтобы попытаться вычислить, где должны быть границы (в пределах прямоугольника (0, 0, parentW, parentH)) и перевести матрицу «назад», когда оназаходит слишком далеко.
Вот то, что у меня есть в настоящее время, но оно определенно не работает, вместо этого я много толкаю его вправо.Я чувствую, что проблема в моей математике, а не в моей идее. Может ли кто-нибудь придумать более простой или понятный код, который переводит матрицу на край, если она слишком далеко, и / или исправляет проблемы с моей попыткой этой реализации? Проверки if, использованные для ее появления, появляются вверхний левый
public void rebound() {
// bounds
RectF currentBounds = new RectF(0, 0, drawW, drawH);
matrix.mapRect(currentBounds);
RectF parentBounds = new RectF(0, 0, parentW, parentH/2);
PointF diff = new PointF(0, 0);
if (currentBounds.left > parentBounds.left) {
diff.x += (parentBounds.left - currentBounds.left);
}
if (currentBounds.top > parentBounds.top) {
diff.y += (parentBounds.top - currentBounds.top);
}
if (currentBounds.width() > parentBounds.width()) {
if (currentBounds.right < parentBounds.right) {
diff.x += (parentBounds.right - currentBounds.right);
}
if (currentBounds.bottom < parentBounds.bottom) {
diff.y += (parentBounds.bottom - currentBounds.bottom);
}
}
matrix.postTranslate(diff.x, diff.y);
}
Предыдущая версия, которую я написал до матрицы, была полем (я просто использовал canvas.scale()
, затем canvas.translate()
в onDraw
), который работал:
public void rebound() {
// bounds
int boundTop = 0;
int boundLeft = 0;
int boundRight = (int)(-scale * drawW + parentW);
int boundBottom = (int)(-scale * drawH + parentH);
if (boundLeft >= boundRight) {
mScrollX = Math.min(boundLeft, Math.max(boundRight, mScrollX));
} else {
mScrollX = 0;
}
if (boundTop >= boundBottom) {
mScrollY = Math.min(boundTop, Math.max(boundBottom, mScrollY));
} else {
mScrollY = 0;
}
}
Я использую новый способ, чтобы можно было правильно масштабировать по центру detector.getFocusX()
, detector.getFocusY()
.
ОБНОВЛЕНИЕ : я изменил метод на тот, который есть сейчас.Он работает только в некоторой степени, все еще ограничивая направление y в направлении от центра и неправильно после изменения уровней масштабирования.Я также сделал это "preScale" и "postTranslate", чтобы (насколько я понимаю) он всегда применял масштаб, а затем переводил, а не смешивал их.
FINAL UPDATE : Этоработает сейчас.Вот рабочий метод rebound
с комментариями:
public void rebound() {
// make a rectangle representing what our current canvas looks like
RectF currentBounds = new RectF(0, 0, drawW, drawH);
matrix.mapRect(currentBounds);
// make a rectangle representing the scroll bounds
RectF areaBounds = new RectF((float) getLeft(),
(float) getTop(),
(float) parentW + (float) getLeft(),
(float) parentH + (float) getTop());
// the difference between the current rectangle and the rectangle we want
PointF diff = new PointF(0f, 0f);
// x-direction
if (currentBounds.width() > areaBounds.width()) {
// allow scrolling only if the amount of content is too wide at this scale
if (currentBounds.left > areaBounds.left) {
// stop from scrolling too far left
diff.x = (areaBounds.left - currentBounds.left);
}
if (currentBounds.right < areaBounds.right) {
// stop from scrolling too far right
diff.x = (areaBounds.right - currentBounds.right);
}
} else {
// negate any scrolling
diff.x = (areaBounds.left - currentBounds.left);
}
// y-direction
if (currentBounds.height() > areaBounds.height()) {
// allow scrolling only if the amount of content is too tall at this scale
if (currentBounds.top > areaBounds.top) {
// stop from scrolling too far above
diff.y = (areaBounds.top - currentBounds.top);
}
if (currentBounds.bottom < areaBounds.bottom) {
// stop from scrolling too far below
diff.y = (areaBounds.bottom - currentBounds.bottom);
}
} else {
// negate any scrolling
diff.y = (areaBounds.top - currentBounds.top);
}
// translate
matrix.postTranslate(diff.x, diff.y);
}
Он сводит на нет любую ненужную прокрутку, переводя ее обратно в границы.Он полностью отменяет прокрутку, если содержимое слишком мало, заставляя содержимое находиться в верхнем левом углу.