1.Use Touch to Perform Scaling
As discussed in Detecting Common Gestures, GestureDetector
helps you detect common gestures used by Android such as scrolling, flinging, and long press. For scaling, Android provides ScaleGestureDetector
. GestureDetector
and ScaleGestureDetector
can be used together when you want a view to recognize additional gestures.
ScaleGestureDetector 是用来识别缩放手势的。它可以和GestureDetector同时使用,来识别额外的手势.
To report detected gesture events, gesture detectors use listener objects passed to their constructors.ScaleGestureDetector
uses ScaleGestureDetector.OnScaleGestureListener
. Android provides ScaleGestureDetector.SimpleOnScaleGestureListener
as a helper class that you can extend if you don’t care about all of the reported events.
ScaleGestureDetector.SimpleOnScaleGestureListener 是一个封装好的缩放手势类.用来接收探测到的手势,构造手势探测器 ScaleGestureDetector 的时候要用到它.
2.Basic scaling example
Here is a snippet that illustrates the basic ingredients involved in scaling.
1 private ScaleGestureDetector mScaleDetector; 2 private float mScaleFactor = 1.f; 3 4 public MyCustomView(Context mContext){ 5 ... 6 // View code goes here 7 ... 8 mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 9 } 10 11 @Override 12 public boolean onTouchEvent(MotionEvent ev) { 13 // Let the ScaleGestureDetector inspect all events. 14 mScaleDetector.onTouchEvent(ev); 15 return true; 16 } 17 18 @Override 19 public void onDraw(Canvas canvas) { 20 super.onDraw(canvas); 21 22 canvas.save(); 23 canvas.scale(mScaleFactor, mScaleFactor); 24 ... 25 // onDraw() code goes here 26 ... 27 canvas.restore(); 28 } 29 30 private class ScaleListener 31 extends ScaleGestureDetector.SimpleOnScaleGestureListener { 32 @Override 33 public boolean onScale(ScaleGestureDetector detector) { 34 mScaleFactor *= detector.getScaleFactor(); 35 36 // Don't let the object get too small or too large. 37 mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); 38 39 invalidate(); 40 return true; 41 } 42 }
3.More complex scaling example
Here is a more complex example from the InteractiveChart
sample provided with this class. TheInteractiveChart
sample supports both scrolling (panning) and scaling with multiple fingers, using the ScaleGestureDetector
"span" (getCurrentSpanX/Y
) and "focus" (getFocusX/Y
) features:
下面是一个复杂的手势识别示例的片段,使用了两个识别器.完整示例下载地址 InteractiveChart
1 @Override 2 private RectF mCurrentViewport = 3 new RectF(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX); 4 private Rect mContentRect; 5 private ScaleGestureDetector mScaleGestureDetector; 6 ... 7 public boolean onTouchEvent(MotionEvent event) { 8 boolean retVal = mScaleGestureDetector.onTouchEvent(event); 9 retVal = mGestureDetector.onTouchEvent(event) || retVal; 10 return retVal || super.onTouchEvent(event); 11 } 12 13 /** 14 * The scale listener, used for handling multi-finger scale gestures. 15 */ 16 private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener 17 = new ScaleGestureDetector.SimpleOnScaleGestureListener() { 18 /** 19 * This is the active focal point in terms of the viewport. Could be a local 20 * variable but kept here to minimize per-frame allocations. 21 */ 22 private PointF viewportFocus = new PointF(); 23 private float lastSpanX; 24 private float lastSpanY; 25 26 // Detects that new pointers are going down. 27 @Override 28 public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { 29 lastSpanX = ScaleGestureDetectorCompat. 30 getCurrentSpanX(scaleGestureDetector); 31 lastSpanY = ScaleGestureDetectorCompat. 32 getCurrentSpanY(scaleGestureDetector); 33 return true; 34 } 35 36 @Override 37 public boolean onScale(ScaleGestureDetector scaleGestureDetector) { 38 39 float spanX = ScaleGestureDetectorCompat. 40 getCurrentSpanX(scaleGestureDetector); 41 float spanY = ScaleGestureDetectorCompat. 42 getCurrentSpanY(scaleGestureDetector); 43 44 float newWidth = lastSpanX / spanX * mCurrentViewport.width(); 45 float newHeight = lastSpanY / spanY * mCurrentViewport.height(); 46 47 float focusX = scaleGestureDetector.getFocusX(); 48 float focusY = scaleGestureDetector.getFocusY(); 49 // Makes sure that the chart point is within the chart region. 50 // See the sample for the implementation of hitTest(). 51 hitTest(scaleGestureDetector.getFocusX(), 52 scaleGestureDetector.getFocusY(), 53 viewportFocus); 54 55 mCurrentViewport.set( 56 viewportFocus.x 57 - newWidth * (focusX - mContentRect.left) 58 / mContentRect.width(), 59 viewportFocus.y 60 - newHeight * (mContentRect.bottom - focusY) 61 / mContentRect.height(), 62 0, 63 0); 64 mCurrentViewport.right = mCurrentViewport.left + newWidth; 65 mCurrentViewport.bottom = mCurrentViewport.top + newHeight; 66 ... 67 // Invalidates the View to update the display. 68 ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this); 69 70 lastSpanX = spanX; 71 lastSpanY = spanY; 72 return true; 73 } 74 };