2011-07-21 5 views
8

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.

Respuesta

0

Implementé algo exactamente así para hacer rodar mi propio pellizco para acercar. Sospecho que el problema con el eje y fuera del centro puede deberse a que la vista no es del tamaño de pantalla completa, o puede que no esté cambiando las coordenadas para que coincidan con la escala.

Mi implementación calcula un factor de escala, lo aplica con: canvas.scale (pinchZoomScale, pinchZoomScale); Luego calcula el tamaño físico de la pantalla en píxeles, lo convierte en metros y finalmente aplica el factor de escala para que todos los objetos dibujados se compensen correctamente.

Esta implementación se basa en saber siempre qué debe estar en el centro de la pantalla y fijarla, sea cual sea el nivel de zoom.

+0

¡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

+0

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

+1

¡Gracias! Con mucho ensayo y error, logré que finalmente funcionara. – Ribose

Cuestiones relacionadas