Android ImageView Zoom-in and Zoom-Out

297,536

Solution 1

Make two java classes

Zoom class

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;

public class Zoom extends View {

    private Drawable image;
    ImageButton img,img1;
    private int zoomControler=20;

    public Zoom(Context context){
            super(context);

            image=context.getResources().getDrawable(R.drawable.j);
            //image=context.getResources().getDrawable(R.drawable.icon);

            setFocusable(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //here u can control the width and height of the images........ this line is very important
        image.setBounds((getWidth()/2)-zoomControler, (getHeight()/2)-zoomControler, (getWidth()/2)+zoomControler, (getHeight()/2)+zoomControler);
        image.draw(canvas);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

            if(keyCode==KeyEvent.KEYCODE_DPAD_UP){
                    // zoom in
                    zoomControler+=10;
            }
            if(keyCode==KeyEvent.KEYCODE_DPAD_DOWN){
                    // zoom out
                    zoomControler-=10;
            }
            if(zoomControler<10){
                    zoomControler=10;
            }

            invalidate();
            return true;
    }
}

make second class

import android.app.Activity;
import android.os.Bundle;

public class Zoomexample extends Activity {
   /** Called when the activity is first created. */

   @Override
   public void onCreate(Bundle icicle) {
       super.onCreate(icicle);
       setContentView(new Zoom(this));
   }
}

Solution 2

Please follow the below class, that is used for Zoom in and Zoom Out for ImageView.

import android.app.Activity;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

public class ZoomInZoomOut extends Activity implements OnTouchListener 
{
    private static final String TAG = "Touch";
    @SuppressWarnings("unused")
    private static final float MIN_ZOOM = 1f,MAX_ZOOM = 1f;
    
    // These matrices will be used to scale points of the image
    Matrix matrix = new Matrix();
    Matrix savedMatrix = new Matrix();

    // The 3 states (events) which the user is trying to perform
    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    int mode = NONE;

    // these PointF objects are used to record the point(s) the user is touching
    PointF start = new PointF();
    PointF mid = new PointF();
    float oldDist = 1f;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ImageView view = (ImageView) findViewById(R.id.imageView);
        view.setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) 
    {
        ImageView view = (ImageView) v;
        view.setScaleType(ImageView.ScaleType.MATRIX);
        float scale;

        dumpEvent(event);
        // Handle touch events here...

        switch (event.getAction() & MotionEvent.ACTION_MASK) 
        {
            case MotionEvent.ACTION_DOWN:   // first finger down only
                                                matrix.set(view.getImageMatrix());
                                                savedMatrix.set(matrix);
                                                start.set(event.getX(), event.getY());
                                                Log.d(TAG, "mode=DRAG"); // write to LogCat
                                                mode = DRAG;
                                                break;
            
            case MotionEvent.ACTION_UP: // first finger lifted

            case MotionEvent.ACTION_POINTER_UP: // second finger lifted
                
                                                mode = NONE;
                                                Log.d(TAG, "mode=NONE");
                                                break;

            case MotionEvent.ACTION_POINTER_DOWN: // first and second finger down
                
                                                oldDist = spacing(event);
                                                Log.d(TAG, "oldDist=" + oldDist);
                                                if (oldDist > 5f) {
                                                    savedMatrix.set(matrix);
                                                    midPoint(mid, event);
                                                    mode = ZOOM;
                                                    Log.d(TAG, "mode=ZOOM");
                                                }
                                                break;

            case MotionEvent.ACTION_MOVE:

                                                if (mode == DRAG) 
                                                { 
                                                    matrix.set(savedMatrix);
                                                    matrix.postTranslate(event.getX() - start.x, event.getY() - start.y); // create the transformation in the matrix  of points
                                                } 
                                                else if (mode == ZOOM) 
                                                { 
                                                    // pinch zooming
                                                    float newDist = spacing(event);
                                                    Log.d(TAG, "newDist=" + newDist);
                                                    if (newDist > 5f) 
                                                    {
                                                        matrix.set(savedMatrix);
                                                        scale = newDist / oldDist; // setting the scaling of the
                                                                                    // matrix...if scale > 1 means
                                                                                    // zoom in...if scale < 1 means
                                                                                    // zoom out
                                                        matrix.postScale(scale, scale, mid.x, mid.y);
                                                    }
                                                }
                                                break;
        }

        view.setImageMatrix(matrix); // display the transformation on screen

        return true; // indicate event was handled
    }

