Lienzo Pinch-Zoom a punto dentro de límites

He estado atrapado en este problema durante ocho horas, así que pensé que era hora de obtener alguna ayuda.

Antes de comenzar mi problema, dejaré que se sepa que he pasado por este sitio y Google, y ninguna de las respuestas que he encontrado han ayudado. ( Esto es uno, otro , y otro .)

Aquí está el trato: Tengo una clase que extiende SurfaceView (vamos a llamarlo MySurface ) y anula muchos métodos en él. Normalmente, dibuja varios cuadrados y cuadros de texto, que está bien. Tan pronto como un usuario comienza a tocar, se convierte en un Bitmap , a continuación, dibuja cada marco que hasta que el usuario se libera.

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

He encontrado algunas implementaciones de pinch-to-zoom y adaptado a mi objeto Canvas en MySurface 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; } 

Mientras que ambos elementos funcionan bien – el desplazamiento de ida y vuelta y el pellizco de zoom – hay un gran problema. El pinch-to-zoom, cuando se utiliza, hace zoom en el punto 0,0 , en lugar de acercarse al punto del dedo.

He intentado muchas maneras de arreglar esto:

  • Utilizando canvas.scale(mScaleVector.z, mScaleVector.z, mScaleVector.x, mScaleVector.y); ; Obviamente, esto produce resultados no deseados, ya que los mScaleVector xey son 0-offsets.
  • Administrar una coordenada "pivot" que utiliza el mismo desplazamiento que el método translate() , pero esto produce el mismo número 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 de cada gesto sucesivo.

Además, este lienzo debe estar limitado, por lo que el usuario no puede desplazarse para siempre. Sin embargo, cuando uso el .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 (al ver una sola imagen). He intentado rastrear el código fuente que maneja esto, en vano.

2 Solutions collect form web for “Lienzo Pinch-Zoom a punto dentro de límites”

Aquí está el código que uso para implementar pinch zoom en un ImageView usando ScaleGestureDetector . Con poca o ninguna modificación debes ser capaz de usarlo también, ya que puedes usar los marcos de transformación también, para dibujar sobre 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, onMeasure() los valores necesarios en el método onMeasure() de la Vista, es posible que desee hacerlo en algún otro lugar de 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 del Bitmap de Bitmap

mScaleFactor es el factor que tiene que multiplicar su relación de escala con.

maxScale y minScale pueden ser valores constantes.

width y height son las dimensiones de la pantalla.

redundantXSpace y redundantYSpace son los espacios vacíos entre los bordes de la imagen y los bordes de la pantalla ya que la imagen está centrada cuando en ella es más pequeña que la pantalla

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

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

El truco es que cuando primero scaleMappingRatio y scaleMappingRatio la imagen en la inicialización, escogí esa proporción de escala para ser 1 y con scaleMappingRatio los valores reales de la escala de la imagen relativa a eso.

  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 puede convertirse fácilmente en JAVA. Usted puede elegir cualesquiera valores para su límite (aquí es el margen de 50 pixeles)

  • ¿Dónde puedo encontrar una biblioteca JAVA de efectos visuales y manipulación de imágenes?
  • Cómo hacer que la vista cambie de tamaño en evento táctil
  • Android: cómo crear un diagrama de árbol genealógico (visual)
  • Posición de escala incorrecta después de aplicar el efecto de zoom en el lienzo de Android
  • Color de fondo de relleno de lienzo de Android (aplicación de lona)
  • Dibujo de texto en ángulo (por ejemplo, al revés) en Android
  • Cómo dibujar un camino con un mapa de bits?
  • Centro del texto vertical y horizontalmente con lienzo
  • Dibuja un círculo transparente sobre un lienzo lleno de android
  • Asegúrese de que el área del mapa de bits sea transparente al tocar -> de nuevo
  • Cómo dibujar Bezier Curve en Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.