Advertisement
Guest User

ScaleGestureDetector

a guest
Apr 9th, 2012
240
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 29.26 KB | None | 0 0
  1. /*
  2.  * Copyright (C) 2010 The Android Open Source Project
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16.  
  17. package android.view;
  18.  
  19. import android.content.Context;
  20. import android.util.DisplayMetrics;
  21. import android.util.FloatMath;
  22. import android.util.Log;
  23.  
  24. /**
  25.  * Detects transformation gestures involving more than one pointer ("multitouch")
  26.  * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
  27.  * callback will notify users when a particular gesture event has occurred.
  28.  * This class should only be used with {@link MotionEvent}s reported via touch.
  29.  *
  30.  * To use this class:
  31.  * <ul>
  32.  *  <li>Create an instance of the {@code ScaleGestureDetector} for your
  33.  *      {@link View}
  34.  *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
  35.  *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
  36.  *          callback will be executed when the events occur.
  37.  * </ul>
  38.  */
  39. public class ScaleGestureDetector {
  40.     private static final String TAG = "ScaleGestureDetector";
  41.  
  42.     /**
  43.      * The listener for receiving notifications when gestures occur.
  44.      * If you want to listen for all the different gestures then implement
  45.      * this interface. If you only want to listen for a subset it might
  46.      * be easier to extend {@link SimpleOnScaleGestureListener}.
  47.      *
  48.      * An application will receive events in the following order:
  49.      * <ul>
  50.      *  <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)}
  51.      *  <li>Zero or more {@link OnScaleGestureListener#onScale(ScaleGestureDetector)}
  52.      *  <li>One {@link OnScaleGestureListener#onScaleEnd(ScaleGestureDetector)}
  53.      * </ul>
  54.      */
  55.     public interface OnScaleGestureListener {
  56.         /**
  57.          * Responds to scaling events for a gesture in progress.
  58.          * Reported by pointer motion.
  59.          *
  60.          * @param detector The detector reporting the event - use this to
  61.          *          retrieve extended info about event state.
  62.          * @return Whether or not the detector should consider this event
  63.          *          as handled. If an event was not handled, the detector
  64.          *          will continue to accumulate movement until an event is
  65.          *          handled. This can be useful if an application, for example,
  66.          *          only wants to update scaling factors if the change is
  67.          *          greater than 0.01.
  68.          */
  69.         public boolean onScale(ScaleGestureDetector detector);
  70.  
  71.         /**
  72.          * Responds to the beginning of a scaling gesture. Reported by
  73.          * new pointers going down.
  74.          *
  75.          * @param detector The detector reporting the event - use this to
  76.          *          retrieve extended info about event state.
  77.          * @return Whether or not the detector should continue recognizing
  78.          *          this gesture. For example, if a gesture is beginning
  79.          *          with a focal point outside of a region where it makes
  80.          *          sense, onScaleBegin() may return false to ignore the
  81.          *          rest of the gesture.
  82.          */
  83.         public boolean onScaleBegin(ScaleGestureDetector detector);
  84.  
  85.         /**
  86.          * Responds to the end of a scale gesture. Reported by existing
  87.          * pointers going up.
  88.          *
  89.          * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
  90.          * and {@link ScaleGestureDetector#getFocusY()} will return the location
  91.          * of the pointer remaining on the screen.
  92.          *
  93.          * @param detector The detector reporting the event - use this to
  94.          *          retrieve extended info about event state.
  95.          */
  96.         public void onScaleEnd(ScaleGestureDetector detector);
  97.     }
  98.  
  99.     /**
  100.      * A convenience class to extend when you only want to listen for a subset
  101.      * of scaling-related events. This implements all methods in
  102.      * {@link OnScaleGestureListener} but does nothing.
  103.      * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} returns
  104.      * {@code false} so that a subclass can retrieve the accumulated scale
  105.      * factor in an overridden onScaleEnd.
  106.      * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns
  107.      * {@code true}.
  108.      */
  109.     public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
  110.  
  111.         public boolean onScale(ScaleGestureDetector detector) {
  112.             return false;
  113.         }
  114.  
  115.         public boolean onScaleBegin(ScaleGestureDetector detector) {
  116.             return true;
  117.         }
  118.  
  119.         public void onScaleEnd(ScaleGestureDetector detector) {
  120.             // Intentionally empty
  121.         }
  122.     }
  123.  
  124.     /**
  125.      * This value is the threshold ratio between our previous combined pressure
  126.      * and the current combined pressure. We will only fire an onScale event if
  127.      * the computed ratio between the current and previous event pressures is
  128.      * greater than this value. When pressure decreases rapidly between events
  129.      * the position values can often be imprecise, as it usually indicates
  130.      * that the user is in the process of lifting a pointer off of the device.
  131.      * Its value was tuned experimentally.
  132.      */
  133.     private static final float PRESSURE_THRESHOLD = 0.67f;
  134.  
  135.     private final Context mContext;
  136.     private final OnScaleGestureListener mListener;
  137.     private boolean mGestureInProgress;
  138.  
  139.     private MotionEvent mPrevEvent;
  140.     private MotionEvent mCurrEvent;
  141.  
  142.     private float mFocusX;
  143.     private float mFocusY;
  144.     private float mPrevFingerDiffX;
  145.     private float mPrevFingerDiffY;
  146.     private float mCurrFingerDiffX;
  147.     private float mCurrFingerDiffY;
  148.     private float mCurrLen;
  149.     private float mPrevLen;
  150.     private float mScaleFactor;
  151.     private float mCurrPressure;
  152.     private float mPrevPressure;
  153.     private long mTimeDelta;
  154.  
  155.     private final float mEdgeSlop;
  156.     private float mRightSlopEdge;
  157.     private float mBottomSlopEdge;
  158.     private boolean mSloppyGesture;
  159.     private boolean mInvalidGesture;
  160.  
  161.     // Pointer IDs currently responsible for the two fingers controlling the gesture
  162.     private int mActiveId0;
  163.     private int mActiveId1;
  164.     private boolean mActive0MostRecent;
  165.  
  166.     /**
  167.      * Consistency verifier for debugging purposes.
  168.      */
  169.     private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
  170.             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
  171.                     new InputEventConsistencyVerifier(this, 0) : null;
  172.  
  173.     public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
  174.         ViewConfiguration config = ViewConfiguration.get(context);
  175.         mContext = context;
  176.         mListener = listener;
  177.         mEdgeSlop = config.getScaledEdgeSlop();
  178.     }
  179.  
  180.     public boolean onTouchEvent(MotionEvent event) {
  181.         if (mInputEventConsistencyVerifier != null) {
  182.             mInputEventConsistencyVerifier.onTouchEvent(event, 0);
  183.         }
  184.  
  185.         final int action = event.getActionMasked();
  186.  
  187.         if (action == MotionEvent.ACTION_DOWN) {
  188.             reset(); // Start fresh
  189.         }
  190.  
  191.         boolean handled = true;
  192.         if (mInvalidGesture) {
  193.             handled = false;
  194.         } else if (!mGestureInProgress) {
  195.             switch (action) {
  196.             case MotionEvent.ACTION_DOWN: {
  197.                 mActiveId0 = event.getPointerId(0);
  198.                 mActive0MostRecent = true;
  199.             }
  200.             break;
  201.  
  202.             case MotionEvent.ACTION_UP:
  203.                 reset();
  204.                 break;
  205.  
  206.             case MotionEvent.ACTION_POINTER_DOWN: {
  207.                 // We have a new multi-finger gesture
  208.  
  209.                 // as orientation can change, query the metrics in touch down
  210.                 DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
  211.                 mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
  212.                 mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
  213.  
  214.                 if (mPrevEvent != null) mPrevEvent.recycle();
  215.                 mPrevEvent = MotionEvent.obtain(event);
  216.                 mTimeDelta = 0;
  217.  
  218.                 int index1 = event.getActionIndex();
  219.                 int index0 = event.findPointerIndex(mActiveId0);
  220.                 mActiveId1 = event.getPointerId(index1);
  221.                 if (index0 < 0 || index0 == index1) {
  222.                     // Probably someone sending us a broken event stream.
  223.                     index0 = findNewActiveIndex(event, index0 == index1 ? -1 : mActiveId1, index0);
  224.                     mActiveId0 = event.getPointerId(index0);
  225.                 }
  226.                 mActive0MostRecent = false;
  227.  
  228.                 setContext(event);
  229.  
  230.                 // Check if we have a sloppy gesture. If so, delay
  231.                 // the beginning of the gesture until we're sure that's
  232.                 // what the user wanted. Sloppy gestures can happen if the
  233.                 // edge of the user's hand is touching the screen, for example.
  234.                 final float edgeSlop = mEdgeSlop;
  235.                 final float rightSlop = mRightSlopEdge;
  236.                 final float bottomSlop = mBottomSlopEdge;
  237.                 float x0 = getRawX(event, index0);
  238.                 float y0 = getRawY(event, index0);
  239.                 float x1 = getRawX(event, index1);
  240.                 float y1 = getRawY(event, index1);
  241.  
  242.                 boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
  243.                         || x0 > rightSlop || y0 > bottomSlop;
  244.                 boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
  245.                         || x1 > rightSlop || y1 > bottomSlop;
  246.  
  247.                 if (p0sloppy && p1sloppy) {
  248.                     mFocusX = -1;
  249.                     mFocusY = -1;
  250.                     mSloppyGesture = true;
  251.                 } else if (p0sloppy) {
  252.                     mFocusX = event.getX(index1);
  253.                     mFocusY = event.getY(index1);
  254.                     mSloppyGesture = true;
  255.                 } else if (p1sloppy) {
  256.                     mFocusX = event.getX(index0);
  257.                     mFocusY = event.getY(index0);
  258.                     mSloppyGesture = true;
  259.                 } else {
  260.                     mSloppyGesture = false;
  261.                     mGestureInProgress = mListener.onScaleBegin(this);
  262.                 }
  263.             }
  264.             break;
  265.  
  266.             case MotionEvent.ACTION_MOVE:
  267.                 if (mSloppyGesture) {
  268.                     // Initiate sloppy gestures if we've moved outside of the slop area.
  269.                     final float edgeSlop = mEdgeSlop;
  270.                     final float rightSlop = mRightSlopEdge;
  271.                     final float bottomSlop = mBottomSlopEdge;
  272.                     int index0 = event.findPointerIndex(mActiveId0);
  273.                     int index1 = event.findPointerIndex(mActiveId1);
  274.  
  275.                     float x0 = getRawX(event, index0);
  276.                     float y0 = getRawY(event, index0);
  277.                     float x1 = getRawX(event, index1);
  278.                     float y1 = getRawY(event, index1);
  279.  
  280.                     boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
  281.                             || x0 > rightSlop || y0 > bottomSlop;
  282.                     boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
  283.                             || x1 > rightSlop || y1 > bottomSlop;
  284.  
  285.                     if (p0sloppy) {
  286.                         // Do we have a different pointer that isn't sloppy?
  287.                         int index = findNewActiveIndex(event, mActiveId1, index0);
  288.                         if (index >= 0) {
  289.                             index0 = index;
  290.                             mActiveId0 = event.getPointerId(index);
  291.                             x0 = getRawX(event, index);
  292.                             y0 = getRawY(event, index);
  293.                             p0sloppy = false;
  294.                         }
  295.                     }
  296.  
  297.                     if (p1sloppy) {
  298.                         // Do we have a different pointer that isn't sloppy?
  299.                         int index = findNewActiveIndex(event, mActiveId0, index1);
  300.                         if (index >= 0) {
  301.                             index1 = index;
  302.                             mActiveId1 = event.getPointerId(index);
  303.                             x1 = getRawX(event, index);
  304.                             y1 = getRawY(event, index);
  305.                             p1sloppy = false;
  306.                         }
  307.                     }
  308.  
  309.                     if(p0sloppy && p1sloppy) {
  310.                         mFocusX = -1;
  311.                         mFocusY = -1;
  312.                     } else if (p0sloppy) {
  313.                         mFocusX = event.getX(index1);
  314.                         mFocusY = event.getY(index1);
  315.                     } else if (p1sloppy) {
  316.                         mFocusX = event.getX(index0);
  317.                         mFocusY = event.getY(index0);
  318.                     } else {
  319.                         mSloppyGesture = false;
  320.                         mGestureInProgress = mListener.onScaleBegin(this);
  321.                     }
  322.                 }
  323.                 break;
  324.  
  325.             case MotionEvent.ACTION_POINTER_UP:
  326.                 if (mSloppyGesture) {
  327.                     final int pointerCount = event.getPointerCount();
  328.                     final int actionIndex = event.getActionIndex();
  329.                     final int actionId = event.getPointerId(actionIndex);
  330.  
  331.                     if (pointerCount > 2) {
  332.                         if (actionId == mActiveId0) {
  333.                             final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex);
  334.                             if (newIndex >= 0) mActiveId0 = event.getPointerId(newIndex);
  335.                         } else if (actionId == mActiveId1) {
  336.                             final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex);
  337.                             if (newIndex >= 0) mActiveId1 = event.getPointerId(newIndex);
  338.                         }
  339.                     } else {
  340.                         // Set focus point to the remaining finger
  341.                         final int index = event.findPointerIndex(actionId == mActiveId0 ?
  342.                                 mActiveId1 : mActiveId0);
  343.                         if (index < 0) {
  344.                             mInvalidGesture = true;
  345.                             Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable());
  346.                             if (mGestureInProgress) {
  347.                                 mListener.onScaleEnd(this);
  348.                             }
  349.                             return false;
  350.                         }
  351.  
  352.                         mActiveId0 = event.getPointerId(index);
  353.  
  354.                         mActive0MostRecent = true;
  355.                         mActiveId1 = -1;
  356.                         mFocusX = event.getX(index);
  357.                         mFocusY = event.getY(index);
  358.                     }
  359.                 }
  360.                 break;
  361.             }
  362.         } else {
  363.             // Transform gesture in progress - attempt to handle it
  364.             switch (action) {
  365.                 case MotionEvent.ACTION_POINTER_DOWN: {
  366.                     // End the old gesture and begin a new one with the most recent two fingers.
  367.                     mListener.onScaleEnd(this);
  368.                     final int oldActive0 = mActiveId0;
  369.                     final int oldActive1 = mActiveId1;
  370.                     reset();
  371.  
  372.                     mPrevEvent = MotionEvent.obtain(event);
  373.                     mActiveId0 = mActive0MostRecent ? oldActive0 : oldActive1;
  374.                     mActiveId1 = event.getPointerId(event.getActionIndex());
  375.                     mActive0MostRecent = false;
  376.  
  377.                     int index0 = event.findPointerIndex(mActiveId0);
  378.                     if (index0 < 0 || mActiveId0 == mActiveId1) {
  379.                         // Probably someone sending us a broken event stream.
  380.                         Log.e(TAG, "Got " + MotionEvent.actionToString(action) +
  381.                                 " with bad state while a gesture was in progress. " +
  382.                                 "Did you forget to pass an event to " +
  383.                                 "ScaleGestureDetector#onTouchEvent?");
  384.                         index0 = findNewActiveIndex(event,
  385.                                 mActiveId0 == mActiveId1 ? -1 : mActiveId1, index0);
  386.                         mActiveId0 = event.getPointerId(index0);
  387.                     }
  388.  
  389.                     setContext(event);
  390.  
  391.                     mGestureInProgress = mListener.onScaleBegin(this);
  392.                 }
  393.                 break;
  394.  
  395.                 case MotionEvent.ACTION_POINTER_UP: {
  396.                     final int pointerCount = event.getPointerCount();
  397.                     final int actionIndex = event.getActionIndex();
  398.                     final int actionId = event.getPointerId(actionIndex);
  399.  
  400.                     boolean gestureEnded = false;
  401.                     if (pointerCount > 2) {
  402.                         if (actionId == mActiveId0) {
  403.                             final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex);
  404.                             if (newIndex >= 0) {
  405.                                 mListener.onScaleEnd(this);
  406.                                 mActiveId0 = event.getPointerId(newIndex);
  407.                                 mActive0MostRecent = true;
  408.                                 mPrevEvent = MotionEvent.obtain(event);
  409.                                 setContext(event);
  410.                                 mGestureInProgress = mListener.onScaleBegin(this);
  411.                             } else {
  412.                                 gestureEnded = true;
  413.                             }
  414.                         } else if (actionId == mActiveId1) {
  415.                             final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex);
  416.                             if (newIndex >= 0) {
  417.                                 mListener.onScaleEnd(this);
  418.                                 mActiveId1 = event.getPointerId(newIndex);
  419.                                 mActive0MostRecent = false;
  420.                                 mPrevEvent = MotionEvent.obtain(event);
  421.                                 setContext(event);
  422.                                 mGestureInProgress = mListener.onScaleBegin(this);
  423.                             } else {
  424.                                 gestureEnded = true;
  425.                             }
  426.                         }
  427.                         mPrevEvent.recycle();
  428.                         mPrevEvent = MotionEvent.obtain(event);
  429.                         setContext(event);
  430.                     } else {
  431.                         gestureEnded = true;
  432.                     }
  433.  
  434.                     if (gestureEnded) {
  435.                         // Gesture ended
  436.                         setContext(event);
  437.  
  438.                         // Set focus point to the remaining finger
  439.                         final int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0;
  440.                         final int index = event.findPointerIndex(activeId);
  441.                         mFocusX = event.getX(index);
  442.                         mFocusY = event.getY(index);
  443.  
  444.                         mListener.onScaleEnd(this);
  445.                         reset();
  446.                         mActiveId0 = activeId;
  447.                         mActive0MostRecent = true;
  448.                     }
  449.                 }
  450.                 break;
  451.  
  452.                 case MotionEvent.ACTION_CANCEL:
  453.                     mListener.onScaleEnd(this);
  454.                     reset();
  455.                     break;
  456.  
  457.                 case MotionEvent.ACTION_UP:
  458.                     reset();
  459.                     break;
  460.  
  461.                 case MotionEvent.ACTION_MOVE: {
  462.                     setContext(event);
  463.  
  464.                     // Only accept the event if our relative pressure is within
  465.                     // a certain limit - this can help filter shaky data as a
  466.                     // finger is lifted.
  467.                     if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
  468.                         final boolean updatePrevious = mListener.onScale(this);
  469.  
  470.                         if (updatePrevious) {
  471.                             mPrevEvent.recycle();
  472.                             mPrevEvent = MotionEvent.obtain(event);
  473.                         }
  474.                     }
  475.                 }
  476.                 break;
  477.             }
  478.         }
  479.  
  480.         if (!handled && mInputEventConsistencyVerifier != null) {
  481.             mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
  482.         }
  483.         return handled;
  484.     }
  485.  
  486.     private int findNewActiveIndex(MotionEvent ev, int otherActiveId, int oldIndex) {
  487.         final int pointerCount = ev.getPointerCount();
  488.  
  489.         // It's ok if this isn't found and returns -1, it simply won't match.
  490.         final int otherActiveIndex = ev.findPointerIndex(otherActiveId);
  491.         int newActiveIndex = -1;
  492.  
  493.         // Pick a new id and update tracking state. Only pick pointers not on the slop edges.
  494.         for (int i = 0; i < pointerCount; i++) {
  495.             if (i != oldIndex && i != otherActiveIndex) {
  496.                 final float edgeSlop = mEdgeSlop;
  497.                 final float rightSlop = mRightSlopEdge;
  498.                 final float bottomSlop = mBottomSlopEdge;
  499.                 float x = getRawX(ev, i);
  500.                 float y = getRawY(ev, i);
  501.                 if (x >= edgeSlop && y >= edgeSlop && x <= rightSlop && y <= bottomSlop) {
  502.                     newActiveIndex = i;
  503.                     break;
  504.                 }
  505.             }
  506.         }
  507.  
  508.         return newActiveIndex;
  509.     }
  510.  
  511.     /**
  512.      * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
  513.      */
  514.     private static float getRawX(MotionEvent event, int pointerIndex) {
  515.         if (pointerIndex < 0) return Float.MIN_VALUE;
  516.         if (pointerIndex == 0) return event.getRawX();
  517.         float offset = event.getRawX() - event.getX();
  518.         return event.getX(pointerIndex) + offset;
  519.     }
  520.  
  521.     /**
  522.      * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
  523.      */
  524.     private static float getRawY(MotionEvent event, int pointerIndex) {
  525.         if (pointerIndex < 0) return Float.MIN_VALUE;
  526.         if (pointerIndex == 0) return event.getRawY();
  527.         float offset = event.getRawY() - event.getY();
  528.         return event.getY(pointerIndex) + offset;
  529.     }
  530.  
  531.     private void setContext(MotionEvent curr) {
  532.         if (mCurrEvent != null) {
  533.             mCurrEvent.recycle();
  534.         }
  535.         mCurrEvent = MotionEvent.obtain(curr);
  536.  
  537.         mCurrLen = -1;
  538.         mPrevLen = -1;
  539.         mScaleFactor = -1;
  540.  
  541.         final MotionEvent prev = mPrevEvent;
  542.  
  543.         final int prevIndex0 = prev.findPointerIndex(mActiveId0);
  544.         final int prevIndex1 = prev.findPointerIndex(mActiveId1);
  545.         final int currIndex0 = curr.findPointerIndex(mActiveId0);
  546.         final int currIndex1 = curr.findPointerIndex(mActiveId1);
  547.  
  548.         if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0) {
  549.             mInvalidGesture = true;
  550.             Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable());
  551.             if (mGestureInProgress) {
  552.                 mListener.onScaleEnd(this);
  553.             }
  554.             return;
  555.         }
  556.  
  557.         final float px0 = prev.getX(prevIndex0);
  558.         final float py0 = prev.getY(prevIndex0);
  559.         final float px1 = prev.getX(prevIndex1);
  560.         final float py1 = prev.getY(prevIndex1);
  561.         final float cx0 = curr.getX(currIndex0);
  562.         final float cy0 = curr.getY(currIndex0);
  563.         final float cx1 = curr.getX(currIndex1);
  564.         final float cy1 = curr.getY(currIndex1);
  565.  
  566.         final float pvx = px1 - px0;
  567.         final float pvy = py1 - py0;
  568.         final float cvx = cx1 - cx0;
  569.         final float cvy = cy1 - cy0;
  570.         mPrevFingerDiffX = pvx;
  571.         mPrevFingerDiffY = pvy;
  572.         mCurrFingerDiffX = cvx;
  573.         mCurrFingerDiffY = cvy;
  574.  
  575.         mFocusX = cx0 + cvx * 0.5f;
  576.         mFocusY = cy0 + cvy * 0.5f;
  577.         mTimeDelta = curr.getEventTime() - prev.getEventTime();
  578.         mCurrPressure = curr.getPressure(currIndex0) + curr.getPressure(currIndex1);
  579.         mPrevPressure = prev.getPressure(prevIndex0) + prev.getPressure(prevIndex1);
  580.     }
  581.  
  582.     private void reset() {
  583.         if (mPrevEvent != null) {
  584.             mPrevEvent.recycle();
  585.             mPrevEvent = null;
  586.         }
  587.         if (mCurrEvent != null) {
  588.             mCurrEvent.recycle();
  589.             mCurrEvent = null;
  590.         }
  591.         mSloppyGesture = false;
  592.         mGestureInProgress = false;
  593.         mActiveId0 = -1;
  594.         mActiveId1 = -1;
  595.         mInvalidGesture = false;
  596.     }
  597.  
  598.     /**
  599.      * Returns {@code true} if a two-finger scale gesture is in progress.
  600.      * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
  601.      */
  602.     public boolean isInProgress() {
  603.         return mGestureInProgress;
  604.     }
  605.  
  606.     /**
  607.      * Get the X coordinate of the current gesture's focal point.
  608.      * If a gesture is in progress, the focal point is directly between
  609.      * the two pointers forming the gesture.
  610.      * If a gesture is ending, the focal point is the location of the
  611.      * remaining pointer on the screen.
  612.      * If {@link #isInProgress()} would return false, the result of this
  613.      * function is undefined.
  614.      *
  615.      * @return X coordinate of the focal point in pixels.
  616.      */
  617.     public float getFocusX() {
  618.         return mFocusX;
  619.     }
  620.  
  621.     /**
  622.      * Get the Y coordinate of the current gesture's focal point.
  623.      * If a gesture is in progress, the focal point is directly between
  624.      * the two pointers forming the gesture.
  625.      * If a gesture is ending, the focal point is the location of the
  626.      * remaining pointer on the screen.
  627.      * If {@link #isInProgress()} would return false, the result of this
  628.      * function is undefined.
  629.      *
  630.      * @return Y coordinate of the focal point in pixels.
  631.      */
  632.     public float getFocusY() {
  633.         return mFocusY;
  634.     }
  635.  
  636.     /**
  637.      * Return the current distance between the two pointers forming the
  638.      * gesture in progress.
  639.      *
  640.      * @return Distance between pointers in pixels.
  641.      */
  642.     public float getCurrentSpan() {
  643.         if (mCurrLen == -1) {
  644.             final float cvx = mCurrFingerDiffX;
  645.             final float cvy = mCurrFingerDiffY;
  646.             mCurrLen = FloatMath.sqrt(cvx*cvx + cvy*cvy);
  647.         }
  648.         return mCurrLen;
  649.     }
  650.  
  651.     /**
  652.      * Return the current x distance between the two pointers forming the
  653.      * gesture in progress.
  654.      *
  655.      * @return Distance between pointers in pixels.
  656.      */
  657.     public float getCurrentSpanX() {
  658.         return mCurrFingerDiffX;
  659.     }
  660.  
  661.     /**
  662.      * Return the current y distance between the two pointers forming the
  663.      * gesture in progress.
  664.      *
  665.      * @return Distance between pointers in pixels.
  666.      */
  667.     public float getCurrentSpanY() {
  668.         return mCurrFingerDiffY;
  669.     }
  670.  
  671.     /**
  672.      * Return the previous distance between the two pointers forming the
  673.      * gesture in progress.
  674.      *
  675.      * @return Previous distance between pointers in pixels.
  676.      */
  677.     public float getPreviousSpan() {
  678.         if (mPrevLen == -1) {
  679.             final float pvx = mPrevFingerDiffX;
  680.             final float pvy = mPrevFingerDiffY;
  681.             mPrevLen = FloatMath.sqrt(pvx*pvx + pvy*pvy);
  682.         }
  683.         return mPrevLen;
  684.     }
  685.  
  686.     /**
  687.      * Return the previous x distance between the two pointers forming the
  688.      * gesture in progress.
  689.      *
  690.      * @return Previous distance between pointers in pixels.
  691.      */
  692.     public float getPreviousSpanX() {
  693.         return mPrevFingerDiffX;
  694.     }
  695.  
  696.     /**
  697.      * Return the previous y distance between the two pointers forming the
  698.      * gesture in progress.
  699.      *
  700.      * @return Previous distance between pointers in pixels.
  701.      */
  702.     public float getPreviousSpanY() {
  703.         return mPrevFingerDiffY;
  704.     }
  705.  
  706.     /**
  707.      * Return the scaling factor from the previous scale event to the current
  708.      * event. This value is defined as
  709.      * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
  710.      *
  711.      * @return The current scaling factor.
  712.      */
  713.     public float getScaleFactor() {
  714.         if (mScaleFactor == -1) {
  715.             mScaleFactor = getCurrentSpan() / getPreviousSpan();
  716.         }
  717.         return mScaleFactor;
  718.     }
  719.  
  720.     /**
  721.      * Return the time difference in milliseconds between the previous
  722.      * accepted scaling event and the current scaling event.
  723.      *
  724.      * @return Time difference since the last scaling event in milliseconds.
  725.      */
  726.     public long getTimeDelta() {
  727.         return mTimeDelta;
  728.     }
  729.  
  730.     /**
  731.      * Return the event time of the current event being processed.
  732.      *
  733.      * @return Current event time in milliseconds.
  734.      */
  735.     public long getEventTime() {
  736.         return mCurrEvent.getEventTime();
  737.     }
  738. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement