photoview library how chrisbanes android zoom imageview

library - zoom animation android



¿Cómo puedo obtener la funcionalidad de zoom para las imágenes? (13)

¿Hay una forma común de mostrar una imagen grande y permitir al usuario acercar y alejar y desplazar la imagen?

Hasta ahora he encontrado dos formas:

  1. sobrescribiendo ImageView, eso parece un poco demasiado para un problema tan común.
  2. utilizando una vista web pero con menos control sobre el diseño general, etc.

ACTUALIZAR

Acabo de darle a TouchImageView una nueva actualización. Ahora incluye Double Tap Zoom and Fling además de Panning y Pinch Zoom. El código de abajo es muy anticuado. Puedes ver el proyecto github para obtener el último código.

USO

Coloque TouchImageView.java en su proyecto. Entonces se puede usar igual que ImageView. Ejemplo:

TouchImageView img = (TouchImageView) findViewById(R.id.img);

Si está utilizando TouchImageView en xml, debe proporcionar el nombre completo del paquete, ya que es una vista personalizada. Ejemplo:

<com.example.touch.TouchImageView android:id="@+id/img” android:layout_width="match_parent" android:layout_height="match_parent" />

Nota: he eliminado mi respuesta anterior, que incluía un código muy antiguo y ahora lo vinculo directamente al código más actualizado en github.

ViewPager

Si está interesado en colocar TouchImageView en un ViewPager, consulte esta respuesta.


@Robert Foss, @Mike Ortiz, muchas gracias por tu trabajo. Combiné tu trabajo y completé las clases de Robert para Android> 2.0 con el trabajo adicional de Mike.

Como resultado de mi trabajo, presento Android Touch Gallery, basado en ViewPager y utilicé TouchImageView modificado. Imágenes cargando por URL y puedes hacer zoom y arrastrarlas. Lo puedes encontrar aquí https://github.com/Dreddik/AndroidTouchGallery


Añadiendo a la respuesta de @Mike. También necesitaba un doble toque para restaurar la imagen a las dimensiones originales cuando se ve por primera vez. Así que agregué un montón de variables de instancia "orig ..." y agregué el SimpleOnGestureListener que hizo el truco.

