Search code examples
androidandroid-listviewandroid-imageviewpinchzoom

Android Pinch and Zoom Image in Activity


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>

Solution

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