阅读更多 常见的有两种
  •      一个控件横向滑动,另一个控件竖向滑动。比如:类似ViewPager,每个页面里面是ListView,不过,我们不用去处理滑动处理,ViewPager内部已经处理好了。
  •      一个控件竖向滑动,另一个控件也竖向滑动。比如:ScollView里面包裹着ListView,这也是需要着重理解掌握的。



 

Android内置了Scoller,用于实现渐进式的滑动。
  •  创建Scroller对象:Scroller mScroller = new Scroller(context);
  •  重写computeScroll()方法;
  •  最后,在我们的smoothScrollTo方法中调用startScroll方法;
Scroller mScroller = new Scroller(context);@Overridepublic void computeScroll() {    if (mScroller.computeScrollOffset()){        scrollTo(mScroller.getCurrX(),mScroller.getCurrY());        postInvalidate();    }}//这里smoothScrollTo实现的是x方向的平滑public void smoothScrollTo(int destX,int destY){    int scrollX=getScrollX();    int deltaX=destX-scrollX;    mScroller.startScroll(scrollX,0,deltaX,0,1000);//startScroll函数的形参分别表示:起始位置的x坐标、起始位置的y坐标、x方向要移动的距离、y方向上要移动的距离以及整个滑动过程完成所需的时间。    invalidate();}
  Scroller.computeScrollOffset():这个方法返回true,表示滑动还未结束,还要继续滑动,false表示滑动已结束。 scrollTo:注意,这里其实用的本质是scrollTo(...),所以要求View是有内容的View,平滑移动的也是其内容,而非View本身位置的改变。 postInvalidate():用在非UI线程,其内部最后也是调用invalidate()(同理,invalidate用在UI线程)。 startScroll:当我们构造一个Scroller对象并且调用它的startScroll方法时,Scroller内部其实什么都没做,它只是保存了我们传递的几个参数。 原理:      Scroller到底时如何让View渐进滑动的呢?答案就是startScroll方法下面的invalidate方法。invalidate方法会导致View重绘,在View的draw方法中又会去调用computeScroll方法,computeScroll方法在View中是一个空实现,因此需要我们自己去实现。我们代码中已经实现了computeScroll方法,所以正是如此,它才能动起来。 总结:      Scroller本身不能实现View的滑动,我们需要借助View的computeScroll方法,并让它不断的刷新重绘。   滑动冲突解决方式:
  •      外部拦截法
    •       通过onInterceptTouchEvent(MotionEvent event)
