4

How to achieve this feature, PullToZoom only with an ImageView (Without the ListView in the sample)?

Similar question, but no answer :(

Community
  • 1
  • 1
Atul O Holic
  • 6,692
  • 4
  • 39
  • 74

2 Answers2

3

What about using the Gesture Detector? You can find and implementation of what (I think) you need in the following code:

public class MainActivity extends AppCompatActivity {

    private static final String DEBUG_TAG = "Gestures";

    private GestureDetectorCompat mDetector;
    private ImageView imageView;
    private MyGestureListener myGestureListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (ImageView) findViewById(R.id.image);

        myGestureListener = new MyGestureListener();
        mDetector = new GestureDetectorCompat(this, myGestureListener);
        imageView.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                int action = MotionEventCompat.getActionMasked(event);
                if (action == MotionEvent.ACTION_UP) {
                    myGestureListener.upDetected();
                }
                return mDetector.onTouchEvent(event);
            }
        });

        imageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.test));

    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

        private static final float MAX_ZOOM = 0.5f;

        private static final float PCT = 300f;
        private float delta;

        private ValueAnimator valueAnimator;

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

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (valueAnimator != null) {
                valueAnimator.cancel();
            }

            delta += distanceY;
            float pct = getPct(delta);
            imageView.setScaleX(1.0f + pct);
            imageView.setScaleY(1.0f + pct);
            return false;
        }

        void upDetected() {

            float pct = getPct(delta);

            valueAnimator = new ValueAnimator();
            valueAnimator.setFloatValues(pct, 0.0f);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    imageView.setScaleX(1.0f + (float) animation.getAnimatedValue());
                    imageView.setScaleY(1.0f + (float) animation.getAnimatedValue());
                }
            });
            valueAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {

                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    delta = 0f;
                    imageView.setScaleX(1.0f);
                    imageView.setScaleY(1.0f);
                }

                @Override
                public void onAnimationCancel(Animator animation) {

                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            });
            valueAnimator.start();
        }

        private float getPct(float delta) {
            float pct = delta / PCT;
            if (pct >= MAX_ZOOM) {
                pct = MAX_ZOOM;
            }
            else if (pct <= -MAX_ZOOM) {
                pct = -MAX_ZOOM;
            }
            return pct;
        }
    }
}

The MainActivity has a simple ImageView. When you 'scroll' inside it, the gesture is detected and the image is scaled (up or down). When you remove the finger from the screen, the image is scaled back to its original size with a simple animation. If you want to avoid the zoom out, you just need to work on getPct() method.

EDIT

For example the getPtc could be something like this (if you are interested just to the zoom in)

private float getPct(float delta) {
    float pct = -delta / PCT;
    if (pct >= MAX_ZOOM) {
        pct = MAX_ZOOM;
    }
    else if (pct <= 0) {
        pct = 0;
    }
    return pct;
}

EDIT #2

It seems that the very first scroll event of the gesture detector is wide. I've added some code to ignore it

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

    private static final float MAX_ZOOM = 0.8f;

    private static final float PCT = 300f;
    private float delta;

    private ValueAnimator valueAnimator;
    private boolean mFirstEvent = true;

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

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        if (valueAnimator != null) {
            valueAnimator.cancel();
        }

        if (mFirstEvent) {
            mFirstEvent = false;
            return false;
        }

        delta += distanceY;
        float pct = getPct(delta);
        imageView.setScaleX(1.0f + pct);
        imageView.setScaleY(1.0f + pct);
        textView.setScaleY(1.0f - pct);
        return false;
    }

    void upDetected() {

        mFirstEvent = true;
        float pct = getPct(delta);

        valueAnimator = new ValueAnimator();
        valueAnimator.setFloatValues(pct, 0.0f);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                imageView.setScaleX(1.0f + (Float) animation.getAnimatedValue());
                imageView.setScaleY(1.0f + (Float) animation.getAnimatedValue());
                textView.setScaleY(1.0f - (Float) animation.getAnimatedValue());
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                delta = 0f;
                imageView.setScaleX(1.0f);
                imageView.setScaleY(1.0f);
                textView.setScaleY(1.0f);
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.start();
    }

    private float getPct(float delta) {
        float pct = -delta / PCT;
        if (pct >= MAX_ZOOM) {
            pct = MAX_ZOOM;
        }
        else if (pct <= 0) {
            pct = 0;
        }
        return pct;
    }

    /*private float getPct(float delta) {
        float pct = delta / PCT;
        if (pct >= MAX_ZOOM) {
            pct = MAX_ZOOM;
        }
        else if (pct <= -MAX_ZOOM) {
            pct = -MAX_ZOOM;
        }
        return pct;
    }*/
}
Mimmo Grottoli
  • 5,758
  • 2
  • 17
  • 27
  • It isn't zooming on pull actually. It detects ACTION_UP gesture and automatically zooms to a specified value. What I am looking for is it should zoom according to the amount of scroll or pull user does – Atul O Holic Aug 07 '15 at 08:19
  • The zoom out thing is infact working smoother, how do we change that for the zoom in? – Atul O Holic Aug 07 '15 at 08:59
  • What do you want to change? I don't understand. – Mimmo Grottoli Aug 07 '15 at 09:05
  • If you scroll up or down into the imageview, you get the zoom in or out. Isn't it like this? – Mimmo Grottoli Aug 07 '15 at 09:07
  • Nope when you pull down, or scroll down (only down action), Image should zoom and when you leave it shapes back to normal. This is one thing out of two which I am trying. – Atul O Holic Aug 07 '15 at 09:13
  • Take a look at the edited answer - as I told you, you just need to play a little bit with getPct – Mimmo Grottoli Aug 07 '15 at 09:17
  • https://drive.google.com/file/d/0ByhJIK8n7rQ4WFk0clN3M21PbTQ/view?usp=sharing - can u see this the image shakes – Atul O Holic Aug 07 '15 at 12:09
  • I guess I've fixed. Come on @AtulOHolic, write some log, use a debugger to see what happens otherwise you'll never really learn Android! – Mimmo Grottoli Aug 07 '15 at 12:28
  • It isn't the firstEvent actually, the issue is that when we drag from somewhere center it works fine but when we do that from the edge issue occurs and thanks for all the help. I will fix the issue and let you know. – Atul O Holic Aug 07 '15 at 12:49
  • Agreed - the image is shaking. Did you managed to resolve this? – Simon Jan 21 '16 at 21:34
0

The Tinder app on android accomplishes a similar feature by overriding the onTouchEvent() method. Check out this post to see more.

Community
  • 1
  • 1
frgnvola
  • 518
  • 3
  • 16
  • Yup that was the first thing I saw. But it is implemented for a listview header and has a fixed height associated with it. I am trying to get that only with an ImageView without having to fix the height. – Atul O Holic Aug 03 '15 at 18:56
  • Exactly, but overriding the `onTouchEvent()` method for View would possibly fix it. – frgnvola Aug 03 '15 at 19:05
  • Yes but it is also making use on ScrollListener of Listview as I can see from the code – Atul O Holic Aug 03 '15 at 19:10
  • https://github.com/Gnod/ParallaxListView/blob/master/src/com/gnod/parallaxlistview/ParallaxScollListView.java - this is the class use in the example you mentioned, here – Atul O Holic Aug 03 '15 at 19:28
  • I am actually talking about the second answer, where `onTouchEvent()` is overrided – frgnvola Aug 03 '15 at 19:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/85029/discussion-between-atul-o-holic-and-frgnvola). – Atul O Holic Aug 03 '15 at 19:37