上篇回顾:上篇中创建了项目的基本组成,并且成功的把Activity的contentView set 给了我们自定义的RelativeLayout中的SlidingMenuAbove
系列二
本片介绍如何实现基本的滑动动作
/** * SlidingMenu的上层用来装Activity的ContentView的视图的容器 * * @author mingwei * */ public class SlidingMenuAbove extends ViewGroup { private final String TAG = SlidingMenuAbove.class.getSimpleName(); private boolean DEBUG = false; private Scroller mScroller; private int mTouchSlop; private VelocityTracker mTracker; private int mMinimumVelocity; private int mMaximumVelocity; private int mFlingDistance; private boolean mEnabled = true; /** * 是否允许被拖拽,在determineDrag中对触摸事件进行计算处理然后决定是否被拖拽 */ private boolean mIsBeingDraged; private boolean mIsUnableToDrag; private boolean mQuickReturn; private float mLastMotionX; private float mLastMotionY; private float mInitialMotionX; private int mCurItem; private float mScrollX = 0.0f; protected int mActivePointerID = INVALID_POINTER; private static final int INVALID_POINTER = -1; private final static int MAX_SETTLE_DURATION = 600; private final static int MIN_DISTANCE_FOR_FLING = 25; private final static Interpolator S_INTERPOLATOR = new Interpolator() { @Override public float getInterpolation(float t) { t -= 1.0f; return t * t * t * t * t + 1.0f; } }; private View mContentView; public SlidingMenuAbove(Context context) { this(context, null); } public SlidingMenuAbove(Context context, AttributeSet attrs) { this(context, attrs, 0); } @SuppressLint("ResourceAsColor") public SlidingMenuAbove(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAboveView(); } private void initAboveView() { setWillNotDraw(false); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setFocusable(true); Context context = getContext(); mScroller = new Scroller(context, S_INTERPOLATOR); ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); /** * 密度,和判定为Fling动作的最小距离 */ final float density = context.getResources().getDisplayMetrics().density; mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density); } @Override protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) { mContentView.layout(arg1, arg2, arg3, arg4); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getDefaultSize(0, widthMeasureSpec); int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height); int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width); int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height); mContentView.measure(contentWidth, contentHeight); } public void setContent(View content) { if (mContentView != null) { this.removeView(mContentView); } mContentView = content; addView(mContentView); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // Log.i("Gmw", "intercept_touch"); final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; /** * 动作判定,是否对touch事件进行拦截 */ switch (action) { case MotionEvent.ACTION_MOVE: // Log.i("Gmw", "intercept_touch_move"); // determineDrag(ev); break; case MotionEvent.ACTION_DOWN: // Log.i("Gmw", "intercept_touch_down"); int index = MotionEventCompat.getActionIndex(ev); mActivePointerID = MotionEventCompat.getPointerId(ev, index); if (mActivePointerID == INVALID_POINTER) { break; } mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index); mLastMotionY = MotionEventCompat.getY(ev, index); mIsBeingDraged = false; mIsUnableToDrag = false; break; case MotionEventCompat.ACTION_POINTER_UP: // Log.i("Gmw", "intercept_touch_up"); break; default: break; } if (!mIsBeingDraged) { if (mTracker == null) { mTracker = VelocityTracker.obtain(); mTracker.addMovement(ev); } } Log.i("Gmw", "intercept_touch_result=" + mIsBeingDraged); return mIsBeingDraged; } @Override public boolean onTouchEvent(MotionEvent ev) { // Log.i("Gmw", "touch_touch"); final int action = ev.getAction(); if (mTracker == null) { mTracker = VelocityTracker.obtain(); } mTracker.addMovement(ev); switch (action & MotionEventCompat.ACTION_MASK) { case MotionEvent.ACTION_DOWN: // Log.i("Gmw", "touch_down"); int index = MotionEventCompat.getActionIndex(ev); mActivePointerID = MotionEventCompat.getPointerId(ev, index); mLastMotionX = mInitialMotionX = ev.getX(); break; case MotionEvent.ACTION_MOVE: // Log.i("Gmw", "touch_move"); if (!mIsBeingDraged) { determineDrag(ev); } if (mIsBeingDraged) { final int activePoiterIndex = getPointerIndex(ev, mActivePointerID); if (activePoiterIndex == INVALID_POINTER) { break; } float x = MotionEventCompat.getX(ev, activePoiterIndex); float deltaX = mLastMotionX - x; // Log.i("Gmw", "detalX=" + detalX + "______" + mLastMotionX + // "-" + x); mLastMotionX = x; float oldScrollX = getScrollX(); float scrollX = oldScrollX + deltaX; Log.i("Gmw", "scrollX_________" + scrollX + "______oldScrollX____" + oldScrollX + "_______" + deltaX); float leftLimit = 0; float rightLimit = -300; if (scrollX > leftLimit) { scrollX = leftLimit; } if (scrollX < rightLimit) { scrollX = rightLimit; } mLastMotionX += scrollX - (int) scrollX; scrollTo((int) scrollX, getScrollY()); } break; case MotionEvent.ACTION_UP: // Log.i("Gmw", "touch_up"); break; case MotionEvent.ACTION_CANCEL: // Log.i("Gmw", "touch_cancel"); break; case MotionEventCompat.ACTION_POINTER_DOWN: // Log.i("Gmw", "touch_pointer_down"); break; case MotionEventCompat.ACTION_POINTER_UP: // Log.i("Gmw", "touch_pointer_up"); break; default: break; } return true; } /** * 根据手指在屏幕上的滑动判定contentView上的滑动姿势 * * <li>是竖着滑动还是横着滑动</li> <li>滑动的时候X方向上的变化大于touchslop ( * {@link ViewConfigurationCompat#getScaledPagingTouchSlop}) * 并且X方向上的持续变化量大于Y方向上的持续变化量时就判定为滑动菜单的拖动动作</li> * * @param event * MotionEvent */ private void determineDrag(MotionEvent event) { final int activePointerID = mActivePointerID; final int pointerIndex = getPointerIndex(event, activePointerID); if (activePointerID == INVALID_POINTER || pointerIndex == INVALID_POINTER) { return; } float x = MotionEventCompat.getX(event, pointerIndex); float dx = x - mLastMotionX; float xDiff = Math.abs(dx); // Log.i("Gmw", "xDiff=" + xDiff); float y = MotionEventCompat.getY(event, pointerIndex); float dy = y - mLastMotionY; float yDiff = Math.abs(dy); // Log.i("Gmw", "yDiff=" + yDiff); if (xDiff > mTouchSlop && xDiff > yDiff) { // Log.i("Gmw", "drag______________________"); startDrag(); mLastMotionX = x; mLastMotionY = y; } else if (xDiff > mTouchSlop) { mIsUnableToDrag = true; } } /** * 使用了V4包中的MotionEventCompat来辅助处理MotionEvent事件 * * @param event * @param id * @return */ private int getPointerIndex(MotionEvent event, int id) { int activePointerIndex = MotionEventCompat.findPointerIndex(event, id); if (activePointerIndex == -1) { activePointerIndex = INVALID_POINTER; } return activePointerIndex; } /** * 拖拽开始 */ private void startDrag() { mIsBeingDraged = true; mQuickReturn = false; } /** * 拖拽结束 */ private void endDrag() { if (mTracker != null) { mTracker.recycle(); mTracker = null; } } /** * 重写scrollTo */ @Override public void scrollTo(int x, int y) { super.scrollTo(x, y); mScrollX = x; } public int determineTargetPage(float offset, int velocity, int deltax) { return 0; } public int getmCurItem() { return mCurItem; } public void setmCurItem(int mCurItem) { this.mCurItem = mCurItem; } /** * 判断LeftMenu或者RightMenu是否打开 * * @return 菜单开返回true,关返回flase */ public boolean isMenuOpen() { return mCurItem == 0 || mCurItem == 2; } /** * 左滑动 * * @return 允许左滑并且滑动成功 */ public boolean pageLeft() { if (mCurItem > 0) { // setmCurItem(mCurItem-1); return true; } return false; } /** * 右滑动 * * @return 允许并且右滑动成功 */ public boolean pageRight() { if (mCurItem < 1) { // do something return true; } return false; } }