Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we use scale gesture detector for pinch zoom in Android?

People also ask

How do you pinch zoom on Android?

Tap anywhere on the screen, except the keyboard or navigation bar. Drag 2 fingers to move around the screen. Pinch with 2 fingers to adjust zoom. To stop magnification, use your magnification shortcut again.

What is gesture detector in Android Studio?

android.view.GestureDetector. Detects various gestures and events using the supplied MotionEvent s. The OnGestureListener callback will notify users when a particular motion event has occurred. This class should only be used with MotionEvent s reported via touch (don't use for trackball events).

What is ScaleGestureDetector?

↳ android.view.ScaleGestureDetector. Detects scaling transformation gestures using the supplied MotionEvent s. The OnScaleGestureListener callback will notify users when a particular gesture event has occurred. This class should only be used with MotionEvent s reported via touch.


You can use this

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

public class MyImageView extends View {

private static final int INVALID_POINTER_ID = -1;

    private Drawable mImage;
    private float mPosX;
    private float mPosY;

    private float mLastTouchX;
    private float mLastTouchY;
    private int mActivePointerId = INVALID_POINTER_ID;

    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;

    public MyImageView(Context context) {
        this(context, null, 0);
    mImage=act.getResources().getDrawable(context.getResources().getIdentifier("imag­ename", "drawable", "packagename"));

        mImage.setBounds(0, 0, mImage.getIntrinsicWidth(), mImage.getIntrinsicHeight());
    }

    public MyImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(ev);

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {
            final float x = ev.getX();
            final float y = ev.getY();

            mLastTouchX = x;
            mLastTouchY = y;
            mActivePointerId = ev.getPointerId(0);
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            final int pointerIndex = ev.findPointerIndex(mActivePointerId);
            final float x = ev.getX(pointerIndex);
            final float y = ev.getY(pointerIndex);

            // Only move if the ScaleGestureDetector isn't processing a gesture.
            if (!mScaleDetector.isInProgress()) {
                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;

                mPosX += dx;
                mPosY += dy;

                invalidate();
            }

            mLastTouchX = x;
            mLastTouchY = y;

            break;
        }

        case MotionEvent.ACTION_UP: {
            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

        case MotionEvent.ACTION_POINTER_UP: {
            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) 
                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = ev.getPointerId(pointerIndex);
            if (pointerId == mActivePointerId) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mLastTouchX = ev.getX(newPointerIndex);
                mLastTouchY = ev.getY(newPointerIndex);
                mActivePointerId = ev.getPointerId(newPointerIndex);
            }
            break;
        }
        }

        return true;
    }

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

        canvas.save();
        Log.d("DEBUG", "X: "+mPosX+" Y: "+mPosY);
        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor);
        mImage.draw(canvas);
        canvas.restore();
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();

            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));

            invalidate();
            return true;
        }
    }

}

to call this in your activity.setContentView(new MyImageView(this));


You can create a reusable class that implements OnTouchListener to accomplish this.

public class MyScaleGestures implements OnTouchListener, OnScaleGestureListener {       
    private View view;
    private ScaleGestureDetector gestureScale;
    private float scaleFactor = 1;  
    private boolean inScale = false;

    public MyScaleGestures (Context c){ gestureScale = new ScaleGestureDetector(c, this); }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        this.view = view; 
        gestureScale.onTouchEvent(event);
        return true;
    }   

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scaleFactor *= detector.getScaleFactor();
        scaleFactor = (scaleFactor < 1 ? 1 : scaleFactor); // prevent our view from becoming too small //
        scaleFactor = ((float)((int)(scaleFactor * 100))) / 100; // Change precision to help with jitter when user just rests their fingers //
        view.setScaleX(scaleFactor);
        view.setScaleY(scaleFactor);
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        inScale = true;
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) { inScale = false; }
}

Then assign it as your View's OnTouchListener like so.

myView.setOnTouchListener(new MyScaleGestures(context));