    /*
     * --------------------------------------------------------------------------
     * Method: spacing Parameters: MotionEvent Returns: float Description:
     * checks the spacing between the two fingers on touch
     * ----------------------------------------------------
     */
    
    private float spacing(MotionEvent event) 
    {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    /*
     * --------------------------------------------------------------------------
     * Method: midPoint Parameters: PointF object, MotionEvent Returns: void
     * Description: calculates the midpoint between the two fingers
     * ------------------------------------------------------------
     */
    
    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);
    }

    /** Show an event in the LogCat view, for debugging */
    private void dumpEvent(MotionEvent 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("Touch Events ---------", sb.toString());
    }
}

Solution 3

The other implementations here all have some kind of a flaw. so i basically mixed them up and came up with this.

Create a custom view like this:

ZoomableImageView.java:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;

public class ZoomableImageView extends ImageView
{
    Matrix matrix = new Matrix();

    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    static final int CLICK = 3;
    int mode = NONE;

    PointF last = new PointF();
    PointF start = new PointF();
    float minScale = 1f;
    float maxScale = 4f;
    float[] m;

    float redundantXSpace, redundantYSpace;
    float width, height;
    float saveScale = 1f;
    float right, bottom, origWidth, origHeight, bmWidth, bmHeight;

    ScaleGestureDetector mScaleDetector;
    Context context;

    public ZoomableImageView(Context context, AttributeSet attr)
    {
        super(context, attr);
        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)
            {
                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())
                {
                    //when one finger is touching
                    //set the mode to DRAG
                    case MotionEvent.ACTION_DOWN:
                        last.set(event.getX(), event.getY());
                        start.set(last);
                        mode = DRAG;
                        break;
                    //when two fingers are touching
                    //set the mode to ZOOM
                    case MotionEvent.ACTION_POINTER_DOWN:
                        last.set(event.getX(), event.getY());
                        start.set(last);
                        mode = ZOOM;
                        break;
                    //when a finger moves
                    //If mode is applicable move image
                    case MotionEvent.ACTION_MOVE:
                        //if the mode is ZOOM or
                        //if the mode is DRAG and already zoomed
                        if (mode == ZOOM || (mode == DRAG && saveScale > minScale))
                        {
                            float deltaX = curr.x - last.x;// x difference
                            float deltaY = curr.y - last.y;// y difference
                            float scaleWidth = Math.round(origWidth * saveScale);// width after applying current scale
                            float scaleHeight = Math.round(origHeight * saveScale);// height after applying current scale
                            //if scaleWidth is smaller than the views width
                            //in other words if the image width fits in the view
                            //limit left and right movement
                            if (scaleWidth < width)
                            {
                                deltaX = 0;
                                if (y + deltaY > 0)
                                    deltaY = -y;
                                else if (y + deltaY < -bottom)
                                    deltaY = -(y + bottom);
                            }
                            //if scaleHeight is smaller than the views height
                            //in other words if the image height fits in the view
                            //limit up and down movement
                            else if (scaleHeight < height)
                            {
                                deltaY = 0;
                                if (x + deltaX > 0)
                                    deltaX = -x;
                                else if (x + deltaX < -right)
                                    deltaX = -(x + right);
                            }
                            //if the image doesnt fit in the width or height
                            //limit both up and down and left and 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);
                            }
                            //move the image with the matrix
                            matrix.postTranslate(deltaX, deltaY);
                            //set the last touch location to the current
                            last.set(curr.x, curr.y);
                        }
                        break;
                    //first finger is lifted
                    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;
                    // second finger is lifted
                    case MotionEvent.ACTION_POINTER_UP:
                        mode = NONE;
                        break;
                }
                setImageMatrix(matrix);
                invalidate();
                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 = detector.getScaleFactor();
            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 =  width / bmWidth;
        float scaleY = height / bmHeight;
        scale = Math.min(scaleX, scaleY);
        matrix.setScale(scale, scale);
        setImageMatrix(matrix);
        saveScale = 1f;

        // Center the image
        redundantYSpace = height - (scale * bmHeight) ;
        redundantXSpace = width - (scale * bmWidth);
        redundantYSpace /= 2;
        redundantXSpace /= 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);
    }
}

