Android VersionedGestureDetector手势事件

时间:2023-03-09 09:56:07
Android VersionedGestureDetector手势事件

今天研究了一下PhotoView,发现里面的自定义的手势事件可以支持所有的SDK版本,该事件可以实现拖拽、滑动、缩放功能。下面直接上代码:

 public abstract class VersionedGestureDetector {
static final String LOG_TAG = "VersionedGestureDetector";
OnGestureListener mListener; public static VersionedGestureDetector newInstance(Context context, OnGestureListener listener) {
final int sdkVersion = Build.VERSION.SDK_INT;
VersionedGestureDetector detector = null;
if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
detector = new CupcakeDetector(context);
} else if (sdkVersion < Build.VERSION_CODES.FROYO) {
detector = new EclairDetector(context);
} else {
detector = new FroyoDetector(context);
} detector.mListener = listener; return detector;
} public abstract boolean onTouchEvent(MotionEvent ev); public abstract boolean isScaling(); public static interface OnGestureListener {
public void onDrag(float dx, float dy); public void onFling(float startX, float startY, float velocityX, float velocityY); public void onScale(float scaleFactor, float focusX, float focusY);
} // <2.0
private static class CupcakeDetector extends VersionedGestureDetector { float mLastTouchX;
float mLastTouchY;
final float mTouchSlop;
final float mMinimumVelocity; public CupcakeDetector(Context context) {
final ViewConfiguration configuration = ViewConfiguration.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mTouchSlop = configuration.getScaledTouchSlop();
} private VelocityTracker mVelocityTracker;
private boolean mIsDragging; float getActiveX(MotionEvent ev) {
return ev.getX();
} float getActiveY(MotionEvent ev) {
return ev.getY();
} public boolean isScaling() {
return false;
} @Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev); mLastTouchX = getActiveX(ev);
mLastTouchY = getActiveY(ev);
mIsDragging = false;
break;
}
case MotionEvent.ACTION_MOVE: {
final float x = getActiveX(ev);
final float y = getActiveY(ev);
final float dx = x - mLastTouchX, dy = y - mLastTouchY; if (!mIsDragging) {
// Use Pythagoras to see if drag length is larger than
// touch slop
mIsDragging = FloatMath.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
} if (mIsDragging) {
mListener.onDrag(dx, dy);
mLastTouchX = x;
mLastTouchY = y; if (null != mVelocityTracker) {
mVelocityTracker.addMovement(ev);
}
}
break;
} case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if (mIsDragging) {
mIsDragging = false; if (null != mVelocityTracker) {
mLastTouchX = getActiveX(ev);
mLastTouchY = getActiveY(ev); // Compute velocity within the last 1000ms
mVelocityTracker.addMovement(ev);
mVelocityTracker.computeCurrentVelocity(1000); final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker.getYVelocity(); // If the velocity is greater than minVelocity, call
// listener
if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
mListener.onFling(mLastTouchX, mLastTouchY, -vX, -vY);
}
}
} // Recycle Velocity Tracker
if (null != mVelocityTracker) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
}
return true;
}
} // =2.0 =2.1
private static class EclairDetector extends CupcakeDetector {
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private int mActivePointerIndex = 0; public EclairDetector(Context context) {
super(context);
} @Override
float getActiveX(MotionEvent ev) {
try {
return ev.getX(mActivePointerIndex);
} catch (Exception e) {
return ev.getX();
}
} @Override
float getActiveY(MotionEvent ev) {
try {
return ev.getY(mActivePointerIndex);
} catch (Exception e) {
return ev.getY();
}
} @Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
}
break;
} mActivePointerIndex = ev.findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0);
return super.onTouchEvent(ev);
}
} // >=2.2 add a ScaleGestureDetector
private static class FroyoDetector extends EclairDetector implements ScaleGestureDetector.OnScaleGestureListener {
private ScaleGestureDetector mDetector; public FroyoDetector(Context context) {
super(context);
mDetector = new ScaleGestureDetector(context, this);
} @Override
public boolean isScaling() {
return mDetector.isInProgress();
} @Override
public boolean onScale(ScaleGestureDetector detector) {
mListener.onScale(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY());
return true;
} @Override
public boolean onTouchEvent(MotionEvent ev) {
mDetector.onTouchEvent(ev);
return super.onTouchEvent(ev);
} @Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
} @Override
public void onScaleEnd(ScaleGestureDetector detector) {
// NO-OP
}
}
}