com.ttxgps.zoom.GestureImageViewTouchListener.java Source code

Java tutorial

Introduction

Here is the source code for com.ttxgps.zoom.GestureImageViewTouchListener.java

Source

/*
 * Copyright (c) 2012 Jason Polites
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ttxgps.zoom;

import android.content.res.Configuration;
import android.graphics.PointF;
import android.support.v4.view.ViewPager;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;

public class GestureImageViewTouchListener implements OnTouchListener {

    private GestureImageView image;
    private OnClickListener onClickListener;

    private final PointF current = new PointF();
    private final PointF last = new PointF();
    private final PointF next = new PointF();
    private final PointF midpoint = new PointF();

    private final VectorF scaleVector = new VectorF();
    private final VectorF pinchVector = new VectorF();

    private boolean touched = false;
    private boolean inZoom = false;

    private float initialDistance;
    private float lastScale = 1.0f;
    private float currentScale = 1.0f;

    private float boundaryLeft = 0;
    private float boundaryTop = 0;
    private float boundaryRight = 0;
    private float boundaryBottom = 0;

    private float maxScale = 5.0f;
    private float minScale = 0.25f;
    private float fitScaleHorizontal = 1.0f;
    private float fitScaleVertical = 1.0f;

    private int canvasWidth = 0;
    private int canvasHeight = 0;

    private float centerX = 0;
    private float centerY = 0;

    private float startingScale = 0;

    private boolean canDragX = false;
    private boolean canDragY = false;

    private boolean multiTouch = false;

    private int displayWidth;
    private int displayHeight;

    private int imageWidth;
    private int imageHeight;

    private FlingListener flingListener;
    private FlingAnimation flingAnimation;
    private ZoomAnimation zoomAnimation;
    private MoveAnimation moveAnimation;
    private GestureDetector tapDetector;
    private GestureDetector flingDetector;
    private GestureImageViewListener imageListener;

    private ViewPager mPager;

    public GestureImageViewTouchListener(final GestureImageView image, int displayWidth, int displayHeight,
            ViewPager pager) {
        super();
        this.image = image;
        mPager = pager;
        this.displayWidth = displayWidth;
        this.displayHeight = displayHeight;

        this.centerX = (float) displayWidth / 2.0f;
        this.centerY = (float) displayHeight / 2.0f;

        this.imageWidth = image.getImageWidth();
        this.imageHeight = image.getImageHeight();

        startingScale = image.getScale();

        currentScale = startingScale;
        lastScale = startingScale;

        boundaryRight = displayWidth;
        boundaryBottom = displayHeight;
        boundaryLeft = 0;
        boundaryTop = 0;

        next.x = image.getImageX();
        next.y = image.getImageY();

        flingListener = new FlingListener();
        flingAnimation = new FlingAnimation();
        zoomAnimation = new ZoomAnimation();
        moveAnimation = new MoveAnimation();

        flingAnimation.setListener(new FlingAnimationListener() {
            @Override
            public void onMove(float x, float y) {
                handleDrag(current.x + x, current.y + y);
            }

            @Override
            public void onComplete() {
            }
        });

        zoomAnimation.setZoom(2.0f);
        zoomAnimation.setZoomAnimationListener(new ZoomAnimationListener() {
            @Override
            public void onZoom(float scale, float x, float y) {
                if (scale <= maxScale && scale >= minScale) {
                    handleScale(scale, x, y);
                }
            }

            @Override
            public void onComplete() {
                inZoom = false;
                handleUp();
            }
        });

        moveAnimation.setMoveAnimationListener(new MoveAnimationListener() {

            @Override
            public void onMove(float x, float y) {
                image.setPosition(x, y);
                image.redraw();
            }
        });

        tapDetector = new GestureDetector(image.getContext(), new SimpleOnGestureListener() {
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                // if (mPager != null
                // && mPager.getOnViewListener() != null) {
                // mPager.getOnViewListener().onDoubleTap();
                // if (image.zoom) {
                // startZoom(e);
                // }
                // }
                return true;
            }

            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                // if (mPager != null
                // && mPager.getOnViewListener() != null) {
                // mPager.getOnViewListener().onSingleTapConfirmed();
                // }
                if (!inZoom) {
                    if (onClickListener != null) {
                        onClickListener.onClick(image);
                        return true;
                    }
                }

                return false;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                // if (mPager != null
                // && mPager.getOnViewListener() != null) {
                // mPager.getOnViewListener().onLongPress();
                // }
                super.onLongPress(e);
            }

        });

        flingDetector = new GestureDetector(image.getContext(), flingListener);
        imageListener = image.getGestureImageViewListener();

        calculateBoundaries();
    }

    private void startFling() {
        flingAnimation.setVelocityX(flingListener.getVelocityX());
        flingAnimation.setVelocityY(flingListener.getVelocityY());
        image.animationStart(flingAnimation);
    }

    private void startZoom(MotionEvent e) {
        inZoom = true;
        zoomAnimation.reset();

        float zoomTo = 1.0f;

        if (image.isLandscape()) {
            if (image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
                int scaledHeight = image.getScaledHeight();

                if (scaledHeight < canvasHeight) {
                    zoomTo = fitScaleVertical / currentScale;
                    zoomAnimation.setTouchX(e.getX());
                    zoomAnimation.setTouchY(image.getCenterY());
                } else {
                    zoomTo = fitScaleHorizontal / currentScale;
                    zoomAnimation.setTouchX(image.getCenterX());
                    zoomAnimation.setTouchY(image.getCenterY());
                }
            } else {
                int scaledWidth = image.getScaledWidth();

                if (scaledWidth == canvasWidth) {
                    zoomTo = currentScale * 4.0f;
                    zoomAnimation.setTouchX(e.getX());
                    zoomAnimation.setTouchY(e.getY());
                } else if (scaledWidth < canvasWidth) {
                    zoomTo = fitScaleHorizontal / currentScale;
                    zoomAnimation.setTouchX(image.getCenterX());
                    zoomAnimation.setTouchY(e.getY());
                } else {
                    zoomTo = fitScaleHorizontal / currentScale;
                    zoomAnimation.setTouchX(image.getCenterX());
                    zoomAnimation.setTouchY(image.getCenterY());
                }
            }
        } else {
            if (image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {

                int scaledHeight = image.getScaledHeight();

                if (scaledHeight == canvasHeight) {
                    zoomTo = currentScale * 4.0f;
                    zoomAnimation.setTouchX(e.getX());
                    zoomAnimation.setTouchY(e.getY());
                } else if (scaledHeight < canvasHeight) {
                    zoomTo = fitScaleVertical / currentScale;
                    zoomAnimation.setTouchX(e.getX());
                    zoomAnimation.setTouchY(image.getCenterY());
                } else {
                    zoomTo = fitScaleVertical / currentScale;
                    zoomAnimation.setTouchX(image.getCenterX());
                    zoomAnimation.setTouchY(image.getCenterY());
                }
            } else {
                int scaledWidth = image.getScaledWidth();

                if (scaledWidth < canvasWidth) {
                    zoomTo = fitScaleHorizontal / currentScale;
                    zoomAnimation.setTouchX(image.getCenterX());
                    zoomAnimation.setTouchY(e.getY());
                } else {
                    zoomTo = fitScaleVertical / currentScale;
                    zoomAnimation.setTouchX(image.getCenterX());
                    zoomAnimation.setTouchY(image.getCenterY());
                }
            }
        }

        zoomAnimation.setZoom(zoomTo);
        image.animationStart(zoomAnimation);
    }

    private void stopAnimations() {
        image.animationStop();
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        if (!inZoom) {

            if (!tapDetector.onTouchEvent(event)) {
                if (event.getPointerCount() == 1 && flingDetector.onTouchEvent(event)) {
                    startFling();
                }

                if (event.getAction() == MotionEvent.ACTION_UP) {
                    handleUp();
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    stopAnimations();

                    last.x = event.getX();
                    last.y = event.getY();

                    if (imageListener != null) {
                        imageListener.onTouch(last.x, last.y);
                    }

                    touched = true;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    if (event.getPointerCount() > 1) {
                        // if (mPager != null) {
                        // mPager.isLeft(false);
                        // mPager.isRight(false);
                        // }

                        multiTouch = true;
                        if (initialDistance > 0) {
                            pinchVector.set(event);
                            pinchVector.calculateLength();

                            float distance = pinchVector.length;

                            if (initialDistance != distance) {

                                float newScale = (distance / initialDistance) * lastScale;

                                if (newScale <= maxScale) {
                                    scaleVector.length *= newScale;

                                    scaleVector.calculateEndPoint();

                                    scaleVector.length /= newScale;

                                    float newX = scaleVector.end.x;
                                    float newY = scaleVector.end.y;

                                    handleScale(newScale, newX, newY);
                                }
                            }
                        } else {
                            initialDistance = MathUtils.distance(event);

                            MathUtils.midpoint(event, midpoint);

                            scaleVector.setStart(midpoint);
                            scaleVector.setEnd(next);

                            scaleVector.calculateLength();
                            scaleVector.calculateAngle();

                            scaleVector.length /= lastScale;
                        }
                    } else {
                        if (!touched) {
                            touched = true;
                            last.x = event.getX();
                            last.y = event.getY();
                            next.x = image.getImageX();
                            next.y = image.getImageY();
                        } else if (!multiTouch) {
                            if (handleDrag(event.getX(), event.getY())) {
                                image.redraw();
                            }
                        }
                    }
                }
            }
        }

        return true;
    }

    protected void handleUp() {

        multiTouch = false;

        initialDistance = 0;
        lastScale = currentScale;

        if (!canDragX) {
            next.x = centerX;
        }

        if (!canDragY) {
            next.y = centerY;
        }

        boundCoordinates();

        if (!canDragX && !canDragY) {

            if (image.isLandscape()) {
                currentScale = fitScaleHorizontal;
                lastScale = fitScaleHorizontal;
            } else {
                currentScale = fitScaleVertical;
                lastScale = fitScaleVertical;
            }
        }

        image.setScale(currentScale);
        image.setPosition(next.x, next.y);

        if (imageListener != null) {
            imageListener.onScale(currentScale);
            imageListener.onPosition(next.x, next.y);
        }

        image.redraw();
    }

    protected void handleScale(float scale, float x, float y) {

        currentScale = scale;

        if (currentScale > maxScale) {
            currentScale = maxScale;
        } else if (currentScale < minScale) {
            currentScale = minScale;
        } else {
            next.x = x;
            next.y = y;
        }

        calculateBoundaries();

        image.setScale(currentScale);
        image.setPosition(next.x, next.y);

        if (imageListener != null) {
            imageListener.onScale(currentScale);
            imageListener.onPosition(next.x, next.y);
        }

        image.redraw();
    }

    protected boolean handleDrag(float x, float y) {
        current.x = x;
        current.y = y;

        float diffX = (current.x - last.x);
        float diffY = (current.y - last.y);

        if (diffX != 0 || diffY != 0) {

            if (canDragX)
                next.x += diffX;
            if (canDragY)
                next.y += diffY;

            boundCoordinates();

            last.x = current.x;
            last.y = current.y;

            if (canDragX || canDragY) {
                image.setPosition(next.x, next.y);

                if (imageListener != null) {
                    imageListener.onPosition(next.x, next.y);
                }

                return true;
            }
        }

        return false;
    }

    public void reset() {
        currentScale = startingScale;
        next.x = centerX;
        next.y = centerY;
        calculateBoundaries();
        image.setScale(currentScale);
        image.setPosition(next.x, next.y);
        image.redraw();
    }

    public float getMaxScale() {
        return maxScale;
    }

    public void setMaxScale(float maxScale) {
        this.maxScale = maxScale;
    }

    public float getMinScale() {
        return minScale;
    }

    public void setMinScale(float minScale) {
        this.minScale = minScale;
    }

    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

    protected void setCanvasWidth(int canvasWidth) {
        this.canvasWidth = canvasWidth;
    }

    protected void setCanvasHeight(int canvasHeight) {
        this.canvasHeight = canvasHeight;
    }

    protected void setFitScaleHorizontal(float fitScale) {
        this.fitScaleHorizontal = fitScale;
    }

    protected void setFitScaleVertical(float fitScaleVertical) {
        this.fitScaleVertical = fitScaleVertical;
    }

    protected void boundCoordinates() {
        if (next.x < boundaryLeft) {
            next.x = boundaryLeft;
            if (mPager != null) {
            }
            // mPager.isLeft(true);
        } else if (next.x > boundaryRight) {
            next.x = boundaryRight;
            if (mPager != null) {
            }
            // mPager.isRight(true);
        } else {
            if (mPager != null) {
                // mPager.isLeft(false);
                // mPager.isRight(false);
            }

        }

        if (next.x == (displayWidth / 2)) {
            if (mPager != null) {
                // mPager.isLeft(true);
                // mPager.isRight(true);
            }
        }

        if (next.y < boundaryTop) {
            next.y = boundaryTop;
        } else if (next.y > boundaryBottom) {
            next.y = boundaryBottom;
        }
    }

    protected void calculateBoundaries() {

        int effectiveWidth = Math.round((float) imageWidth * currentScale);
        int effectiveHeight = Math.round((float) imageHeight * currentScale);

        canDragX = effectiveWidth > displayWidth;
        canDragY = effectiveHeight > displayHeight;

        if (canDragX) {
            float diff = (float) (effectiveWidth - displayWidth) / 2.0f;
            boundaryLeft = centerX - diff;
            boundaryRight = centerX + diff;
        }

        if (canDragY) {
            float diff = (float) (effectiveHeight - displayHeight) / 2.0f;
            boundaryTop = centerY - diff;
            boundaryBottom = centerY + diff;
        }
    }
}