Android Open Source - android-pdfview Drag Pinch Listener






From Project

Back to project page android-pdfview.

License

The source code is released under:

GNU General Public License

If you think the Android project android-pdfview listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * Copyright 2014 Joan Zapata//from w  w w . j av a2  s  .c o m
 *
 * This file is part of Android-pdfview.
 *
 * Android-pdfview is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Android-pdfview is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Android-pdfview.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.joanzapata.pdfview.util;

import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

/**
 * @author Joan Zapata
 *         <p/>
 *         This class manage MotionEvents. Use it on your view with
 *         setOnTouchListener(dragManager);
 *         <p/>
 *         Use {@link #setOnDragListener(OnDragListener)} and {@link #setOnPinchListener(OnPinchListener)}
 *         to receive events when a drag or pinch event occurs.
 */
public class DragPinchListener implements OnTouchListener {

    /**
     * Max time a finger can stay pressed before this
     * action is considered as a non-click (in ms)
     */
    private static final long MAX_CLICK_TIME = 500;

    /**
     * Max distance a finger can move before this action
     * is considered as a non-click (in px)
     */
    private static final float MAX_CLICK_DISTANCE = 5;

    /**
     * Max time between 2 clicks to be considered as a
     * double click
     */
    private static final float MAX_DOUBLE_CLICK_TIME = 280;

    private static final int POINTER1 = 0, POINTER2 = 1;

    /** Implement this interface to receive Drag events */
    public static interface OnDragListener {

        /**
         * @param dx The differential X offset
         * @param dy The differential Y offset
         */
        void onDrag(float dx, float dy);

        /** Called when a drag event starts */
        void startDrag(float x, float y);

        /** Called when a drag event stops */
        void endDrag(float x, float y);

    }

    /** Implement this interface to receive Pinch events */
    public static interface OnPinchListener {

        /**
         * @param dr    The differential ratio
         * @param pivot The pivot point on which the redim occurs
         */
        void onPinch(float dr, PointF pivot);

    }

    /** Implement this interface to receive Double Tap events */
    public interface OnDoubleTapListener {

        /**
         * Called when a double tap happens.
         * @param x X-offset of event.
         * @param y Y-offset of event.
         */
        void onDoubleTap(float x, float y);

    }

    enum State {NONE, ZOOM, DRAG}

    private State state = State.NONE;

    private float dragLastX, dragLastY;

    private float pointer2LastX, pointer2LastY;

    private float zoomLastDistance;

    private OnDragListener onDragListener;

    private OnPinchListener onPinchListener;

    private OnDoubleTapListener onDoubleTapListener;

    private float lastDownX, lastDownY;

    private long lastClickTime;

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

            // NORMAL CASE : FIRST POINTER DOWN
            case MotionEvent.ACTION_DOWN:
                // Start dragging
                startDrag(event);
                state = State.DRAG;
                lastDownX = event.getX();
                lastDownY = event.getY();
                break;

            // NORMAL CASE : SECOND POINTER DOWN
            case MotionEvent.ACTION_POINTER_2_DOWN:
                startDrag(event);
                startZoom(event);
                state = State.ZOOM;
                break;

            // NORMAL CASE : SECOND POINTER UP
            case MotionEvent.ACTION_POINTER_2_UP:
                // End zooming, goes back to dragging
                state = State.DRAG;
                break;

            // NORMAL CASE : FIRST POINTER UP
            case MotionEvent.ACTION_UP:
                // End everything
                state = State.NONE;
                endDrag();

                // Treat clicks
                if (isClick(event, lastDownX, lastDownY, event.getX(), event.getY())) {
                    long time = System.currentTimeMillis();
                    if (time - lastClickTime < MAX_DOUBLE_CLICK_TIME) {
                        if (onDoubleTapListener != null) {
                            onDoubleTapListener.onDoubleTap(event.getX(), event.getY());
                        }
                        lastClickTime = 0;
                    } else {
                        lastClickTime = System.currentTimeMillis();
                    }
                }
                break;

            // TRICKY CASE : FIRST POINTER UP WHEN SECOND STILL DOWN
            case MotionEvent.ACTION_POINTER_1_UP:

                // FIXME Probably not the good value
                dragLastX = pointer2LastX;
                dragLastY = pointer2LastY;
                startDrag(event);
                state = State.DRAG;
                break;

            // TRICKY CASE : FIRST POINTER UP THEN DOWN WHILE SECOND POINTER STILL UP
            case MotionEvent.ACTION_POINTER_1_DOWN:
                pointer2LastX = event.getX(POINTER1);
                pointer2LastY = event.getY(POINTER1);

                startDrag(event);
                startZoom(event);
                state = State.ZOOM;
                break;

            // NORMAL CASE : MOVE
            case MotionEvent.ACTION_MOVE:

