2012-07-05 5 views
7

He estado atascado en este problema durante ocho horas, así que pensé que era hora de obtener ayuda.Lienzo pellizcar-Zoom a punto dentro de los límites

Antes de comenzar mi problema, solo dejaré saber que he visitado este sitio y Google, y ninguna de las respuestas que he encontrado me ha ayudado. (This es uno, another y another.)

Este es el trato: Tengo una clase que se extiende SurfaceView (vamos a llamarlo MySurface) y anula muchos métodos en ella. Normalmente, dibuja varios cuadrados y cuadros de texto, lo cual está bien. Tan pronto como un usuario comienza a tocar, se convierte en Bitmap, luego dibuja cada cuadro hasta que el usuario lo suelte.

Aquí está el problema: quiero implementar una funcionalidad tal que el usuario pueda colocar dos dedos en la pantalla, pellizcar para hacer zoom, y también para desplazarse (pero SOLAMENTE con dos dedos hacia abajo).

me encontré con un par de implementaciones de pellizcar para hacer zoom y ellas adaptada a mi objeto Canvas en MySurface a través de lo siguiente:

public void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 

    canvas.save(); 

    canvas.scale(mScaleVector.z, mScaleVector.z); // This is the scale factor as seen below 
    canvas.translate(mScaleVector.x, mScaleVector.y); // These are offset values from 0,0, both working fine 

    // Start draw code 

    // ... 

    // End draw code 

    canvas.restore(); 
} 
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
    @Override 
    public boolean onScale(ScaleGestureDetector detector) { 
     float factor = detector.getScaleFactor(); 
     if (Math.abs(factor - 1.0f) >= 0.0075f) { 
      mScaleVector.z *= factor; 
      mScaleVector.z = Math.max(MIN_ZOOM, Math.min(mScaleVector.z, MAX_ZOOM)); 
     } 

     // ... 

     invalidate(); 

     return true; 
    } 
} 
public boolean onTouchEvent(MotionEvent event) { 
    int action = event.getAction() & MotionEvent.ACTION_MASK; 
    int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
    if (event.getPointerCount() == 2) { 
     if (action == MotionEvent.ACTION_POINTER_DOWN && pointerIndex == 1) { 
      // The various pivot coordinate codes would belong here 
     } 
    } 

    detector.onTouchEvent(event); // Calls the Scale Gesture Detector 
    return true; 
} 

Si bien ambos elementos funcionan bien - el desplazamiento de ida y vuelta y el pellizco -to-zoom: hay un gran problema. La función de pellizcar para hacer zoom, cuando se utiliza, se acerca al punto 0,0, en lugar de acercarse al punto del dedo.

He intentado muchas maneras de solucionar este problema:

  • Usando canvas.scale(mScaleVector.z, mScaleVector.z, mScaleVector.x, mScaleVector.y);; obviamente, esto produce resultados no deseados ya que los valores mScaleVector xey son 0-offsets.
  • Gestionando una coordenada "pivote" que usa la misma compensación que el método translate(), pero esto produce el mismo problema 0,0, o salta cuando se toca la vista.
  • Numerosas otras cosas ... He hecho mucho con la coordenada de pivote antes mencionada, tratando de basar su ubicación en el primer toque del usuario, y moviéndolo en relación con ese toque cada gesto sucesivo.

Además, este lienzo debe estar delimitado, por lo que el usuario no puede desplazarse para siempre. Sin embargo, cuando uso el método .scale(sx, sy, px, py), empuja las cosas más allá de los límites establecidos en .translate().

Estoy ... bastante abierto a cualquier cosa en este momento. Sé que esta funcionalidad se puede agregar, como se ve en la galería de Android 4.0 (cuando se ve una sola imagen). Intenté rastrear el código fuente que maneja esto, en vano.

+0

Personalmente, manejo las dos posiciones de los dedos directamente usando 'onTouchEvent'. Con esto, puede encontrar fácilmente el punto medio y la escala requerida. – Doomsknight

+0