import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.PointF; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; import android.widget.ImageView; public class TouchImageView extends ImageView { Matrix matrix = new Matrix(); // We can be in one of these 3 states static final int NONE = 0; static final int DRAG = 1; static final int ZOOM = 2; int mode = NONE; // Remember some things for zooming PointF last = new PointF(); PointF start = new PointF(); float minScale = 1f; float maxScale = 3f; float[] m; float redundantXSpace, redundantYSpace, origRedundantXSpace, origRedundantYSpace;; float width, height; static final int CLICK = 3; static final float SAVE_SCALE = 1f; float saveScale = SAVE_SCALE; float right, bottom, origWidth, origHeight, bmWidth, bmHeight, origScale, origBottom,origRight; ScaleGestureDetector mScaleDetector; GestureDetector mGestureDetector; Context context; public TouchImageView(Context context) { super(context); super.setClickable(true); this.context = context; mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); matrix.setTranslate(1f, 1f); m = new float[9]; setImageMatrix(matrix); setScaleType(ScaleType.MATRIX); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { boolean onDoubleTapEvent = mGestureDetector.onTouchEvent(event); if (onDoubleTapEvent) { // Reset Image to original scale values mode = NONE; bottom = origBottom; right = origRight; last = new PointF(); start = new PointF(); m = new float[9]; saveScale = SAVE_SCALE; matrix = new Matrix(); matrix.setScale(origScale, origScale); matrix.postTranslate(origRedundantXSpace, origRedundantYSpace); setImageMatrix(matrix); invalidate(); return true; } mScaleDetector.onTouchEvent(event); matrix.getValues(m); float x = m[Matrix.MTRANS_X]; float y = m[Matrix.MTRANS_Y]; PointF curr = new PointF(event.getX(), event.getY()); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: last.set(event.getX(), event.getY()); start.set(last); mode = DRAG; break; case MotionEvent.ACTION_MOVE: if (mode == DRAG) { float deltaX = curr.x - last.x; float deltaY = curr.y - last.y; float scaleWidth = Math.round(origWidth * saveScale); float scaleHeight = Math.round(origHeight * saveScale); if (scaleWidth < width) { deltaX = 0; if (y + deltaY > 0) deltaY = -y; else if (y + deltaY < -bottom) deltaY = -(y + bottom); } else if (scaleHeight < height) { deltaY = 0; if (x + deltaX > 0) deltaX = -x; else if (x + deltaX < -right) deltaX = -(x + right); } else { if (x + deltaX > 0) deltaX = -x; else if (x + deltaX < -right) deltaX = -(x + right); if (y + deltaY > 0) deltaY = -y; else if (y + deltaY < -bottom) deltaY = -(y + bottom); } matrix.postTranslate(deltaX, deltaY); last.set(curr.x, curr.y); } break; case MotionEvent.ACTION_UP: mode = NONE; int xDiff = (int) Math.abs(curr.x - start.x); int yDiff = (int) Math.abs(curr.y - start.y); if (xDiff < CLICK && yDiff < CLICK) performClick(); break; case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; } setImageMatrix(matrix); invalidate(); return true; // indicate event was handled } }); mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTapEvent(MotionEvent e) { return true; } }); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); bmWidth = bm.getWidth(); bmHeight = bm.getHeight(); } public void setMaxZoom(float x) { maxScale = x; } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScaleBegin(ScaleGestureDetector detector) { mode = ZOOM; return true; } @Override public boolean onScale(ScaleGestureDetector detector) { float mScaleFactor = (float) Math.min( Math.max(.95f, detector.getScaleFactor()), 1.05); 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; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); height = MeasureSpec.getSize(heightMeasureSpec); // 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 = SAVE_SCALE; origScale = scale; // Center the image redundantYSpace = (float) height - (scale * (float) bmHeight); redundantXSpace = (float) width - (scale * (float) bmWidth); redundantYSpace /= (float) 2; redundantXSpace /= (float) 2; origRedundantXSpace = redundantXSpace; origRedundantYSpace = redundantYSpace; 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); origRight = right; origBottom = bottom; setImageMatrix(matrix); } }


Acabo de integrar el TouchImageView de Robert Foss: ¡funcionó perfectamente fuera de la caja! ¡Gracias!

Acabo de modificar un poco el código para poder instanciarlo desde mi layout.xml.

Solo agrega dos constructores

public TouchImageView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public TouchImageView(Context context) { super(context); init(context); }

y transformar el viejo constructor en un método de inicio:

private void init(Context context){ //...old code ofconstructor of Robert Moss''s code }


Adapté algunos códigos para crear un TouchImageView que admite multitouch (> 2.1). Está inspirado en el libro ¡Hola, Android! (3ª edición)

Se encuentra dentro de los siguientes 3 archivos TouchImageView.java WrapMotionEvent.java EclairMotionEvent.java

TouchImageView.java

import se.robertfoss.ChanImageBrowser.Viewer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.PointF; import android.util.FloatMath; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; public class TouchImageView extends ImageView { private static final String TAG = "Touch"; // These matrices will be used to move and zoom image Matrix matrix = new Matrix(); Matrix savedMatrix = new Matrix(); // We can be in one of these 3 states static final int NONE = 0; static final int DRAG = 1; static final int ZOOM = 2; int mode = NONE; // Remember some things for zooming PointF start = new PointF(); PointF mid = new PointF(); float oldDist = 1f; Context context; public TouchImageView(Context context) { super(context); super.setClickable(true); this.context = context; matrix.setTranslate(1f, 1f); setImageMatrix(matrix); setScaleType(ScaleType.MATRIX); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent rawEvent) { WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent); // Dump touch event to log if (Viewer.isDebug == true){ dumpEvent(event); } // Handle touch events here... switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: savedMatrix.set(matrix); start.set(event.getX(), event.getY()); Log.d(TAG, "mode=DRAG"); mode = DRAG; break; case MotionEvent.ACTION_POINTER_DOWN: oldDist = spacing(event); Log.d(TAG, "oldDist=" + oldDist); if (oldDist > 10f) { savedMatrix.set(matrix); midPoint(mid, event); mode = ZOOM; Log.d(TAG, "mode=ZOOM"); } break; case MotionEvent.ACTION_UP: int xDiff = (int) Math.abs(event.getX() - start.x); int yDiff = (int) Math.abs(event.getY() - start.y); if (xDiff < 8 && yDiff < 8){ performClick(); } case MotionEvent.ACTION_POINTER_UP: mode = NONE; Log.d(TAG, "mode=NONE"); break; case MotionEvent.ACTION_MOVE: if (mode == DRAG) { // ... matrix.set(savedMatrix); matrix.postTranslate(event.getX() - start.x, event.getY() - start.y); } else if (mode == ZOOM) { float newDist = spacing(event); Log.d(TAG, "newDist=" + newDist); if (newDist > 10f) { matrix.set(savedMatrix); float scale = newDist / oldDist; matrix.postScale(scale, scale, mid.x, mid.y); } } break; } setImageMatrix(matrix); return true; // indicate event was handled } }); } public void setImage(Bitmap bm, int displayWidth, int displayHeight) { super.setImageBitmap(bm); //Fit to screen. float scale; if ((displayHeight / bm.getHeight()) >= (displayWidth / bm.getWidth())){ scale = (float)displayWidth / (float)bm.getWidth(); } else { scale = (float)displayHeight / (float)bm.getHeight(); } savedMatrix.set(matrix); matrix.set(savedMatrix); matrix.postScale(scale, scale, mid.x, mid.y); setImageMatrix(matrix); // Center the image float redundantYSpace = (float)displayHeight - (scale * (float)bm.getHeight()) ; float redundantXSpace = (float)displayWidth - (scale * (float)bm.getWidth()); redundantYSpace /= (float)2; redundantXSpace /= (float)2; savedMatrix.set(matrix); matrix.set(savedMatrix); matrix.postTranslate(redundantXSpace, redundantYSpace); setImageMatrix(matrix); } /** Show an event in the LogCat view, for debugging */ private void dumpEvent(WrapMotionEvent event) { // ... String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" }; StringBuilder sb = new StringBuilder(); int action = event.getAction(); int actionCode = action & MotionEvent.ACTION_MASK; sb.append("event ACTION_").append(names[actionCode]); if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) { sb.append("(pid ").append( action >> MotionEvent.ACTION_POINTER_ID_SHIFT); sb.append(")"); } sb.append("["); for (int i = 0; i < event.getPointerCount(); i++) { sb.append("#").append(i); sb.append("(pid ").append(event.getPointerId(i)); sb.append(")=").append((int) event.getX(i)); sb.append(",").append((int) event.getY(i)); if (i + 1 < event.getPointerCount()) sb.append(";"); } sb.append("]"); Log.d(TAG, sb.toString()); } /** Determine the space between the first two fingers */ private float spacing(WrapMotionEvent event) { // ... float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return FloatMath.sqrt(x * x + y * y); } /** Calculate the mid point of the first two fingers */ private void midPoint(PointF point, WrapMotionEvent event) { // ... float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } }

WrapMotionEvent.java

import android.view.MotionEvent; public class WrapMotionEvent { protected MotionEvent event; protected WrapMotionEvent(MotionEvent event) { this.event = event; } static public WrapMotionEvent wrap(MotionEvent event) { try { return new EclairMotionEvent(event); } catch (VerifyError e) { return new WrapMotionEvent(event); } } public int getAction() { return event.getAction(); } public float getX() { return event.getX(); } public float getX(int pointerIndex) { verifyPointerIndex(pointerIndex); return getX(); } public float getY() { return event.getY(); } public float getY(int pointerIndex) { verifyPointerIndex(pointerIndex); return getY(); } public int getPointerCount() { return 1; } public int getPointerId(int pointerIndex) { verifyPointerIndex(pointerIndex); return 0; } private void verifyPointerIndex(int pointerIndex) { if (pointerIndex > 0) { throw new IllegalArgumentException( "Invalid pointer index for Donut/Cupcake"); } } }

EclairMotionEvent.java

import android.view.MotionEvent; public class EclairMotionEvent extends WrapMotionEvent { protected EclairMotionEvent(MotionEvent event) { super(event); } public float getX(int pointerIndex) { return event.getX(pointerIndex); } public float getY(int pointerIndex) { return event.getY(pointerIndex); } public int getPointerCount() { return event.getPointerCount(); } public int getPointerId(int pointerIndex) { return event.getPointerId(pointerIndex); } }


Algo como abajo lo hará.

@Override public boolean onTouch(View v,MotionEvent e) { tap=tap2=drag=pinch=none; int mask=e.getActionMasked(); posx=e.getX();posy=e.getY(); float midx= img.getWidth()/2f; float midy=img.getHeight()/2f; int fingers=e.getPointerCount(); switch(mask) { case MotionEvent.ACTION_POINTER_UP: tap2=1;break; case MotionEvent.ACTION_UP: tap=1;break; case MotionEvent.ACTION_MOVE: drag=1; } if(fingers==2){nowsp=Math.abs(e.getX(0)-e.getX(1));} if((fingers==2)&&(drag==0)){ tap2=1;tap=0;drag=0;} if((fingers==2)&&(drag==1)){ tap2=0;tap=0;drag=0;pinch=1;} if(pinch==1) { if(nowsp>oldsp)scale+=0.1; if(nowsp<oldsp)scale-=0.1; tap2=tap=drag=0; } if(tap2==1) { scale-=0.1; tap=0;drag=0; } if(tap==1) { tap2=0;drag=0; scale+=0.1; } if(drag==1) { movx=posx-oldx; movy=posy-oldy; x+=movx; y+=movy; tap=0;tap2=0; } m.setTranslate(x,y); m.postScale(scale,scale,midx,midy); img.setImageMatrix(m);img.invalidate(); tap=tap2=drag=none; oldx=posx;oldy=posy; oldsp=nowsp; return true; } public void onCreate(Bundle b) { super.onCreate(b); img=new ImageView(this); img.setScaleType(ImageView.ScaleType.MATRIX); img.setOnTouchListener(this); path=Environment.getExternalStorageDirectory().getPath(); path=path+"/DCIM"+"/behala.jpg"; byte[] bytes; bytes=null; try{ FileInputStream fis; fis=new FileInputStream(path); BufferedInputStream bis; bis=new BufferedInputStream(fis); bytes=new byte[bis.available()]; bis.read(bytes); if(bis!=null)bis.close(); if(fis!=null)fis.close(); } catch(Exception e) { ret="Nothing"; } Bitmap bmp=BitmapFactory.decodeByteArray(bytes,0,bytes.length); img.setImageBitmap(bmp); setContentView(img); }

Para ver el programa completo, vea aquí: Programa para ampliar la imagen en Android


En respuesta a la pregunta original de Janusz, hay varias formas de lograrlo, las cuales varían en su nivel de dificultad y se detallan a continuación. El uso de una vista web es bueno, pero es muy limitado en términos de apariencia y control. Si está dibujando un mapa de bits de un lienzo, las soluciones más versátiles que se han propuesto parecen ser las de MikeOrtiz, Robert Foss y / o lo que Jacob Nordfalk sugirió. Hay un gran ejemplo para incorporar el controlador multitáctil androide de PaulBourke , y es excelente para tener el soporte multitáctil y todo tipo de vistas personalizadas.

Personalmente, si simplemente está dibujando un lienzo en un mapa de bits y luego lo muestra dentro e ImageView y quiere poder hacer zoom y moverse con el multitáctil, encuentro la solución de MikeOrtiz como la más fácil. Sin embargo, para mis propósitos, el código del Git que ha proporcionado parece funcionar solo cuando su clase personalizada de ImageView TouchImageView es el único hijo o proporciona los parámetros de diseño como:

android:layout_height="match_parent" android:layout_height="match_parent"

Desafortunadamente, debido a mi diseño de diseño, necesitaba "wrap_content" para "layout_height". Cuando lo cambié, la imagen se recortó en la parte inferior y no pude desplazarme ni hacer zoom en la región recortada. Así que eché un vistazo a la Source de ImageView solo para ver cómo implementó Android "onMeasure" y cambié el de MikeOrtiz para que se ajustara.

@Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //**** ADDED THIS ********///// int w = (int) bmWidth; int h = (int) bmHeight; width = resolveSize(w, widthMeasureSpec); height = resolveSize(h, heightMeasureSpec); //**** END ********/// // width = MeasureSpec.getSize(widthMeasureSpec); // REMOVED // height = MeasureSpec.getSize(heightMeasureSpec); // REMOVED //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; // 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; // origHeight = bmHeight; right = width * saveScale - width - (2 * redundantXSpace * saveScale); bottom = height * saveScale - height - (2 * redundantYSpace * saveScale); setImageMatrix(matrix); }

Aquí resolverSize (int, int) es una "Utilidad para conciliar un tamaño deseado con restricciones impuestas por un MeasureSpec, donde:

Parámetros:

- size How big the view wants to be - MeasureSpec Constraints imposed by the parent

Devoluciones:

- The size this view should be."

Esencialmente, proporcionar un comportamiento un poco más similar a la clase ImageView original cuando se carga la imagen. Se podrían hacer algunos cambios más para admitir una mayor variedad de pantallas que modifican la relación de aspecto. Pero por ahora espero que esto ayude. Gracias a MikeOrtiz por su código original, gran trabajo.


Esta es una adición muy tardía a este hilo, pero he estado trabajando en una vista de imagen que admite zoom y paneo y tiene un par de características que no he encontrado en ningún otro lugar. Esto comenzó como una forma de mostrar imágenes muy grandes sin causar OutOfMemoryError s, submuestreando la imagen cuando se OutOfMemoryError y OutOfMemoryError mosaicos de mayor resolución cuando se acercó. Ahora admite el uso en ViewPager , ViewPager manualmente o usa información EXIF ​​(90 ° para ), anule los eventos táctiles seleccionados mediante OnClickListener o su propio GestureDetector o OnTouchListener , OnTouchListener subclases para agregar superposiciones, encuadre mientras hace zoom y arroje impulso.

No pretende ser un reemplazo de uso general para ImageView por lo que no lo amplía y no admite la visualización de imágenes de recursos, solo recursos y archivos externos. Requiere SDK 10.

La fuente está en GitHub, y hay una muestra que ilustra el uso en un ViewPager .

https://github.com/davemorrissey/subsampling-scale-image-view



Puedes intentar usar los LayoutParams para esto

public void zoom(boolean flag){ if(flag){ int width=40; int height=40; } else{ int width=20; int height=20; } RelativeLayout.LayoutParams param=new RelativeLayout.LayoutParams(width,height); //use the parent layout of the ImageView; imageView.setLayoutParams(param); //imageView is the view which needs zooming. }

ZoomIn = zoom (verdadero); ZoomOut = zoom (falso);



Usé un WebView y cargué la imagen de la memoria a través de

webview.loadUrl("file://...")

El WebView maneja todo el zoom y el desplazamiento de la panorámica. Si usa wrap_content, la vista web no será más grande que la imagen y no se mostrarán áreas en blanco. El WebView es el mejor ImageView;)


@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageDetail = (ImageView) findViewById(R.id.imageView1); imageDetail.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { ImageView view = (ImageView) v; System.out.println("matrix=" + savedMatrix.toString()); switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: savedMatrix.set(matrix); startPoint.set(event.getX(), event.getY()); mode = DRAG; break; case MotionEvent.ACTION_POINTER_DOWN: oldDist = spacing(event); if (oldDist > 10f) { savedMatrix.set(matrix); midPoint(midPoint, event); mode = ZOOM; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; case MotionEvent.ACTION_MOVE: if (mode == DRAG) { matrix.set(savedMatrix); matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y); } else if (mode == ZOOM) { float newDist = spacing(event); if (newDist > 10f) { matrix.set(savedMatrix); float scale = newDist / oldDist; matrix.postScale(scale, scale, midPoint.x, midPoint.y); } } break; } view.setImageMatrix(matrix); return true; } @SuppressLint("FloatMath") private float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return FloatMath.sqrt(x * x + y * y); } private void midPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } }); }

y la carpeta dibujable debe tener un archivo de imagen bticn. funciona perfectamente :)