@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {  boolean intercepted = false;  int x = (int) event.getX();  int y = (int) event.getY();  switch (event.getAction()) {  case MotionEvent.ACTION_DOWN:        intercepted = false;        break;  case MotionEvent.ACTION_MOVE:  if (父容器需要当前点击事件) {     intercepted = true;  } else {     intercepted = false;  }  break;  case MotionEvent.ACTION_UP:  intercepted = false;  break;  default:        break;              }  mLastXIntercept = x;  mLastYIntercept = y;  return intercepted;}/*  注意:  1. ACTION_DOWN 一定返回false,不要拦截它,否则根据View事件分发机制,后续ACTION_MOVE与ACTION_UP事件都将默认交给父View去处理!  2. 原则上ACTION_UP 也要返回false,如果返回true,并且滑动事件交给子View处理,那么子View将接收不到ACTION_UP事件,子View的onClick事件也就无法触发。  而父View不一样,如果父View在ACTION_MOVE中开始拦截事件,那么后续ACTION_UP也将默认交给父View处理。  */
 
  •      内部拦截法
    •      通过dispatchTouchEvent(MotionEvent event)
    •      内部拦截法就是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器进行处理,这种方法和Android的事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起来较外部拦截法较复杂。
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {  int x= (int) ev.getX();  int y= (int) ev.getY();  switch (ev.getAction()){  case MotionEvent.ACTION_DOWN:        parent.requestDisallowInterceptTouchEvent(true);        break;  case MotionEvent.ACTION_MOVE:  int deltaX=x-mLastX;  int deltaY=y-mLastY;  if (父容器需要此类点击事件){  parent.requestDisallowInterceptTouchEvent(false);  }        break;  case MotionEvent.ACTION_UP:        break;  default:        break;  }  mLastX=x;  mLastY=y;  return super.dispatchTouchEvent(ev);}/*内部拦截法要求父View不能拦截ACTION_DOWN事件,由于ACTION_DOWN不受FLAG_DISALLOW_INTERCEPT标志位控制,一旦父容器拦截ACTION_DOWN那么所有的事件都不会传递给子View。*/ 
  ViewPager源码分析 借阅理解文章( http://blog.csdn.net/huachao1001/article/details/51654692) ViewPager的滑动冲突处理:      我们知道,ViewGroup是在onInterceptTouchEvent函数中决定是否拦截触摸事件,那么我们就去学习一下ViewPager的onInterceptTouchEvent函数。
@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {     //1. 触摸动作    final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;     //2. 时刻要注意触摸是否已经结束    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {        //3. Release the drag.        if (DEBUG) Log.v(TAG, "Intercept done!");        //4. 重置一些跟判断是否拦截触摸相关变量        resetTouch();        //5. 触摸结束,无需拦截        return false;    }     //6. 如果当前不是按下事件,我们就判断一下,是否是在拖拽切换页面    if (action != MotionEvent.ACTION_DOWN) {        //7. 如果当前是正在拽切换页面,直接拦截掉事件,后面无需再做拦截判断        if (mIsBeingDragged) {            if (DEBUG) Log.v(TAG, "Intercept returning true!");            return true;        }        //8. 如果标记为不允许拖拽切换页面,我们就"放过"一切触摸事件        if (mIsUnableToDrag) {            if (DEBUG) Log.v(TAG, "Intercept returning false!");            return false;        }    }    //9. 根据不同的动作进行处理    switch (action) {        //10. 如果是手指移动操作        case MotionEvent.ACTION_MOVE: {             //11. 代码能执行到这里,就说明mIsBeingDragged==false,否则的话,在第7个注释处就已经执行结束了             //12.使用触摸点Id,主要是为了处理多点触摸            final int activePointerId = mActivePointerId;            if (activePointerId == INVALID_POINTER) {                //13.如果当前的触摸点id不是一个有效的Id,无需再做处理                break;            }            //14.根据触摸点的id来区分不同的手指,我们只需关注一个手指就好            final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);            //15.根据这个手指的序号,来获取这个手指对应的x坐标            final float x = MotionEventCompat.getX(ev, pointerIndex);            //16.在x轴方向上移动的距离            final float dx = x - mLastMotionX;            //17.x轴方向的移动距离绝对值            final float xDiff = Math.abs(dx);            //18.同理,参照16、17条注释            final float y = MotionEventCompat.getY(ev, pointerIndex);            final float yDiff = Math.abs(y - mInitialMotionY);            if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);             //19.判断当前显示的页面是否可以滑动,如果可以滑动,则将该事件丢给当前显示的页面处理            //isGutterDrag是判断是否在两个页面之间的缝隙内移动            //canScroll是判断页面是否可以滑动            if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&                    canScroll(this, false, (int) dx, (int) x, (int) y)) {                mLastMotionX = x;                mLastMotionY = y;                //20.标记ViewPager不去拦截事件                mIsUnableToDrag = true;                return false;            }            //21.如果x移动距离大于最小距离,并且斜率小于0.5,表示在水平方向上的拖动            if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {                if (DEBUG) Log.v(TAG, "Starting drag!");                //22.水平方向的移动,需要ViewPager去拦截                mIsBeingDragged = true;                //23.如果ViewPager还有父View,则还要向父View申请将触摸事件传递给ViewPager                requestParentDisallowInterceptTouchEvent(true);                //24.设置滚动状态                setScrollState(SCROLL_STATE_DRAGGING);                //25.保存当前位置                mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :                        mInitialMotionX - mTouchSlop;                mLastMotionY = y;                //26.启用缓存                setScrollingCacheEnabled(true);            } else if (yDiff > mTouchSlop) {//27.否则的话,表示是竖直方向上的移动                if (DEBUG) Log.v(TAG, "Starting unable to drag!");                //28.竖直方向上的移动则不去拦截触摸事件                mIsUnableToDrag = true;            }            if (mIsBeingDragged) {                // 29.跟随手指一起滑动                if (performDrag(x)) {                    ViewCompat.postInvalidateOnAnimation(this);                }            }            break;        }        //30.如果手指是按下操作        case MotionEvent.ACTION_DOWN: {             //31.记录按下的点位置            mLastMotionX = mInitialMotionX = ev.getX();            mLastMotionY = mInitialMotionY = ev.getY();            //32.第一个ACTION_DOWN事件对应的手指序号为0            mActivePointerId = MotionEventCompat.getPointerId(ev, 0);            //33.重置允许拖拽切换页面            mIsUnableToDrag = false;            //34.标记开始滚动            mIsScrollStarted = true;            //35.手动调用计算滑动的偏移量            mScroller.computeScrollOffset();            //36.如果当前滚动状态为正在将页面放置到最终位置,            //且当前位置距离最终位置足够远            if (mScrollState == SCROLL_STATE_SETTLING &&                    Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {                //37. 如果此时用户手指按下,则立马暂停滑动                mScroller.abortAnimation();                mPopulatePending = false;                populate();                mIsBeingDragged = true;                //38.如果ViewPager还有父View,则还要向父View申请将触摸事件传递给ViewPager                requestParentDisallowInterceptTouchEvent(true);                //39.设置当前状态为正在拖拽                setScrollState(SCROLL_STATE_DRAGGING);            } else {                //40.结束滚动                completeScroll(false);                mIsBeingDragged = false;            }             if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY                    + " mIsBeingDragged=" + mIsBeingDragged                    + "mIsUnableToDrag=" + mIsUnableToDrag);            break;        }         case MotionEventCompat.ACTION_POINTER_UP:            onSecondaryPointerUp(ev);            break;    }     //41.添加速度追踪    if (mVelocityTracker == null) {        mVelocityTracker = VelocityTracker.obtain();    }    mVelocityTracker.addMovement(ev);      //42.只有在当前是拖拽切换页面时我们才会去拦截事件    return mIsBeingDragged;}
  我们看看ViewPager是如何决定是拦截还是不拦截,从源码上面看出,但斜率小于0.5时,则要拦截,否则不拦截,斜率是什么情况呢?高中数学可知,在第一象限中,越靠近y轴的直线,斜率越大,越靠近x轴直线斜率越小,先看简单图示: (何为斜率:斜率就是倾斜程度,斜率一般用k表示,斜率k值为直线与x轴正方向夹角的正切值,若直线上任意两点为(x1,y1)、(x2,y2)则直线斜率k=(y2-y1)/(x2-x1).直线平行于y轴,斜率不存在,平行于x轴,斜率为0)

 
  • 大小: 11.1 KB
  • 大小: 7.4 KB
  • 查看图片附件

更多相关文章

  1. Android(安卓)listview 加载更多定位问题
  2. Android(安卓)NDK开发的环境配置和使用
  3. Android复杂数据模型序列化
  4. Android(安卓)BaseAadapter 异步加载图片方法
  5. EventBus从入门到装逼,源码分析,手撸框架
  6. Visual Studio 跨平台开发实战(5) - Xamarin Android(安卓)多页
  7. Android笔记:Android(安卓)UI学习 - 对话框 (AlertDialog & Progr
  8. Adnroid——自定义控件(入门篇之自定义验证码)
  9. Android(安卓)UI开发第十八篇――ActivityGroup实现tab功能

随机推荐

  1. Android中的广播也定向
  2. 实现android启动界面字体的动画效果
  3. 初窥图像处理利器RenderScript
  4. Android如何代码混淆
  5. Android(安卓)HttpClient GET或者POST请
  6. Android之系统自带的文字外观设置及实际
  7. Android 办公自动化(Office Automation)
  8. android跨进程通信(IPC):使用AIDL
  9. android的第一天学习
  10. Android播放视频的方式