How can I get zoom functionality for images?

AndroidImageviewZooming

Android Problem Overview


Is there a common way to show a big image and enable the user to zoom in and out and pan the image?

Until now I found two ways:

  1. overwriting ImageView, that seems a little bit too much for such a common problem.
  2. using a webview but with less control over the overall layout etc.

Android Solutions


Solution 1 - Android

UPDATE

I've just given TouchImageView a new update. It now includes Double Tap Zoom and Fling in addition to Panning and Pinch Zoom. The code below is very dated. You can check out the github project to get the latest code.

USAGE

Place TouchImageView.java in your project. It can then be used the same as ImageView. Example:

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

If you are using TouchImageView in xml, then you must provide the full package name, because it is a custom view. Example:

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

Note: I've removed my prior answer, which included some very old code and now link straight to the most updated code on github.

ViewPager

If you are interested in putting TouchImageView in a ViewPager, refer to this answer.

Solution 2 - Android

I adapted some code to create a TouchImageView that supports multitouch (>2.1). It is inspired by the book Hello, Android! (3rd edition)

It is contained within the following 3 files 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);
	}
}

Solution 3 - Android

I used a WebView and loaded the image from the memory via

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

The WebView handles all the panning zooming and scrolling. If you use wrap_content the webview won't be bigger then the image and no white areas are shown. The WebView is the better ImageView ;)

Solution 4 - Android

In Response to Janusz original question, there are several ways to achieve this all of which vary in their difficulty level and have been stated below. Using a web view is good, but it is very limited in terms of look and feel and controllability. If you are drawing a bitmap from a canvas, the most versatile solutions that have been proposed seems to be MikeOrtiz's, Robert Foss's and/or what Jacob Nordfalk suggested. There is a great example for incorporating the android-multitouch-controller by [PaulBourke][1], and is great for having the multi-touch support and alltypes of custom views.

Personally, if you are simply drawing a canvas to a bitmap and then displaying it inside and ImageView and want to be able to zoom into and move around using multi touch, I find MikeOrtiz's solution as the easiest. However, for my purposes the code from the [Git][2] that he has provided seems to only work when his TouchImageView custom ImageView class is the only child or provide the layout params as:

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

Unfortunately due to my layout design, I needed "wrap_content" for "layout_height". When I changed it to this the image was cropped at the bottom and I couldn't scroll or zoom to the cropped region. So I took a look at the [Source][3] for ImageView just to see how Android implemented "onMeasure" and changed MikeOrtiz's to suit.

   @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);
}

Here resolveSize(int,int) is a "Utility to reconcile a desired size with constraints imposed by a MeasureSpec, where :

Parameters:

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

Returns:

 - The size this view should be."

So essentially providing a behaviour a little more similar to the original ImageView class when the image is loaded. Some more changes could be made to support a greater variety of screens which modify the aspect ratio. But for now I Hope this helps. Thanks to MikeOrtiz for his original code, great work. [1]: http://paulbourke.blogspot.com.au/2011/10/writing-multitouch-applications-on.html [2]: https://github.com/MikeOrtiz/TouchImageView [3]: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/widget/ImageView.java

Solution 5 - Android

You could also try out http://code.google.com/p/android-multitouch-controller/

The library is really great, although initially a little hard to grasp.

Solution 6 - Android

I just integrated Robert Foss's TouchImageView: it worked perfectly out of the box! Thanks!

I just modified a bit the code so I could be able to instantiate it from my layout.xml.

Just add two constructors

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

public TouchImageView(Context context) {
    super(context);
    init(context);
}

and transform the old constructor into an init method:

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

Solution 7 - Android

@Robert Foss, @Mike Ortiz, thank you very much for your work. I merged your work, and completed Robert classes for android > 2.0 with Mike additional work.

As result of my work I present Android Touch Gallery, based on ViewPager and used modificated TouchImageView. Images loading by URL and you can zoom and drag them. You can find it here https://github.com/Dreddik/AndroidTouchGallery

Solution 8 - Android

Try using ZoomView for zooming any other view.

http://code.google.com/p/android-zoom-view/ it's easy, free and fun to use!

Solution 9 - Android

Adding to @Mike's answer. I also needed double tap to restore the image to the original dimensions when first viewed. So I added a whole heap of "orig..." instance variables and added the SimpleOnGestureListener which did the trick.

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);
	}

}

Solution 10 - Android

This is a very late addition to this thread but I've been working on an image view that supports zoom and pan and has a couple of features I haven't found elsewhere. This started out as a way of displaying very large images without causing OutOfMemoryErrors, by subsampling the image when zoomed out and loading higher resolution tiles when zoomed in. It now supports use in a ViewPager, rotation manually or using EXIF information (90° stops), override of selected touch events using OnClickListener or your own GestureDetector or OnTouchListener, subclassing to add overlays, pan while zooming, and fling momentum.

It's not intended as a general use replacement for ImageView so doesn't extend it, and doesn't support display of images from resources, only assets and external files. It requires SDK 10.

Source is on GitHub, and there's a sample that illustrates use in a ViewPager.

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

Solution 11 - Android

You can try using the LayoutParams for this

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(true); ZoomOut = zoom(false);

Solution 12 - Android

@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);
        }
    });
}

and drawable folder should have bticn image file. perfectly works :)

Solution 13 - Android

Something like below will do it.

@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);
}

For viewing complete program see here: Program to zoom image in android

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJanuszView Question on Stackoverflow
Solution 1 - AndroidMike OrtizView Answer on Stackoverflow
Solution 2 - AndroidRobert FossView Answer on Stackoverflow
Solution 3 - AndroidJanuszView Answer on Stackoverflow
Solution 4 - AndroiddigiphdView Answer on Stackoverflow
Solution 5 - AndroidJacob NordfalkView Answer on Stackoverflow
Solution 6 - AndroidzontarView Answer on Stackoverflow
Solution 7 - AndroidRoman TrubaView Answer on Stackoverflow
Solution 8 - AndroidkarooolekView Answer on Stackoverflow
Solution 9 - AndroidTerenceView Answer on Stackoverflow
Solution 10 - AndroidDave MorrisseyView Answer on Stackoverflow
Solution 11 - AndroidnoobView Answer on Stackoverflow
Solution 12 - AndroidMuhammad Usman GhaniView Answer on Stackoverflow
Solution 13 - AndroidAnimesh ShrivastavView Answer on Stackoverflow