puede por favor publicar la solución si ha resuelto el problema que estoy enfrentando el mismo problema, y ​​mi cliente es .... :(, o puede decirme cómo ha resuelto el problema. Gracias – AkashG

Respuesta

13

Aquí está el código que utilizo para implementar el zoom de pellizco en ImageView usando ScaleGestureDetector. Con poca o ninguna modificación, debería poder usarlo también, ya que también puede usar mariquitos de transformación para dibujar en un Canvas.

@Override 
public boolean onScale(ScaleGestureDetector detector) { 
    float mScaleFactor = (float) Math.min(
     Math.max(.8f, detector.getScaleFactor()), 1.2); 
    float origScale = saveScale; 
    saveScale *= mScaleFactor; 
    if (saveScale > maxScale) { 
     saveScale = maxScale; 
     mScaleFactor = maxScale/origScale; 
    } else if (saveScale < minScale) { 
     saveScale = minScale; 
     mScaleFactor = minScale/origScale; 
    } 
    right = width * saveScale - width 
      - (2 * redundantXSpace * saveScale); 
    bottom = height * saveScale - height 
      - (2 * redundantYSpace * saveScale); 
    if (origWidth * saveScale <= width 
      || origHeight * saveScale <= height) { 
     matrix.postScale(mScaleFactor, mScaleFactor, width/2, height/2); 
     if (mScaleFactor < 1) { 
      matrix.getValues(m); 
      float x = m[Matrix.MTRANS_X]; 
      float y = m[Matrix.MTRANS_Y]; 
      if (mScaleFactor < 1) { 
       if (Math.round(origWidth * saveScale) < width) { 
        if (y < -bottom) 
         matrix.postTranslate(0, -(y + bottom)); 
        else if (y > 0) 
         matrix.postTranslate(0, -y); 
       } else { 
        if (x < -right) 
         matrix.postTranslate(-(x + right), 0); 
        else if (x > 0) 
         matrix.postTranslate(-x, 0); 
       } 
      } 
     } 
    } else { 
     matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY()); 
     matrix.getValues(m); 
     float x = m[Matrix.MTRANS_X]; 
     float y = m[Matrix.MTRANS_Y]; 
     if (mScaleFactor < 1) { 
      if (x < -right) 
       matrix.postTranslate(-(x + right), 0); 
      else if (x > 0) 
       matrix.postTranslate(-x, 0); 
      if (y < -bottom) 
       matrix.postTranslate(0, -(y + bottom)); 
      else if (y > 0) 
       matrix.postTranslate(0, -y); 
     } 
    } 
    return true; 
} 

En mi caso, calculé los valores neccesary en el método de la Vista onMeasure(), es posible que desee hacer esto en otro lugar en su SurfaceView

width = MeasureSpec.getSize(widthMeasureSpec); // Change this according to your screen size 
height = MeasureSpec.getSize(heightMeasureSpec); // Change this according to your screen size 

// Fit to screen. 
float scale; 
float scaleX = (float) width/(float) bmWidth; 
float scaleY = (float) height/(float) bmHeight; 
scale = Math.min(scaleX, scaleY); 
matrix.setScale(scale, scale); 
setImageMatrix(matrix); 
saveScale = 1f; 
scaleMappingRatio = saveScale/scale; 

// Center the image 
redundantYSpace = (float) height - (scale * (float) bmHeight); 
redundantXSpace = (float) width - (scale * (float) bmWidth); 
redundantYSpace /= (float) 2; 
redundantXSpace /= (float) 2; 

matrix.postTranslate(redundantXSpace, redundantYSpace); 

origWidth = width - 2 * redundantXSpace; 
origHeight = height - 2 * redundantYSpace; 
right = width * saveScale - width - (2 * redundantXSpace * saveScale); 
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale); 
setImageMatrix(matrix); 

Una pequeña explicación:

saveScale es la relación de escala actual de Bitmap

mScaleFactor es th El factor con el que debe multiplicar su relación de escala.

maxScale y minScale pueden ser valores constantes.

width y height son las dimensiones de la pantalla.

redundantXSpace y redundantYSpace son el vacío entre los bordes de la imagen y bordes de la pantalla ya que la imagen está centrado cuando en ella es más pequeño que la pantalla

origHeight y origWidth son los tamaños de mapa de bits

matrix es la matriz de transformación actual utilizada para dibujar el mapa de bits

El truco es que cuando escaneé por primera vez y centré la imagen en la inicialización, seleccioné esa relación de escala como 1 y con scaleMappingRatio Cartografié los valores de escala reales de la imagen en relación con eso.

+2

Oh hombre, después de revisar y probando a fondo aspectos de tu código, ¡descubrí que las transformaciones de matriz eran la clave! ¡Te compraría una cerveza si pudiera! A +, buen señor. – Eric

+1

De nada, pasé mucho tiempo averiguando el camino correcto también. , cuando comencé a escribir el código anterior, pensé que valía la pena compartirlo. –

+0

@ AdamL.Mónos buen trabajo Adam, ¡salud! –

-1
protected override void OnDraw(Canvas canvas) 
    { 
     canvas.Save(); 

     int x = (int)(canvas.Width * mScaleFactor - canvas.Width)/2; 
     int y = (int)(canvas.Height * mScaleFactor - canvas.Height)/2; 

     if (mPosX > (canvas.Width + x - 50)) 
     { 
      mPosX = canvas.Width + x - 50; 
     } 
     if (mPosX < (50 - canvas.Width - x)) 
     { 
      mPosX = 50 - canvas.Width - x; 
     } 
     if (mPosY > (canvas.Height + y - 50)) 
     { 
      mPosY = canvas.Height + y - 50; 
     } 
     if (mPosY < (50 - canvas.Height - y)) 
     { 
      mPosY = 50 - canvas.Height - y; 
     } 

     canvas.Translate(mPosX, mPosY); 
     canvas.Scale(mScaleFactor, mScaleFactor, canvas.Width/2, canvas.Height/2); 
     base.OnDraw(canvas); 
     canvas.Restore(); 
    } 

Este es el código MonoDroid, pero se puede convertir fácilmente a JAVA. Puede elegir cualquier valor para su límite (aquí hay 50 píxeles de margen)

+0

Aquí hay una biblioteca que hará esto. https://stackoverflow.com/questions/6578320/how-to-apply-zoom-drag-and-rotation-to-an-image-in-android/47861501 # 47861501 – user2288580

Cuestiones relacionadas