If you want to add a scrolling ability to the View you will need to implement onScroll from the OnGestureListener interface. You can add this override to the MyScaleGestures class to accomplish this.

@Override
public boolean onScroll(MotionEvent event1, MotionEvent event2, float x, float y) {
    float newX = view.getX();
    float newY = view.getY();
    if(!inScale){
        newX -= x;
        newY -= y;
    }
    WindowManager wm = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
    Display d = wm.getDefaultDisplay();
    Point p = new Point();
    d.getSize(p);

    if (newX > (view.getWidth() * scaleFactor - p.x) / 2){
        newX = (view.getWidth() * scaleFactor - p.x) / 2;
    } else if (newX < -((view.getWidth() * scaleFactor - p.x) / 2)){
        newX = -((view.getWidth() * scaleFactor - p.x) / 2);
    }

    if (newY > (view.getHeight() * scaleFactor - p.y) / 2){
        newY = (view.getHeight() * scaleFactor - p.y) / 2;
    } else if (newY < -((view.getHeight() * scaleFactor - p.y) / 2)){
        newY = -((view.getHeight() * scaleFactor - p.y) / 2);
    }

    view.setX(newX);
    view.setY(newY);

    return true;
}

The end result of doing all of the above will give you class like this one:

public class StandardGestures implements OnTouchListener, OnGestureListener, OnDoubleTapListener, OnScaleGestureListener {
    private View view;
    private GestureDetector gesture;
    private ScaleGestureDetector gestureScale;
    private float scaleFactor = 1;
    private boolean inScale;

    public StandardGestures(Context c){
        gesture = new GestureDetector(c, this);
        gestureScale = new ScaleGestureDetector(c, this);
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        this.view = view;
        gesture.onTouchEvent(event);
        gestureScale.onTouchEvent(event);
        return true;
    }

    @Override
    public boolean onDown(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2, float x, float y) {
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
    }

    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2, float x, float y) {
        float newX = view.getX();
        float newY = view.getY();
        if(!inScale){
            newX -= x;
            newY -= y;
        }
        WindowManager wm = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
        Display d = wm.getDefaultDisplay();
        Point p = new Point();
        d.getSize(p);

        if (newX > (view.getWidth() * scaleFactor - p.x) / 2){
            newX = (view.getWidth() * scaleFactor - p.x) / 2;
        } else if (newX < -((view.getWidth() * scaleFactor - p.x) / 2)){
            newX = -((view.getWidth() * scaleFactor - p.x) / 2);
        }

        if (newY > (view.getHeight() * scaleFactor - p.y) / 2){
            newY = (view.getHeight() * scaleFactor - p.y) / 2;
        } else if (newY < -((view.getHeight() * scaleFactor - p.y) / 2)){
            newY = -((view.getHeight() * scaleFactor - p.y) / 2);
        }

        view.setX(newX);
        view.setY(newY);

        return true;
    }

    @Override
    public void onShowPress(MotionEvent event) {
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        view.setVisibility(View.GONE);
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {

        scaleFactor *= detector.getScaleFactor();
        scaleFactor = scaleFactor < 1 ? 1 : scaleFactor; // prevent our image from becoming too small
        scaleFactor = (float) (int) (scaleFactor * 100) / 100; // Change precision to help with jitter when user just rests their fingers //
        view.setScaleX(scaleFactor);
        view.setScaleY(scaleFactor);
        onScroll(null, null, 0, 0); // call scroll to make sure our bounds are still ok //
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        inScale = true;
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        inScale = false;
        onScroll(null, null, 0, 0); // call scroll to make sure our bounds are still ok //
    }
}

ScaleGestureDetector is available starting in Android 2.2 (aka Froyo, API level 8). See: http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html

In 2.0/2.1, you don't have ScaleGestureDetector, but you can provide pinch-to-zoom using the ZDNet blog entry by Ed Burnette that Pieter888 linked to above: http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-6-implementing-the-pinch-zoom-gesture/1847


actually there is a library that uses this class just for the zooming of images.

it's called "TouchImageView"