《Android 开发艺术探索》——View 事件体系

1,701 阅读3分钟
原文链接: www.jianshu.com

自定义控件、滑动冲突解决

View基础知识

  1. View的位置参数
  2. MotionEvent和TouchSlop对象
  3. VelocityTracker
  4. GestureDetector和Scroller对象

1. View的位置参数


图片来源

2. MotionEvent和TouchSlop

注意:各个方法相对目标不一样。
view获取自身坐标:getLeft(),getTop(),getRight(),getBottom()
view获取自身宽高:getHeight(),getWidth()
motionEvent获取坐标:getX(),getY(),getRawX(),getRawY()

  • MotionEvent
    Touch事件中,典型的事件有如下几种:
    • ACTION_DOWN —— 手指接触屏幕
    • ACTION_MOVE —— 手指在屏幕上移动
    • ACTION_UP —— 手指从屏幕离开
  • TouchSlop
    TouchSlop是系统所能识别出的被认为是滑动的最小距离,换句话说,当手指在屏幕上滑动时,当两次滑动之间的距离小于这个常量,那么系统不认为是在进行滑动操作。通过以下方式获取这个常量值:
    ViewConfiguration.get(getContext()).getScaledTouchSlop();
    这个常量有神马意义呢?
    可以使用这个常量判断是否达到滑动条件,处理滑动时,做一些过滤。

3. VelocityTracker、GestureDetector和Scroller对象

  • VelocityTracker
    速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度。
    构造方法中初始化获取VelocityTracker对象
    VelocityTracker mVelocityTracker = VelocityTracker.obtain();
    onTouchEvent方法中添加追踪事件
    mVelocityTracker.addMovement(event);
    ACTION_UP事件中获取当前的速度。注意这里计算的是1000ms时间间隔移动的像素值,假设像素是100,即速度是每秒100像素。手指从右向左滑动,速度为负值。
    mVelocityTracker.computeCurrentVelocity(1000);
    float xVelocity = mVelocityTracker.getXVelocity();
    float yVelocity = mVelocityTracker.getYVelocity();
    最后,当不需要它的时候需要调用clear方法来重置并回收内存。
    mVelocityTracker.clear();
    mVelocityTracker.recycle();
  • GestureDetecor
    手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。

    创建一个GestureDetecor对象并实现OnGestureListener接口,根据需要实现单击等方法:

    GestureDetector mGestureDetector = new GestureDetector(this);
    // 解决长按屏幕后无法拖动的现象
    mGestureDetector.setIsLongpressEnabled(false);

    接管目标ViewonTouchEvent方法,在待监听ViewonTouchEvent方法中添加如下实现:

    boolean consume = mGestureDetector.onTouchEvent(event);
    return consume;

    建议
    如果只是监听滑动操作,建议在onTouchEvent中实现;如果要监听双击这种行为,则使用GestureDetector 。

  • Scroller
    弹性滑动对象,用于实现View的弹性滑动。
    View的scrollTo/scrollBy方法来滑动时,过程是瞬间完成的。使用Scroller则有过渡滑动的效果。注意,Scoller本身无法让View弹性滑动,它需要和View的computerScroller方法配合使用。
    构造方法初始化
    Scroller mScroller = new Scroller(getContext());
    缓慢滑动到指定位置,一般在ACTION_UP 方法中执行,松手回弹效果。
    private void smoothScrollBy(int dx, int dy) {
      mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
      invalidate();
    }
    @Override
    public void computeScroll() {
      if (mScroller.computeScrollOffset()) {
          scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
          postInvalidate();
      }
    }
    Scroller原理

    原理图

    当在MotionEvent.ACTION_UP事件触发时,调用startScroll方法,并调用invalidate/postInvalidate方法,会导致View重绘,执行View.draw方法。在此方法中会调用View.computeScroll方法,此方法是空实现,需要我们自己处理逻辑。具体逻辑是:先判断computeScrollOffset,如果为true,表示滚动未结束。则执行scrollTo方法,再次调用postInvalidate,如此反复执行,直到结束。

computeScrollOffset方法计算了一小段时间间隔内偏移的距离,即CurrX,CurrY。并返回是否滚动结束的标记。true表示未结束,false表示结束。

View的scrollTo/scrollBy方法操作的View的内容滑动。
getScrollX返回的是View的左边缘到其内容左边缘的距离。相对于View的左边缘
getScrollY返回的是View的上边缘到其内容上边缘的距离。

如果View的内容向左滑,滑出View的左边界,getScrollX为正值,反之为负值。
如果View的内容向上滑,滑出View的上边界,getScrollY为正值,反之为负值。


getScrollX和getScrollY的变化示意图

参考链接:
Android Scroller完全解析,关于Scroller你所需知道的一切 - 郭霖