手势滑动结束 Activity(一)基本功能的实现

喜欢听音乐的朋友可能都看过天天动听这款 app, 这款 app 有一个亮点就是在切换页面(Fragment)的时候可以通过手势滑动来结束当前页面,这里先说一下,我为什么会这么关心这个功能呢,因为前两天 PM说我们即将开始做的这款app 也要实现页面能通过手势滑动来结束的功能,所以我就拿着这款 app 滑了一上午;但是我要实现的跟天天动听这款 app又有点不同,细心观察的朋友可能会发现,天天动听是 Fragment 之间的切换,而我这里要实现的是 Activity 之间的切换,不过,不管是哪种,最终效果都是一样,就是页面能随着手势的滑动而滑动,最终达到某个特定条件,结束此页面。 要实现这个功能其实也不是特别难,这里我把这个功能的实现分为了以下两个步骤: 1、识别手势滑动自定义ViewGroup 的实现 2、实现自定义 ViewGroup 和 Activity 绑定

根据以上两个步骤,我们发现,这其中涉及到的知识点有:Android 事件处理机制、自定义 View(ViewGroup)的实现,Activity Window的知识,在开发的过程中还涉及到Activity 主题的配置。Android 事件处理和自定义 View 都在我前面的 blog 中有讲到,如果不了解的朋友可以去看看。下面开始按步骤来实现功能

一、自定义 ViewGroup 这个 ViewGroup 的功能只要是对事件的拦截,能够实现手势滑动效果;显示 Activity 的内容包括 ActionBar 和内容区。 1、实现测量和布局

(int widthMeasureSpec, int heightMeasureSpec) {/*获取默认的宽度*/int width = getDefaultSize(0, widthMeasureSpec);/*获取默认的高度*/int height = getDefaultSize(0, heightMeasureSpec);/*设置ViewGroup 的宽高*/setMeasuredDimension(width, height);contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);/*设置子View 的大小*/mContent.measure(contentWidth, contentHeight);} (boolean changed, int l, int t, int r, int b) {final int width = r – l;final int height = b – t;mContent.layout(0, 0, width, height);}

因为每个 Activity 都只有一个 Layout,所以这里只有一个子 View,布局和测量就显得非常简单。

2、事件拦截

@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {if (!isEnable) {return false;}final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP|| action != MotionEvent.ACTION_DOWN && mIsUnableToDrag) {/*结束手势的滑动,不拦截*/endToDrag();return false;}switch (action) {case MotionEvent.ACTION_DOWN:= MotionEventCompat.getActionIndex(ev);mActivePointerId = MotionEventCompat.getPointerId(ev, index);if (mActivePointerId == INVALID_POINTER)break;mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);mLastMotionY = MotionEventCompat.getY(ev, index);/*这里判读,如果这个触摸区域是允许滑动拦截的,则拦截事件*/if (thisTouchAllowed(ev)) {mIsBeingDragged = false;mIsUnableToDrag = false;} else {mIsUnableToDrag = true;}break;case MotionEvent.ACTION_MOVE:/*继续判断是否需要拦截*/determineDrag(ev);break;case MotionEvent.ACTION_UP:break;case MotionEvent.ACTION_POINTER_UP:/*这里做了对多点触摸的处理,当有多个手指触摸的时候依然能正确的滑动*/onSecondaryPointerUp(ev);break;}if (!mIsBeingDragged) {if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(ev);}return mIsBeingDragged;}

事件拦截,是拦截而是其不会向子 View 分发,直接执行本级 View的 onTouchEvent方法;

3、事件处理

@Overridepublic boolean onTouchEvent(MotionEvent event) {if (!isEnable) {return false;}if (!mIsBeingDragged && !thisTouchAllowed(event))return false;final int action = event.getAction();if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(event);switch (action & MotionEventCompat.ACTION_MASK) {case MotionEvent.ACTION_DOWN:/*按下则结束滚动*/completeScroll();int index = MotionEventCompat.getActionIndex(event);mActivePointerId = MotionEventCompat.getPointerId(event, index);mLastMotionX = mInitialMotionX = event.getX();break;case MotionEventCompat.ACTION_POINTER_DOWN: {/*有多个点按下的时候,取最后一个按下的点为有效点*/final int indexx = MotionEventCompat.getActionIndex(event);mLastMotionX = MotionEventCompat.getX(event, indexx);mActivePointerId = MotionEventCompat.getPointerId(event, indexx);break;}case MotionEvent.ACTION_MOVE:if (!mIsBeingDragged) {determineDrag(event);if (mIsUnableToDrag)return false;}/*如果已经是滑动状态,则根据手势滑动,而改变View 的位置*/if (mIsBeingDragged) {// 以下代码用来判断和执行View 的滑动final int activePointerIndex = getPointerIndex(event, mActivePointerId);if (mActivePointerId == INVALID_POINTER)break;final float x = MotionEventCompat.getX(event, activePointerIndex);final float deltaX = mLastMotionX – x;mLastMotionX = x;float oldScrollX = getScrollX();float scrollX = oldScrollX + deltaX;final float leftBound = getLeftBound();final float rightBound = getRightBound();if (scrollX < leftBound) {scrollX = leftBound;} else if (scrollX > rightBound) {scrollX = rightBound;}mLastMotionX += scrollX – (int) scrollX;scrollTo((int) scrollX, getScrollY());}break;case MotionEvent.ACTION_UP:/*如果已经是滑动状态,抬起手指,需要判断滚动的位置*/if (mIsBeingDragged) {final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(1000, mMaxMunVelocity);int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(velocityTracker, mActivePointerId);final int scrollX = getScrollX();final float pageOffset = (float) (-scrollX) / getContentWidth();final int activePointerIndex = getPointerIndex(event, mActivePointerId);if (mActivePointerId != INVALID_POINTER) {final float x = MotionEventCompat.getX(event, activePointerIndex);final int totalDelta = (int) (x – mInitialMotionX);/*这里判断是否滚动到下一页,还是滚回原位置*/int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);setCurrentItemInternal(nextPage, true, initialVelocity);} else {setCurrentItemInternal(mCurItem, true, initialVelocity);}mActivePointerId = INVALID_POINTER;endToDrag();} else {//setCurrentItemInternal(0, true, 0);endToDrag();}break;case MotionEventCompat.ACTION_POINTER_UP:/*这里有事多点处理*/onSecondaryPointerUp(event);int pointerIndex = getPointerIndex(event, mActivePointerId);if (mActivePointerId == INVALID_POINTER)break;mLastMotionX = MotionEventCompat.getX(event, pointerIndex);break;}return true;}要温暖还是怕麻烦。

手势滑动结束 Activity(一)基本功能的实现

相关文章:

你感兴趣的文章:

标签云: