上篇回顧:上篇中創建了項目的基本組成,並且成功的把Activity的contentView set 給了我們自定義的RelativeLayout中的SlidingMenuAbove
系列二
本片介紹如何實現基本的滑動動作
- 手指在SlidingMenuAbove上左右滑動的時候SlidingMenuAbove在一定的情況下跟隨手指滑動
- 鬆開手指後Sliding完成餘下的動作,比如SldingMenuAbove滑動到初始位置/滑動到最終位置
- 判定手指的滑動是否爲拖動動作,具體就是判斷X方向上的持續變化量>touchSlop(ViewCofigration中的常量,默認爲64,表示爲拖動動作的最小變化量),同時X>Y方向上的持續變化量。此時認爲是一個左右滑動動作而非上下滑動。然後將事件交給onTouchEvent處理
- 不滿足上一條時把此事件攔截,滑動動作就不執行
/**
* 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;
}
}