OSDN Git Service

Edge of screen slop detection for ScaleGestureDetector.
authorAdam Powell <adamp@google.com>
Wed, 20 Jan 2010 00:33:46 +0000 (16:33 -0800)
committerAdam Powell <adamp@google.com>
Wed, 20 Jan 2010 00:33:46 +0000 (16:33 -0800)
core/java/android/view/ScaleGestureDetector.java

index 2da8f14..50bfefc 100644 (file)
@@ -17,6 +17,8 @@
 package android.view;
 
 import android.content.Context;
+import android.util.DisplayMetrics;
+import android.util.Log;
 
 /**
  * Detects transformation gestures involving more than one pointer ("multitouch")
@@ -137,10 +139,20 @@ public class ScaleGestureDetector {
     private float mCurrPressure;
     private float mPrevPressure;
     private long mTimeDelta;
+    
+    private float mEdgeSlop;
+    private float mRightSlopEdge;
+    private float mBottomSlopEdge;
+    private boolean mSloppyGesture;
 
     public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+        ViewConfiguration config = ViewConfiguration.get(context);
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         mContext = context;
         mListener = listener;
+        mEdgeSlop = config.getScaledEdgeSlop();
+        mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
+        mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
     }
 
     public boolean onTouchEvent(MotionEvent event) {
@@ -152,15 +164,68 @@ public class ScaleGestureDetector {
                     action == MotionEvent.ACTION_POINTER_2_DOWN) &&
                     event.getPointerCount() >= 2) {
                 // We have a new multi-finger gesture
-                
+
                 // Be paranoid in case we missed an event
                 reset();
-                
+
                 mPrevEvent = MotionEvent.obtain(event);
                 mTimeDelta = 0;
-                
+
                 setContext(event);
-                mGestureInProgress = mListener.onScaleBegin(this);
+
+                // Check if we have a sloppy gesture. If so, delay
+                // the beginning of the gesture until we're sure that's
+                // what the user wanted. Sloppy gestures can happen if the
+                // edge of the user's hand is touching the screen, for example.
+                final float edgeSlop = mEdgeSlop;
+                final float rightSlop = mRightSlopEdge;
+                final float bottomSlop = mBottomSlopEdge;
+                final float x0 = event.getRawX();
+                final float y0 = event.getRawY();
+                final float x1 = getRawX(event, 1);
+                final float y1 = getRawY(event, 1);
+
+                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop ||
+                        x1 < edgeSlop || y1 < edgeSlop;
+                boolean p1sloppy = x0 > rightSlop || y0 > bottomSlop ||
+                        x1 > rightSlop || y1 > bottomSlop;
+
+                if (p0sloppy) {
+                    mFocusX = event.getX(1);
+                    mFocusY = event.getY(1);
+                    mSloppyGesture = true;
+                } else if (p1sloppy) {
+                    mFocusX = event.getX(0);
+                    mFocusY = event.getY(0);
+                    mSloppyGesture = true;
+                } else {
+                    mGestureInProgress = mListener.onScaleBegin(this);
+                }
+            } else if (action == MotionEvent.ACTION_MOVE && mSloppyGesture) {
+                // Initiate sloppy gestures if we've moved outside of the slop area.
+                final float edgeSlop = mEdgeSlop;
+                final float rightSlop = mRightSlopEdge;
+                final float bottomSlop = mBottomSlopEdge;
+                final float x0 = event.getRawX();
+                final float y0 = event.getRawY();
+                final float x1 = getRawX(event, 1);
+                final float y1 = getRawY(event, 1);
+
+                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop ||
+                x1 < edgeSlop || y1 < edgeSlop;
+                boolean p1sloppy = x0 > rightSlop || y0 > bottomSlop ||
+                x1 > rightSlop || y1 > bottomSlop;
+
+                if (p0sloppy) {
+                    mFocusX = event.getX(1);
+                    mFocusY = event.getY(1);
+                } else if (p1sloppy) {
+                    mFocusX = event.getX(0);
+                    mFocusY = event.getY(0);
+                } else {
+                    mSloppyGesture = false;
+                    mGestureInProgress = mListener.onScaleBegin(this);
+                }
             }
         } else {
             // Transform gesture in progress - attempt to handle it
@@ -169,22 +234,24 @@ public class ScaleGestureDetector {
                 case MotionEvent.ACTION_POINTER_2_UP:
                     // Gesture ended
                     setContext(event);
-                    
+
                     // Set focus point to the remaining finger
                     int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
                             >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
                     mFocusX = event.getX(id);
                     mFocusY = event.getY(id);
-                    
-                    mListener.onScaleEnd(this);
-                    mGestureInProgress = false;
+
+                    if (!mSloppyGesture) {
+                        mListener.onScaleEnd(this);
+                    }
 
                     reset();
                     break;
 
                 case MotionEvent.ACTION_CANCEL:
-                    mListener.onScaleEnd(this);
-                    mGestureInProgress = false;
+                    if (!mSloppyGesture) {
+                        mListener.onScaleEnd(this);
+                    }
 
                     reset();
                     break;
@@ -208,6 +275,22 @@ public class ScaleGestureDetector {
         }
         return handled;
     }
+    
+    /**
+     * MotionEvent has no getRawX(int) method; simulate it pending future API approval. 
+     */
+    private static float getRawX(MotionEvent event, int pointerIndex) {
+        float offset = event.getX() - event.getRawX();
+        return event.getX(pointerIndex) + offset;
+    }
+    
+    /**
+     * MotionEvent has no getRawY(int) method; simulate it pending future API approval. 
+     */
+    private static float getRawY(MotionEvent event, int pointerIndex) {
+        float offset = event.getY() - event.getRawY();
+        return event.getY(pointerIndex) + offset;
+    }
 
     private void setContext(MotionEvent curr) {
         if (mCurrEvent != null) {
@@ -255,6 +338,8 @@ public class ScaleGestureDetector {
             mCurrEvent.recycle();
             mCurrEvent = null;
         }
+        mSloppyGesture = false;
+        mGestureInProgress = false;
     }
 
     /**