                switch (state) {
                    case ZOOM:
                        pointer2LastX = event.getX(POINTER2);
                        pointer2LastY = event.getY(POINTER2);
                        zoom(event);

                    case DRAG:
                        drag(event);
                        break;
                    default:
                        break;
                }
                break;
        }

        return true;
    }

    private void endDrag() {
        onDragListener.endDrag(dragLastX, dragLastY);
    }

    private void startZoom(MotionEvent event) {
        zoomLastDistance = distance(event);
    }

    private void zoom(MotionEvent event) {
        float zoomCurrentDistance = distance(event);

        if (onPinchListener != null) {
            onPinchListener.onPinch(zoomCurrentDistance / zoomLastDistance, //
                    new PointF(event.getX(POINTER1), event.getY(POINTER1)));
        }

        zoomLastDistance = zoomCurrentDistance;
    }

    private void startDrag(MotionEvent event) {
        dragLastX = event.getX(POINTER1);
        dragLastY = event.getY(POINTER1);
        onDragListener.startDrag(dragLastX, dragLastY);
    }

    private void drag(MotionEvent event) {
        float dragCurrentX = event.getX(POINTER1);
        float dragCurrentY = event.getY(POINTER1);

        if (onDragListener != null) {
            onDragListener.onDrag(dragCurrentX - dragLastX,
                    dragCurrentY - dragLastY);
        }

        dragLastX = dragCurrentX;
        dragLastY = dragCurrentY;
    }

    /** Calculates the distance between the 2 current pointers */
    private float distance(MotionEvent event) {
        if (event.getPointerCount() < 2) {
            return 0;
        }
        return PointF.length(event.getX(POINTER1) - event.getX(POINTER2), //
                event.getY(POINTER1) - event.getY(POINTER2));
    }

    /**
     * Test if a MotionEvent with the given start and end offsets
     * can be considered as a "click".
     * @param upEvent The final finger-up event.
     * @param xDown   The x-offset of the down event.
     * @param yDown   The y-offset of the down event.
     * @param xUp     The x-offset of the up event.
     * @param yUp     The y-offset of the up event.
     * @return true if it's a click, false otherwise
     */
    private boolean isClick(MotionEvent upEvent, float xDown, float yDown, float xUp, float yUp) {
        if (upEvent == null) return false;
        long time = upEvent.getEventTime() - upEvent.getDownTime();
        float distance = PointF.length( //
                xDown - xUp, //
                yDown - yUp);
        return time < MAX_CLICK_TIME && distance < MAX_CLICK_DISTANCE;
    }

    public void setOnDragListener(OnDragListener onDragListener) {
        this.onDragListener = onDragListener;
    }

    public void setOnPinchListener(OnPinchListener onPinchListener) {
        this.onPinchListener = onPinchListener;
    }

    public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
        this.onDoubleTapListener = onDoubleTapListener;
    }

}




Java Source Code List

com.joanzapata.PDFViewActivity.java
com.joanzapata.pdfview.AnimationManager.java
com.joanzapata.pdfview.CacheManager.java
com.joanzapata.pdfview.DecodingAsyncTask.java
com.joanzapata.pdfview.DragPinchManager.java
com.joanzapata.pdfview.PDFView.java
com.joanzapata.pdfview.RenderingAsyncTask.java
com.joanzapata.pdfview.SpiralLoopManager.java
com.joanzapata.pdfview.exception.FileNotFoundException.java
com.joanzapata.pdfview.listener.OnDrawListener.java
com.joanzapata.pdfview.listener.OnLoadCompleteListener.java
com.joanzapata.pdfview.listener.OnPageChangeListener.java
com.joanzapata.pdfview.model.PagePart.java
com.joanzapata.pdfview.util.ArrayUtils.java
com.joanzapata.pdfview.util.Constants.java
com.joanzapata.pdfview.util.DragPinchListener.java
com.joanzapata.pdfview.util.FileUtils.java
com.joanzapata.pdfview.util.NumberUtils.java
org.vudroid.core.DecodeServiceBase.java
org.vudroid.core.DecodeService.java
org.vudroid.core.DocumentView.java
org.vudroid.core.PageTreeNode.java
org.vudroid.core.Page.java
org.vudroid.core.VuDroidLibraryLoader.java
org.vudroid.core.codec.CodecContext.java
org.vudroid.core.codec.CodecDocument.java
org.vudroid.core.codec.CodecPage.java
org.vudroid.core.events.BringUpZoomControlsEvent.java
org.vudroid.core.events.BringUpZoomControlsListener.java
org.vudroid.core.events.CurrentPageListener.java
org.vudroid.core.events.DecodingProgressListener.java
org.vudroid.core.events.EventDispatcher.java
org.vudroid.core.events.Event.java
org.vudroid.core.events.SafeEvent.java
org.vudroid.core.events.ZoomChangedEvent.java
org.vudroid.core.events.ZoomListener.java
org.vudroid.core.models.CurrentPageModel.java
org.vudroid.core.models.DecodingProgressModel.java
org.vudroid.core.models.ZoomModel.java
org.vudroid.core.multitouch.MultiTouchZoomImpl.java
org.vudroid.core.multitouch.MultiTouchZoom.java
org.vudroid.core.utils.MD5StringUtil.java
org.vudroid.core.utils.PathFromUri.java
org.vudroid.pdfdroid.codec.PdfContext.java
org.vudroid.pdfdroid.codec.PdfDocument.java
org.vudroid.pdfdroid.codec.PdfPage.java