Android自定义控件——手把手教你实现SlidingMenu(二)

上篇回顾:上篇中创建了项目的基本组成,并且成功的把Activity的contentView  set  给了我们自定义的RelativeLayout中的SlidingMenuAbove

系列二

      本片介绍如何实现基本的滑动动作

  1. 手指在SlidingMenuAbove上左右滑动的时候SlidingMenuAbove在一定的情况下跟随手指滑动
  2. 松开手指后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;
	}
}

效果
Android自定义控件——手把手教你实现SlidingMenu(二)_第1张图片

2.可见目前的效果是手指拖动的时候上层的View已经跟着滚动了,但是松开手指后View就停在那儿不动了,所以我们在onTouchEvent中还需要继续处理。

你可能感兴趣的:(android,自定义view)