package org.andengine.input.touch.detector; import org.andengine.input.touch.TouchEvent; import org.andengine.util.math.MathUtils; import android.view.MotionEvent; /** * (c) 2010 Nicolas Gramlich * (c) 2011 Zynga Inc. * * @author Nicolas Gramlich * @since 19:16:19 - 04.11.2010 */ public class PinchZoomDetector extends BaseDetector { // =========================================================== // Constants // =========================================================== private static final float TRIGGER_PINCHZOOM_MINIMUM_DISTANCE_DEFAULT = 10; // =========================================================== // Fields // =========================================================== private final IPinchZoomDetectorListener mPinchZoomDetectorListener; private float mInitialDistance; private float mCurrentDistance; private boolean mPinchZooming; // =========================================================== // Constructors // =========================================================== public PinchZoomDetector(final IPinchZoomDetectorListener pPinchZoomDetectorListener) { this.mPinchZoomDetectorListener = pPinchZoomDetectorListener; } // =========================================================== // Getter & Setter // =========================================================== public boolean isZooming() { return this.mPinchZooming; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== /** * When {@link PinchZoomDetector#isZooming()} this method will call through to {@link IPinchZoomDetectorListener#onPinchZoomFinished(PinchZoomDetector, TouchEvent, float)}. */ @Override public void reset() { if(this.mPinchZooming) { this.mPinchZoomDetectorListener.onPinchZoomFinished(this, null, this.getZoomFactor()); } this.mInitialDistance = 0; this.mCurrentDistance = 0; this.mPinchZooming = false; } @Override public boolean onManagedTouchEvent(final TouchEvent pSceneTouchEvent) { final MotionEvent motionEvent = pSceneTouchEvent.getMotionEvent(); final int action = motionEvent.getAction() & MotionEvent.ACTION_MASK; switch(action) { case MotionEvent.ACTION_POINTER_DOWN: if(!this.mPinchZooming && PinchZoomDetector.hasTwoOrMorePointers(motionEvent)) { this.mInitialDistance = PinchZoomDetector.calculatePointerDistance(motionEvent); this.mCurrentDistance = this.mInitialDistance; if(this.mInitialDistance > PinchZoomDetector.TRIGGER_PINCHZOOM_MINIMUM_DISTANCE_DEFAULT) { this.mPinchZooming = true; this.mPinchZoomDetectorListener.onPinchZoomStarted(this, pSceneTouchEvent); } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: if(this.mPinchZooming) { this.mPinchZooming = false; this.mPinchZoomDetectorListener.onPinchZoomFinished(this, pSceneTouchEvent, this.getZoomFactor()); } break; case MotionEvent.ACTION_MOVE: if(this.mPinchZooming) { if (PinchZoomDetector.hasTwoOrMorePointers(motionEvent)) { this.mCurrentDistance = PinchZoomDetector.calculatePointerDistance(motionEvent); if(this.mCurrentDistance > PinchZoomDetector.TRIGGER_PINCHZOOM_MINIMUM_DISTANCE_DEFAULT) { this.mPinchZoomDetectorListener.onPinchZoom(this, pSceneTouchEvent, this.getZoomFactor()); } } else { this.mPinchZooming = false; this.mPinchZoomDetectorListener.onPinchZoomFinished(this, pSceneTouchEvent, this.getZoomFactor()); } } break; } return true; } private float getZoomFactor() { return this.mCurrentDistance / this.mInitialDistance; } // =========================================================== // Methods // =========================================================== /** * Calculate the euclidian distance between the first two fingers. */ private static float calculatePointerDistance(final MotionEvent pMotionEvent) { return MathUtils.distance(pMotionEvent.getX(0), pMotionEvent.getY(0), pMotionEvent.getX(1), pMotionEvent.getY(1)); } private static boolean hasTwoOrMorePointers(final MotionEvent pMotionEvent) { return pMotionEvent.getPointerCount() >= 2; } // =========================================================== // Inner and Anonymous Classes // =========================================================== public static interface IPinchZoomDetectorListener { // =========================================================== // Constants // =========================================================== // =========================================================== // Methods // =========================================================== public void onPinchZoomStarted(final PinchZoomDetector pPinchZoomDetector, final TouchEvent pSceneTouchEvent); public void onPinchZoom(final PinchZoomDetector pPinchZoomDetector, final TouchEvent pTouchEvent, final float pZoomFactor); public void onPinchZoomFinished(final PinchZoomDetector pPinchZoomDetector, final TouchEvent pTouchEvent, final float pZoomFactor); } }