Android Pinch and Zoom Image in Activity

20,090

Try this code:

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;

/**
 * Extends Android ImageView to include pinch zooming and panning.
 */
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;

    float width, height;
    static final int CLICK = 3;
    float saveScale = 1f;
    float right, bottom, origWidth, origHeight, bmWidth, bmHeight;

    ScaleGestureDetector mScaleDetector;

    Context context;

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

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

    private void sharedConstructing(Context 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)
            {
                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
            }
        });
    }

    @Override
    public void setImageBitmap(Bitmap bm)
    {
        super.setImageBitmap(bm);
        if (bm != null)
        {
            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 = 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;
        right = width * saveScale - width - (2 * redundantXSpace * saveScale);
        bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
        setImageMatrix(matrix);
    }
}
Share:
20,090
suresh cheemalamudi
Author by

suresh cheemalamudi

Android Developer

Updated on August 05, 2022

Comments

  • suresh cheemalamudi
    suresh cheemalamudi almost 2 years

    I have a ListView with set of images. On clicking any of them, the clicked image will appear on a new activity and at the center of the activity in ImageView, also it is Zoomable. The Problem is when i zoom the image it gains Zoom but also moves like drag and drop. I should eliminate that movement of the image and should only be zoomable from the center of the activity.

    ZoomInZoomOutActivity.java

    public class ZoomInZoomOutActivity extends Activity implements OnTouchListener 
    {
        private static final String TAG = "Touch";
        @SuppressWarnings("unused")
        private static final float MIN_ZOOM = 1f,MAX_ZOOM = 1f;
    
        Matrix matrix = new Matrix();
        Matrix savedMatrix = new Matrix();
        static final int NONE = 0;
        static final int DRAG = 1;
        static final int ZOOM = 2;
        int mode = NONE;
        PointF start = new PointF();
        PointF mid = new PointF();
        float oldDist = 1f;
        @Override
        public void onCreate(Bundle savedInstanceState) 
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.imagedisp);
    
            Bundle extras = getIntent().getExtras();
    
            long id =extras.getLong("ID");
            String s=String.valueOf(id);
            ImageView view;
            view = (ImageView) findViewById(R.id.imageView1);
            view.setOnTouchListener(this);
    
            if (s.equals("0")) 
            {
                view.setImageResource(R.drawable.img1);
            }
            else if (s.equals("1"))
            {
                view.setImageResource(R.drawable.img2);
            }
            else if (s.equals("2"))
            {
                view.setImageResource(R.drawable.img3);
            } 
            else 
            {
                view.setImageResource(R.drawable.img4);
            }
    
        }
    
    
        public boolean onTouch(View v, MotionEvent event) 
        {
            ImageView view = (ImageView) v;
            view.setScaleType(ImageView.ScaleType.MATRIX);
            float scale;
    
            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_UP:
    
                case MotionEvent.ACTION_POINTER_UP:
    
                                                    mode = NONE;
                                                    Log.d(TAG, "mode=NONE");
                                                    break;
    
                case MotionEvent.ACTION_POINTER_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); 
                                                    } 
                                                    else if (mode == ZOOM) 
                                                    { 
    
                                                        float newDist = spacing(event);
                                                        Log.d(TAG, "newDist=" + newDist);
                                                        if (newDist > 5f) 
                                                        {
                                                            matrix.set(savedMatrix);
                                                            scale = newDist / oldDist; 
                                                            matrix.postScale(scale, scale, mid.x, mid.y);
                                                        }
                                                    }
                                                    break;
            }
    
            view.setImageMatrix(matrix); 
            return true; 
        }
    
    
    
        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);
        }
    
    }
    

    imagedisp.xml

    <?xml version="1.0" encoding="utf-8"?>
    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" 
        android:gravity="center">
    
        <ImageView android:layout_width="fill_parent" 
            android:id="@+id/imageView1"
            android:src="@drawable/img1" 
            android:layout_height="fill_parent"
            android:scaleType="centerInside">
    
        </ImageView>
    
    </LinearLayout>