今天研究了一下PhotoView,发现里面的自定义的手势事件可以支持所有的SDK版本,该事件可以实现拖拽、滑动、缩放功能。下面直接上代码:
1 public abstract class VersionedGestureDetector { 2 static final String LOG_TAG = "VersionedGestureDetector"; 3 OnGestureListener mListener; 4 5 public static VersionedGestureDetector newInstance(Context context, OnGestureListener listener) { 6 final int sdkVersion = Build.VERSION.SDK_INT; 7 VersionedGestureDetector detector = null; 8 if (sdkVersion < Build.VERSION_CODES.ECLAIR) { 9 detector = new CupcakeDetector(context); 10 } else if (sdkVersion < Build.VERSION_CODES.FROYO) { 11 detector = new EclairDetector(context); 12 } else { 13 detector = new FroyoDetector(context); 14 } 15 16 detector.mListener = listener; 17 18 return detector; 19 } 20 21 public abstract boolean onTouchEvent(MotionEvent ev); 22 23 public abstract boolean isScaling(); 24 25 public static interface OnGestureListener { 26 public void onDrag(float dx, float dy); 27 28 public void onFling(float startX, float startY, float velocityX, float velocityY); 29 30 public void onScale(float scaleFactor, float focusX, float focusY); 31 } 32 33 // <2.0 34 private static class CupcakeDetector extends VersionedGestureDetector { 35 36 float mLastTouchX; 37 float mLastTouchY; 38 final float mTouchSlop; 39 final float mMinimumVelocity; 40 41 public CupcakeDetector(Context context) { 42 final ViewConfiguration configuration = ViewConfiguration.get(context); 43 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); 44 mTouchSlop = configuration.getScaledTouchSlop(); 45 } 46 47 private VelocityTracker mVelocityTracker; 48 private boolean mIsDragging; 49 50 float getActiveX(MotionEvent ev) { 51 return ev.getX(); 52 } 53 54 float getActiveY(MotionEvent ev) { 55 return ev.getY(); 56 } 57 58 public boolean isScaling() { 59 return false; 60 } 61 62 @Override 63 public boolean onTouchEvent(MotionEvent ev) { 64 switch (ev.getAction()) { 65 case MotionEvent.ACTION_DOWN: { 66 mVelocityTracker = VelocityTracker.obtain(); 67 mVelocityTracker.addMovement(ev); 68 69 mLastTouchX = getActiveX(ev); 70 mLastTouchY = getActiveY(ev); 71 mIsDragging = false; 72 break; 73 } 74 case MotionEvent.ACTION_MOVE: { 75 final float x = getActiveX(ev); 76 final float y = getActiveY(ev); 77 final float dx = x - mLastTouchX, dy = y - mLastTouchY; 78 79 if (!mIsDragging) { 80 // Use Pythagoras to see if drag length is larger than 81 // touch slop 82 mIsDragging = FloatMath.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; 83 } 84 85 if (mIsDragging) { 86 mListener.onDrag(dx, dy); 87 mLastTouchX = x; 88 mLastTouchY = y; 89 90 if (null != mVelocityTracker) { 91 mVelocityTracker.addMovement(ev); 92 } 93 } 94 break; 95 } 96 97 case MotionEvent.ACTION_CANCEL: 98 case MotionEvent.ACTION_UP: { 99 if (mIsDragging) { 100 mIsDragging = false; 101 102 if (null != mVelocityTracker) { 103 mLastTouchX = getActiveX(ev); 104 mLastTouchY = getActiveY(ev); 105 106 // Compute velocity within the last 1000ms 107 mVelocityTracker.addMovement(ev); 108 mVelocityTracker.computeCurrentVelocity(1000); 109 110 final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker.getYVelocity(); 111 112 // If the velocity is greater than minVelocity, call 113 // listener 114 if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) { 115 mListener.onFling(mLastTouchX, mLastTouchY, -vX, -vY); 116 } 117 } 118 } 119 120 // Recycle Velocity Tracker 121 if (null != mVelocityTracker) { 122 mVelocityTracker.recycle(); 123 mVelocityTracker = null; 124 } 125 break; 126 } 127 } 128 return true; 129 } 130 } 131 132 // =2.0 =2.1 133 private static class EclairDetector extends CupcakeDetector { 134 private static final int INVALID_POINTER_ID = -1; 135 private int mActivePointerId = INVALID_POINTER_ID; 136 private int mActivePointerIndex = 0; 137 138 public EclairDetector(Context context) { 139 super(context); 140 } 141 142 @Override 143 float getActiveX(MotionEvent ev) { 144 try { 145 return ev.getX(mActivePointerIndex); 146 } catch (Exception e) { 147 return ev.getX(); 148 } 149 } 150 151 @Override 152 float getActiveY(MotionEvent ev) { 153 try { 154 return ev.getY(mActivePointerIndex); 155 } catch (Exception e) { 156 return ev.getY(); 157 } 158 } 159 160 @Override 161 public boolean onTouchEvent(MotionEvent ev) { 162 final int action = ev.getAction(); 163 switch (action & MotionEvent.ACTION_MASK) { 164 case MotionEvent.ACTION_DOWN: 165 mActivePointerId = ev.getPointerId(0); 166 break; 167 case MotionEvent.ACTION_CANCEL: 168 case MotionEvent.ACTION_UP: 169 mActivePointerId = INVALID_POINTER_ID; 170 break; 171 case MotionEvent.ACTION_POINTER_UP: 172 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 173 final int pointerId = ev.getPointerId(pointerIndex); 174 if (pointerId == mActivePointerId) { 175 // This was our active pointer going up. Choose a new 176 // active pointer and adjust accordingly. 177 final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 178 mActivePointerId = ev.getPointerId(newPointerIndex); 179 mLastTouchX = ev.getX(newPointerIndex); 180 mLastTouchY = ev.getY(newPointerIndex); 181 } 182 break; 183 } 184 185 mActivePointerIndex = ev.findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0); 186 return super.onTouchEvent(ev); 187 } 188 } 189 190 // >=2.2 add a ScaleGestureDetector 191 private static class FroyoDetector extends EclairDetector implements ScaleGestureDetector.OnScaleGestureListener { 192 private ScaleGestureDetector mDetector; 193 194 public FroyoDetector(Context context) { 195 super(context); 196 mDetector = new ScaleGestureDetector(context, this); 197 } 198 199 @Override 200 public boolean isScaling() { 201 return mDetector.isInProgress(); 202 } 203 204 @Override 205 public boolean onScale(ScaleGestureDetector detector) { 206 mListener.onScale(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY()); 207 return true; 208 } 209 210 @Override 211 public boolean onTouchEvent(MotionEvent ev) { 212 mDetector.onTouchEvent(ev); 213 return super.onTouchEvent(ev); 214 } 215 216 @Override 217 public boolean onScaleBegin(ScaleGestureDetector detector) { 218 return true; 219 } 220 221 @Override 222 public void onScaleEnd(ScaleGestureDetector detector) { 223 // NO-OP 224 } 225 } 226 }