Por lo tanto, estoy ampliando un SurfaceView
personalizado y estoy intentando que tenga una función de zoom de pellizco y desplazamiento.¿Cómo puedo poner límites en la matriz de traducción de mi lienzo?
Cómo desplazarse:
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// subtract scrolled amount
matrix.postTranslate(-distanceX, -distanceY);
rebound();
invalidate();
// handled
return true;
}
Cómo i zoom:
@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;
}
(de referencia que utiliza este código para 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
}
Actualmente ambos métodos son funcionando bien El zoom se detiene en un valor de escala mínimo (entre 0f y 1f) y máximo (actualmente siempre 1f) correctamente.
campos globales utilizadas:
drawW
,drawH
=float
, el tamaño en píxeles de los datos de la lona en escala = 1.parentW
,parentH
=float
, el tamaño en píxeles de la vista visible .matrix
=android.graphics.Matrix
.
El problema es la "rebound()
" (tal vez necesita un nombre mejor, je) método que estoy tratando de poner en práctica que forzaría automáticamente el contenido de permanecer a la vista.
He intentado varios métodos para tratar de calcular dónde deben estar los límites (dentro del rectángulo (0, 0, parentW, parentH)) y traducir la matriz "atrás" cuando se va demasiado lejos.
Esto es lo que tengo actualmente, que definitivamente no funciona, sino que lo empujo más hacia la derecha. Siento que el problema es mi matemática, no mi idea. ¿Alguien puede encontrar un código más simple o más limpio que traduzca la matriz al límite si está demasiado lejos y/o solucione los problemas con mi intento de esta implementación? El caso de los cheques utilizados para hacer que aparece en la parte superior izquierda
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);
}
Una versión anterior que escribí antes de la matriz era un campo (acabo de utilizar canvas.scale()
continuación canvas.translate()
en onDraw
) que funcionó:
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;
}
}
Estoy usando la nueva forma para que pueda escalar correctamente centrado en detector.getFocusX()
, detector.getFocusY()
.
ACTUALIZACIÓN: He cambiado el método de lo que es ahora. Funciona solo de alguna manera, sigue limitando la dirección y fuera del centro y está mal después de cambiar los niveles de zoom. También lo hice "preScale" y "postTranslate" para que (tal como lo entiendo) siempre debería estar aplicando la escala, luego la traducción y no mezclarlos.
ACTUALIZACIÓN FINAL: Funciona ahora.He aquí un método rebound
trabajar con comentarios:
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);
}
Se niega cualquier desplazamiento que no quiero traduciendo de nuevo a los límites. Se cancela por completo el desplazamiento si el contenido es demasiado pequeño, lo que obliga al contenido a estar en la parte superior izquierda.
¡Tienes razón! La vista no es todo el tamaño de la pantalla. ¿Las transformaciones de la matriz son relativas a la pantalla en lugar de la vista del lienzo? Asumí que tendría más sentido que sea relativo al lienzo y no a la pantalla ... – Ribose
La matriz es relativa al lienzo pero se ancla en la posición 0,0, por lo que duplicar la escala y dibujar algo a 5,5 lo hará hacer que parezca estar en 2.5,2.5 Encontré que en algunos casos el tamaño de la vista puede confundirse y tratar de usar el tamaño de la pantalla como guía, no la vista real que está manipulando. Me costó un montón de prueba y error hacer que mi escala para el lienzo jugara bien con mi escala para la posición de los objetos en el lienzo. – ScouseChris
¡Gracias! Con mucho ensayo y error, logré que finalmente funcionara. – Ribose