Search code examples
androidandroid-activitylistenergesturezooming

Image zoom activity for android


I'm trying to implement the double tap and the pinch to zoom activity. I wrote some code for the first one but it seem not to work (i can use only the android emulator for eclipse for now. So I'm not sure if my result is the right one)

Anyway I'm not able to understand what am I doing wrong. I'm surely forgetting something.

This is my code:

public class ImageZoomView extends View implements OnGestureListener {

private static final int SCALING_FACTOR = 50;
private final int LANDSCAPE = 1;
private GestureDetector gestureDetector;
private Drawable image = null;
private int scalefactor = 0;
private int orientation;
private int zoomCtr = 0;
private long lastTouchTime = 0;
private int winX, winY, imageX, imageY, scrollX = 0, scrollY = 0, left,
top, bottom, right;

public ImageZoomView(Context context, int orientation) {
            super(context);
            setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
                    LayoutParams.FILL_PARENT));
            this.orientation = orientation;
            gestureDetector = new GestureDetector(this);
        }

    public void setImage(Drawable bitmap, Activity activity) {
        image = bitmap;
        imageSetting(activity);
    }

    public void setImage(Bitmap bitmap, Activity activity) {
        image = new BitmapDrawable(bitmap).getCurrent();
        imageSetting(activity);
    }

    /**
     * Works in both landscape and portrait mode.
     */
    private void imageSetting(Activity activity) {
        scrollX = scrollY = 0;
        scalefactor = 0;
        imageX = winX = activity.getWindow().getWindowManager()
                .getDefaultDisplay().getWidth();
        imageY = winY = activity.getWindow().getWindowManager()
                .getDefaultDisplay().getHeight();
        if (orientation == LANDSCAPE) {
            imageX = 3 * imageY / 4;
        }
        calculatePos();
    }

    public void calculatePos() {
        int tempx, tempy;
        tempx = imageX + imageX * scalefactor / 100;
        tempy = imageY + imageY * scalefactor / 100;
        left = (winX - tempx) / 2;
        top = (winY - tempy) / 2;
        right = (winX + tempx) / 2;
        bottom = (winY + tempy) / 2;
        invalidate();
    }

    /**
     * Redraws the bitmap when zoomed or scrolled.
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (image == null)
            return;

        image.setBounds(left + scrollX, top + scrollY, right + scrollX, bottom
                + scrollY);
        image.draw(canvas);
    }

    public void zoomIn() {
        scalefactor += SCALING_FACTOR;
        calculatePos();
    }

    public void zoomOut() {
        if (scalefactor == 0)
            return;
        scrollX = scrollY = 0;
        scalefactor -= SCALING_FACTOR;
        calculatePos();
    }

    public void scroll(int x, int y) {
        scrollX += x / 5;
        scrollY += y / 5;
        if (scrollX + left > 0) {
            scrollX = 0 - left;
        } else if (scrollX + right < winX) {
            scrollX = winX - right;
        }
        if (scrollY + top > 0) {
            scrollY = 0 - top;
        } else if (scrollY + bottom < winY) {
            scrollY = winY - bottom;
        }
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent me) {
        boolean onTouchEvent = gestureDetector.onTouchEvent(me);
        return onTouchEvent;
    }

    @Override
    public boolean onDown(MotionEvent arg0) {
        long thisTime = arg0.getEventTime();
        if (thisTime - lastTouchTime < 250) {
            lastTouchTime = -1;
            onDoubleTap();
            return true;
        }
        lastTouchTime = thisTime;
        return true;
    }

    @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                float distanceY) {
            if (zoomCtr == 0)
                return false;
            scroll((int) (e2.getX() - e1.getX()), (int) (e2.getY() - e1.getY()));
            return true;
        }

        private void onDoubleTap() {
            if (zoomCtr == 0) {
                zoomCtr++;
                zoomIn();
                return;
            }
            zoomCtr--;
            zoomOut();
    }
}

And this is where I call the activity:

public class FullScreenPhoto extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_full_screen_photo);
    Intent intent = getIntent();
    String path = intent.getStringExtra("Path");
    ImageView img = (ImageView)findViewById(R.id.photo);
    img.setImageURI(Uri.parse(path));

    ImageZoomView imageView = new ImageZoomView(
            this,getWindow().getWindowManager().getDefaultDisplay().getOrientation());
    this.setContentView(imageView);
    }
}

It display the full image on the screen but when tapping on it, nothing happens.


Solution

  • Great create a class called ZoomFunctionality

    ZoomFunctionality

    package com.androidhive.work;
    
    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 ZoomFunctionality 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 ZoomFunctionality(Context context) {
        super(context);
        sharedConstructing(context);
    }
    
    public ZoomFunctionality(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() {
    
            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 = 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 = (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);
    }
    }
    

    Now when ever you want to call the ZoomFunctionality from your Activity just write the following statement

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 2;
    Bitmap bmp = BitmapFactory.decodeFile(filename, options);
    
    
    ZoomFunctionality img = new ZoomFunctionality(this);
    img.setImageBitmap(bmp);
    img.setMaxZoom(4f);
    setContentView(img);
    

    Note
    bmp: Is the bitmap image of which you want the zoom functionality
    this: Its recommended that you use Activity.this
    Its not necessary to use BitmapFactoryOptions but decoding from filename is required