Then add the image like this:

ZoomableImageView touch = (ZoomableImageView)findViewById(R.id.IMAGEID);
touch.setImageBitmap(bitmap);

Add the view like this in XML:

<PACKAGE.ZoomableImageView
android:id="@+id/IMAGEID"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

Solution 4

Simple way:

PhotoViewAttacher pAttacher;
pAttacher = new PhotoViewAttacher(Your_Image_View);
pAttacher.update();

Add below line in build.gradle:

compile 'com.commit451:PhotoView:1.2.4'

Solution 5

I have improved the answer I got from stack for flawless ZOOM (two finger) / ROTATION (two finger) / DRAG (Single finger).

enter image description here

//============================XML code==================

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.flochat.imageviewzoomforstack.MainActivity">

    <ImageView
        android:id="@+id/imageview_trash"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/trash" />

</LinearLayout>

//============================Java code==========================

public class MainActivity extends AppCompatActivity {

    ImageView photoview2;
    float[] lastEvent = null;
    float d = 0f;
    float newRot = 0f;
    private boolean isZoomAndRotate;
    private boolean isOutSide;
    private static final int NONE = 0;
    private static final int DRAG = 1;
    private static final int ZOOM = 2;
    private int mode = NONE;
    private PointF start = new PointF();
    private PointF mid = new PointF();
    float oldDist = 1f;
    private float xCoOrdinate, yCoOrdinate;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.activity_main);

        photoview2 = findViewById(R.id.imageview_trash);

        photoview2.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                ImageView view = (ImageView) v;
                view.bringToFront();
                viewTransformation(view, event);
                return true;
            }
        });
    }


    private void viewTransformation(View view, MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
            xCoOrdinate = view.getX() - event.getRawX();
            yCoOrdinate = view.getY() - event.getRawY();

                start.set(event.getX(), event.getY());
                isOutSide = false;
                mode = DRAG;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    midPoint(mid, event);
                    mode = ZOOM;
                }

                lastEvent = new float[4];
                lastEvent[0] = event.getX(0);
                lastEvent[1] = event.getX(1);
                lastEvent[2] = event.getY(0);
                lastEvent[3] = event.getY(1);
                d = rotation(event);
                break;
            case MotionEvent.ACTION_UP:
                isZoomAndRotate = false;
                if (mode == DRAG) {
                    float x = event.getX();
                    float y = event.getY();
                }
            case MotionEvent.ACTION_OUTSIDE:
                isOutSide = true;
                mode = NONE;
                lastEvent = null;
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_MOVE:
                if (!isOutSide) {
                    if (mode == DRAG) {
                        isZoomAndRotate = false;
                        view.animate().x(event.getRawX() + xCoOrdinate).y(event.getRawY() + yCoOrdinate).setDuration(0).start();
                    }
                    if (mode == ZOOM && event.getPointerCount() == 2) {
                        float newDist1 = spacing(event);
                        if (newDist1 > 10f) {
                            float scale = newDist1 / oldDist * view.getScaleX();
                            view.setScaleX(scale);
                            view.setScaleY(scale);
                        }
                        if (lastEvent != null) {
                            newRot = rotation(event);
                            view.setRotation((float) (view.getRotation() + (newRot - d)));
                        }
                    }
                }
                break;
        }
    }

    private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);
        return (float) Math.toDegrees(radians);
    }

    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (int) Math.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);
    }

}

