上篇回顾:上篇中创建了项目的基本组成,并且成功的把Activity的contentView set 给了我们自定义的RelativeLayout中的SlidingMenuAbove
系列二
本片介绍如何实现基本的滑动动作
- 手指在SlidingMenuAbove上左右滑动的时候SlidingMenuAbove在一定的情况下跟随手指滑动
- 松开手指后Sliding完成余下的动作,比如SldingMenuAbove滑动到初始位置/滑动到最终位置
1.此时我们就已经要开始考虑事件分发的情况了
- 判定手指的滑动是否为拖动动作,具体就是判断X方向上的持续变化量>touchSlop(ViewCofigration中的常量,默认为64,表示为拖动动作的最小变化量),同时X>Y方向上的持续变化量。此时认为是一个左右滑动动作而非上下滑动。然后将事件交给onTouchEvent处理
- 不满足上一条时把此事件拦截,滑动动作就不执行
SlidingMenuAbove.java 中的onInterceptTouchEvent中处理 事件交由onTouchEvent的Move中做具体的跟随手指滚动,每次滚动的距离为上一次滚动到的位置加上本次X的变化量,getScrollX()+deltaX
/**
* 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;
}
}
效果
2.可见目前的效果是手指拖动的时候上层的View已经跟着滚动了,但是松开手指后View就停在那儿不动了,所以我们在onTouchEvent中还需要继续处理。