//========================Just pass any view which you want to zoom/rotate/drag to viewTransformation() method. Very applicable for textview zoom. It will not pixelate text.

Share:
297,536
karthi
Author by

karthi

Updated on February 02, 2022

Comments

  • karthi
    karthi over 2 years

    I want to Zoom-in and Zoom-out an Android ImageView. I tried most of the samples but in all of them the image in the ImageView itself is getting Zoomed-in and Zoomed-out, while I want to Zoom-in and Zoom-out the ImageView. I want to increase the ImageView width and height while Zooming-in and reduce the ImageView width and height while Zooming-out. How do I achieve that?

  • chinna_82
    chinna_82 almost 12 years
    i got error in... image=context.getResources().getDrawable(R.drawable.j); I copy an image in drawable folder. I renamed the image to j.. I got j cannot be resolved or is not a field..
  • Muhammad Umar
    Muhammad Umar over 11 years
    whenever i tap on image it zooms. what should i do to stop it
  • Baz
    Baz over 11 years
    I would really appreciate an answer to the comment above. The same happens to me. Is there any way to stop it from zooming in on the first tap? The ImageView itself is set to centerFit.
  • Shihab Uddin
    Shihab Uddin over 11 years
    great works by @Chirag Raval.. how can i add panning effects now?
  • KhalidTaha
    KhalidTaha about 11 years
    @chinna_82, the image should be j.png and you should place it in Drawable folder. Then refresh your project and the error shouldn't be
  • Johann
    Johann almost 11 years
    Has poor response and jumps all over the place.
  • Johann
    Johann almost 11 years
    I've gone through over a dozen of these implementations and none of them have smooth behavior, including this one.
  • gta0004
    gta0004 almost 11 years
    For some reason, matrix = new Matrix(this.getImageMatrix()); line makes the image disappear for me. Do you know why?
  • SysHex
    SysHex over 10 years
    OK, best implementation so far. My main issue with this implementation is that from the get go the image is displayed at 1:1 ratio and we cannot perform zoom out. Apart from that, it is my fav implementation thus far on this thread
  • jpm
    jpm over 10 years
    Works great for me too! Thanks for sharing!
  • jpm
    jpm over 10 years
    Although I don't get why you write "fitCenter" works best, even though you call setScaleType(ScaleType.MATRIX) right in the constructor...? However. Thanks!
  • string.Empty
    string.Empty over 10 years
    its up to you what scaletype you prefer.
  • anshul
    anshul over 10 years
    Don' make fool of people who seek for an answer.This will not work if you change the scaletype to fitXy in the code where you have mentioned SCaleType to MATRIX.
  • string.Empty
    string.Empty over 10 years
    I didn't say you should change the scaletype to fitXy... And if you change the code its not my fault that it does not work. I provided you with a working solution that i've used before.
  • jpm
    jpm over 10 years
    Hey @NicolasTyler , I'm using your solution and it works great! just one question: When I change the layout where the ZoomableImageView is in, the view is resized and updated but 'loses' its last state (zoom/position) (it's zoomed out to the initial state again). Do you know where it would be best to save the last state(matrix) and where to apply it again? (zooming and scrolling to the last position) Thanks!
  • string.Empty
    string.Empty over 10 years
    Ask a new question and provide more info.
  • goRGon
    goRGon over 10 years
    If you do it inside onMeasure() the matrix will just obtain the correct value according to current View zoom default scale. And this matrix will be used only after the next interaction with user (tap for example). And when did you image disappear?
  • Johnny Doe
    Johnny Doe about 10 years
    @Baz, to avoid zooming on the first tap use case MotionEvent.ACTION_DOWN: // first finger down only matrix.set(view.getImageMatrix()); savedMatrix.set(matrix); This will copy current scaling settings to the initial matrix
  • Chirag
    Chirag over 9 years
    @user1386213 yes, you can.
  • Omid Omidi
    Omid Omidi over 9 years
    add this: ViewTreeObserver vto = getViewTreeObserver(); vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { public boolean onPreDraw() { bmHeight = getMeasuredHeight(); bmWidth = getMeasuredWidth(); return true; } });
  • sravanalakshmi.sunkara
    sravanalakshmi.sunkara about 9 years
    I was searching for matrix.set(view.getImageMatrix()); thanku @JohnnyDoe
  • Aggressor
    Aggressor about 9 years
    This looks great but I need to make some modifications and I wish it was commented :( Im brushing up on my Algebra but I've been stuck on this for two days. Any chance you could add comments, most notably to the onTouch sections and what 'saveScale' means?
  • string.Empty
    string.Empty about 9 years
    I will add comments, but its not that complicated, saveScale is the current scale value of the image, minScale being the smallest it can go and maxScale is the most the image can scale.
  • Aggressor
    Aggressor about 9 years
    Thanks for the comments this is going to help immensely. I am going to attempt to get a different version of this pinch zoom added here to. Your solution is great for situations where you allow alpha space at max zoom out, I am designing a version where you cannot zoom past the minimum ratio that would lead to having alpha space.
  • Bullionist
    Bullionist almost 9 years
    Works like a charm. Thanks a lot.
  • Dusan Dimitrijevic
    Dusan Dimitrijevic almost 9 years
    @NicolasTyler And how would i use this class in some other package and activity?
  • Atul O Holic
    Atul O Holic almost 9 years
    @Nicolas Can this zoom in and out achieved only by dragging like when I drag Image it zooms and when I drag out it resizes to original?
  • string.Empty
    string.Empty almost 9 years
    I wrote this 2 years ago, i havent tested it but it was meant to be pinch zoom and drag to move image.
  • Can Canbek
    Can Canbek over 8 years
    How do we keep the image in the screen? Because I can keep scrolling it until it is very much out of view.
  • moffeltje
    moffeltje over 8 years
    @CanCanbek I think that is what this is supposed to do. You can ask a new question if you can't figure out how to do that.
  • Nagaraj Alagusundaram
    Nagaraj Alagusundaram over 8 years
    For those who use the above code, "return FloatMath.sqrt(x * x + y * y);" replace it with "return (float)Math.sqrt(x * x + y * y);" coz FloatMath is deprecated now. Cheers
  • Satheesh
    Satheesh over 8 years
    @goRGon Hi, In my application i set image with fit to screen. And i want to zoom apart from screen size which is working fine. And I need to zoom out,drag,rotate the image which is done only outside of the screen not inside of the screen. pls help me how to do it.
  • Satheesh
    Satheesh over 8 years
    @NicolasTyler Hi this is working fine what i want u gave here, thank you very much and i need image rotation with the same feature. could u please provide this as soon as possible.
  • YakirNa
    YakirNa over 8 years
    What is the meaning of "alpha space"?
  • YakirNa
    YakirNa over 8 years
    It work's great and really is a much cleaner solution than the original. One thing you missed though is that the scale gesture always scale to the top left corner of the image, instead of in between the fingers. i'm going to try and fix it, so if you have any hints for me it would be much appreciated. Thanks!
  • YakirNa
    YakirNa over 8 years
    I found that replacing mMatrix.postScale(scaleFactor, scaleFactor); with mMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); inside public boolean onScale(ScaleGestureDetector detector) is doing the trick when you zoom in. more code is needed to support zoom out. but for now, it is enough for me.
  • jmtsilva69
    jmtsilva69 over 8 years
    For better perform change this line: Bitmap bm = BitmapFactory.decodeResource(context.getResources(), R.mipmap.myinfolinks_splash); for this one: ImageView img = (ImageView) findViewById(R.id.action_infolinks_splash);
  • jmtsilva69
    jmtsilva69 over 8 years
    And you don't need this (it's already set in the xml layout): imgDisplay.setImageResource(R.mipmap.myinfolinks_splash);
  • driftking9987
    driftking9987 over 8 years
    Can anyone tell me how to initialize it? In mainactivity. I removed android:src and android:scaleType from the xml file. How to add the image which is in "drawable" folder to it.
  • jmtsilva69
    jmtsilva69 over 8 years
    Skizo- this: ImageView img = (ImageView) findViewById(R.id.action_infolinks_splash)
  • jmtsilva69
    jmtsilva69 over 8 years
    Skizo- this: ImageView img = (ImageView) findViewById(R.id.action_infolinks_splash) it's for to get the width&height image programmatically. Yesterday i improve the code to rescale and i use only xxhdpi jpg image (1026px x 770px), like that i make smaller app and the image adjust for all screen size sucessfuly.
  • jmtsilva69
    jmtsilva69 over 8 years
    In xml: <***.****.TouchImageView android:id="@+id/action_infolinks_about_support" android:layout_width="match_parent" android:layout_height="wrap_content" android:src="@mipmap/myinfolinks_about_support" android:scaleType="centerInside" android:contentDescription="@string/aboutSupport_description‌​_image"/>
  • Skizo-ozᴉʞS
    Skizo-ozᴉʞS over 8 years
    But does it zoom in zoom out repeatly? I mean with 1 click it zooms in zooms out repeatly
  • Skizo-ozᴉʞS
    Skizo-ozᴉʞS over 8 years
    I want when click an ImageView it zooms in and zooms out infinity with your answer can I do it?
  • alexbirkett
    alexbirkett about 8 years
    No need to use setOnTouchListener() you can just override public boolean onTouchEvent(MotionEvent event)
  • alexbirkett
    alexbirkett about 8 years
    There are some issues with this: * The initial zoom is wrong (Possible solution use scale = Math.min(scaleX, scaleY); in adjustScale(). * The zooming does not follow the mid point between fingers
  • roroinpho21
    roroinpho21 about 8 years
    Great job. I modified a bit your code. I disabled the ability to move the image on X or Y if the width or height is less then display's bounds
  • Umair
    Umair almost 8 years
    @ChiragRaval can we use it on the layout ? which contains multiples images ... I am trying to zoom and swipe left, right, up, down but couldn't find a way to accomplish it.
  • Yohanim
    Yohanim over 7 years
    Follow our community guidelines . Do not provide external links because links in the future may not be accessible. Furthermore, give some snippet code.
  • Dr.jacky
    Dr.jacky over 7 years
    @ChiragRaval How to use this class for a imageView inserted in XML?
  • Shubham A.
    Shubham A. over 7 years
    We can actually zoom out the original image to make it smaller. How to prevent this? Image should not go less than 100% of its original size.
  • roroinpho21
    roroinpho21 over 7 years
    @ROHITPARMAR I will post a new comment with my class.
  • user2630165
    user2630165 over 7 years
    Working great. How can I get the real image x,y coordinates when a click was performed?
  • Admin
    Admin over 7 years
    Perfect class! One question, how can I log the X/Y-position on action up relative to the ImageView's X/Y-position. Now it registers all the white space around it... I want to know the coordinates inside my ImageView.
  • Alejandro Cumpa
    Alejandro Cumpa almost 7 years
    I have been using this for a while, but now I wanted to use it with Glide and it won't show the image, then I updated the reference to android.support.v7.widget.AppCompatImageView and get it works.
  • Alejandro Cumpa
    Alejandro Cumpa almost 7 years
    If this doesn't work try extending android.support.v7.widget.AppCompatImageView.
  • Daniel Viglione
    Daniel Viglione almost 7 years
    This is exactly what I want but for a bitmap in canvas for scaling
  • temirbek
    temirbek almost 7 years
    how to make it zoom in with double tap when in original size?
  • Vishwajit Palankar
    Vishwajit Palankar over 6 years
    is there a way to restore the scaling of image when the ImageView is double tapped?
  • Leonid Ustenko
    Leonid Ustenko over 6 years
    Kinda ugly to do all this stuff within an Activity
  • Muhammad Noman
    Muhammad Noman over 6 years
    Hi, Can you tell me how to use that in Main Class? I came up with this, Can you tell me what should be the next? ZoomableImageView touch =(ZoomableImageView)findViewById(R.id.image1);
  • CoolMind
    CoolMind over 6 years
    Strange, but after replacing ImageView in XML with iImage, I got "android.view.InflateException: Binary XML file line #0: Error inflating class ...iImage".
  • CoolMind
    CoolMind over 6 years
    Thanks! Works nice. If you use this widget inside ViewPager, some difficulties appear. 1) A scale is not reset to 1 after moving forward and backward. 2) If you try to shift a picture, the movement is slow and short. And you can occasionally move to next picture. It's not a problem of the solution, but you should realize a new behaviour.
  • CoolMind
    CoolMind over 6 years
    Doesn't work when image is inside ViewPager. Shows empty screen. While similar solutions and derived projects work
  • Zia Ur Rahman
    Zia Ur Rahman about 6 years
    Good, but this is only for imageview. How to attach a Class extended by View to this attacher. Have you any idea,
  • Däñish Shärmà
    Däñish Shärmà about 6 years
    your example have incomplete code, so many errors and ImageViewTouch class missing.
  • Beeing Jk
    Beeing Jk almost 6 years
    not so smooth when put it in a ScrollView with multiple images
  • Wesos de Queso
    Wesos de Queso almost 6 years
    Im actually amazed by this. Works great inside my fragment.
  • Mehdi Dehghani
    Mehdi Dehghani almost 6 years
    How to avoid to much zoom out?
  • Mehdi Dehghani
    Mehdi Dehghani almost 6 years
    You added getWidth() and getHeight() in onScale method, where is the implementation of them?
  • MohammedAli
    MohammedAli over 5 years
    how can i zoom outside of viewpager means a whole screen?
  • Feuby
    Feuby over 5 years
    This works very well. I trid other answers and they did not work, or did not work properly.
  • Lensflare
    Lensflare over 5 years
    I am using your class in a kotlin project. All I had to do is to replace the ImageView with your ZoomableImageView in the layout xml and it did work out of the box! Thanks.
  • Sanal S
    Sanal S over 5 years
    I can't seem to apply this on an ImageView added to WindowManager in a class extending Service class.
  • Ed J
    Ed J almost 5 years
    This is an outdated version. See the repo for instructions for latest PhotoView: github.com/chrisbanes/PhotoView
  • appukrb
    appukrb over 4 years
    @Chirag could you update the code, when click on first tap moved to the top of the screen
  • arniotaki
    arniotaki over 4 years
    Although the PhotoView according to above links instructions it does not work. Your answer (PhotoViewAttacher) seems to work
  • Asad Ali
    Asad Ali over 4 years
    This code is working fine, its purpose is to ZoomIn/ZoomOut a view not the image in it.
  • SuppressWarnings
    SuppressWarnings about 4 years
    I start it with setImageURI(imageUri) and every image appears black... The view does get the correct size, height and different attributes but doesnt display anything no matter the image.
  • Zain
    Zain about 4 years
    @SuppressWarnings thanks for notifying that, I am not sure if I tried to set it programmatically; need to look back.. and will mention you for any workaround :)
  • mr.volatile
    mr.volatile almost 3 years
    Using this code image is zoom in/out infinitely, how to restrict it?
  • Asesha George
    Asesha George over 2 years
    excellent solution, nice approach. Thank You
  • mikeB
    mikeB about 2 years
    This is a great sample. Thanks to Chirag and @Nikunj Paradva. I have two things that I want to add in my code, One, limit the min and max zoom size, and Two, have the click (touch) event to be handled (like navigate back). I will work at this but if anyone has a suggestion...