0%

View 的事件体系(一):View 基础知识

1. 什么是 View

  • View 是 Android 中所有控件的基类,不管是简单的 Button 和 TextView 还是复杂的 RelativeLayout 和 ListView,它们的共同基类都是 View。即,View 是一种界面层的控件的一种抽象,它代表了一个控件
  • 除了 View,还有 ViewGroup,即控件组,ViewGroup 内部包含了许多个控件,即一组 View。View 通常绘制用户可查看并进行交互的内容ViewGroup 是不可见容器用于定义 View 和其他 ViewGroup 对象的布局结构
  • 在 Android 的设计中,ViewGroup 也继承了 View,这意味着 View 本身就可以是单个控件也可以是由多个控件组成的一组控件,通过这种关系就形成了 View 树的结构,这和 Web 前端中的 DOM 树的概念是类似的

2. View 的位置参数

  • View 的位置主要由它的四个顶点来决定,分别对应 View 的四个属性:left、top、right、bottom

    • left: 左上角横坐标;top: 左上角纵坐标;right: 右下角横坐标;bottom: 右下角纵坐标
    • 需要注意的是,这些坐标都是相对于 View 的直接父容器,因此是一种相对坐标
    • 在 Android 中,x 轴和 y 轴的正方向分别为右和下。不仅是 Android,大部分显示系统都是按照这个标准来定义坐标系的
  • View 的宽高和坐标的关系

    • width = getWidth() = mRight - mLeft = getRight() - getLeft()
    • height = getHeight() = mBottom - mTop = getBottom() - getTop()
  • View 的位置坐标和直接父容器的关系

    View 的位置坐标和直接父容器的关系

  • Android 3.0 开始,View 新增加了几个参数:xytranslationXtranslationY

    • x 和 y 是 View 左上角的坐标,translationX 和 translationY 是 View 左上角相对于父容器的偏移量
    • 这 4 个参数也是相对于直接父容器的相对坐标,并且 translationX 和 translationY 的默认值是 0
    • 和 View 的四个基本的位置参数一样,View 也为这几个新增的参数提供了 get/set 方法,这些参数的换算关系为:x = left + translationXy = top + translationY
  • 需要注意的是,View 在平移的过程中

    • top 和 left 表示的是原始左上角的位置信息,其值并不会发生改变
    • 此时发生改变的是 x、y、translationX、translationY 这四个新增的参数

3. MotionEvent 和 TouchSlop

  1. MotionEvent

    • 典型的屏幕触摸事件类型

      • ACTION_DOWN: 手指刚接触屏幕
      • ACTION_MOVE: 手指在屏幕上移动
      • ACTION_UP: 手指从屏幕上松开的一瞬间
    • 正常情况下,一次手指触摸屏幕的行为会触发一系列的点击事件

      • 点击屏幕后离开松开,事件序列为:DOWN -> UP
      • 点击屏幕滑动一会再松开,事件序列为:DOWN -> MOVE -> … -> MOVE -> UP
    • 通过 MotionEvent 对象可以得到点击事件发生的 x 和 y 坐标,系统提供了两组方法:getX()/getY()getRawX()/getRawY()

      • getX()/getY(): 返回的是相对于当前 View 左上角的 x 和 y 坐标
      • getRawX()/getRawY(): 返回的是相对于手机屏幕左上角的 x 和 y 坐标
  2. TouchSlop

    • TouchSlop 是系统所能识别出的被认为是滑动的最小距离

    • 这是一个常量和设备有关,在不同设备上这个值可能是不同的

    • 获取这个常量的方法:ViewConfiguration.get(getContext()).getScaledTouchSlop()

    • 源码中这个常量定义在文件 frameworks/base/core/res/res/values/config.xml 中:

      1
      2
      <!-- Base "touch slop" value used by ViewConfiguration as a movement threshold where scrolling should begin. -->
      <dimen name="config_viewConfigurationTouchSlop">8dp</dimen>

4. VelocityTracker、GestureDetector 和 Scroller

  1. VelocityTracker

    • VelocityTracker 即为速度追踪,用于追踪手指在滑动过程中的速度,包括水平速度垂直速度,坐标轴正方向即为正值、反方向即为负值

    • VelocityTracker 的用法

      1. 首先,在 View 的 onTouchEvent() 方法中追踪当前单击事件的速度

        1
        2
        VelocityTracker velocityTracker = VelocityTracker.obtain();
        velocityTracker.addMovement(event);
      2. 然后,获取当前滑动速度:

        1
        2
        3
        velocityTracker.computeCurrentVelocity(1000);
        int xVelocity = (int) velocityTracker.getXVelocity();
        int yVelocity = (int) velocityTracker.getYVelocity();
        • 获取速度之前必须先计算速度,即 getXVelocity()getYVelocity() 这两个方法的前面必须要调用 computeCurrentVelocity() 方法
        • 这里的速度是指一段时间内手指所滑过的像素数,方法 computeCurrentVelocity() 的参数表示的就是这个“一段时间”,单位是毫秒
      3. 最后,当不需要使用它的时候,需要调用 clear() 方法来重置并回收内存

        1
        2
        velocityTracker.clear();
        velocityTracker.recycle();
  2. GestureDetector

    • GestureDetector 即为手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为

    • GestureDetector 的用法

      1. 首先,需要创建一个 GestureDetector 对象并实现 OnGestureListener 接口,根据需要还可以实现 OnDoubleTapListener 接口(监听双击行为):

        1
        2
        3
        GestureDetector mGestrueDetector = new GestureDetector(this);
        // 解决长按屏幕后无法拖动的现象
        mGestureDetector.setIsLongpressEnabled(false);
      2. 然后,接管目标 View 的 onTouchEvent() 方法,在待监听的 onTouchEvent() 方法中添加如下实现:

        1
        2
        boolean consume = mGestureDetector.onTouchEvent(event);
        return consume;
      3. 最后,可以有选择地实现 OnGestureListenerOnDoubleTapListener 中的方法

        方法名 描述 所属接口
        onDown() 手指轻轻触摸屏幕的一瞬间,由 1 个 ACTION_DOWN 触发 OnGestureListener
        onShowPress() 手指轻轻触摸屏幕,尚未松开或拖动,由 1 个 ACTION_DOWN 触发。注意和 onDown() 的区别,它强调的是没有松开或者拖动的状态 OnGestureListener
        onSingleTapUp() 手指(轻轻触摸屏幕后)松开,伴随着 1 个 ACTION_UP 触发,即单击行为 OnGestureListener
        onScroll() 手指按下屏幕并拖动,由 1 个 ACTION_DOWN,多个 ACTION_MOVE 触发,即拖动行为 OnGestureListene
        onLongPress() 用户长久地按着屏幕不放,即长按行为 OnGestureListener
        onFling() 用户按下屏幕、快速滑动后松开,由 1 个 ACTION_DOWN、多个 ACTION_MOVE 和 1 个 ACTION_UP 触发,即快速滑动行为 OnGestureListener
        onDoubleTap() 双击行为,由 2 次连续的单击组成,它不可能和 onSingleTapConfirmed() 共存 OnDoubleTapListener
        onSingleTapConfirmed() 严格的单击行为,注意它和 onSingleTapUp() 的区别,如果触发了 onSingleTapConfirmed(),那么后面不可能再紧跟着另一个单击行为,即这只可能是单击行为,而不可能是双击中的一次单击(奇怪为什么所属 OnDoubleTapListener 这个接口,有点矛盾) OnDoubleTapListener
        onDoubleTapEvent() 表示发生了双击行为,在双击的期间,ACTION_DOWN、ACTION_MOVE 和 ACTION_UP 都会触发此回调 OnDoubleTapListener
      4. 注意

        • 说明:实际开发中可以不使用 GestureDetector,完全可以在 View 的 onTouchEvent() 方法中实现所需的监听
        • 建议:如果只是监听滑动相关的,可以在 onTouchEvent() 中实现;如果要监听双击行为,可以使用 GestureDetector
  3. Scroller

    • Scroller 即为弹性滑动对象,用于实现 View 的弹性滑动

    • Scroller 的作用

      • 当使用 View 的 scrollTo()/scrollBy() 方法来进行滑动时,其过程是瞬间完成的,这个没有过渡效果的滑动体验并不是很好
      • 此时可以使用 Scroller 来实现有过渡效果的滑动,其过程不是瞬间完成的,而是在一定的时间间隔内完成的滑动行为
      • Scroller 本身无法让 View 弹性滑动,它需要和 View 的 computeScroll() 方法配合使用才能共同完成这个功能
    • 使用 Scroller 的典型代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      Scroller scroller = new Scroller(mContext);

      // 缓慢滚动到指定位置
      private void smoothScrollTo(int destX, int destY) {
      int scrollX = getScrollX();
      int delta = destX - scroollX;
      // 1000 ms 内滑向 destX,效果就是慢慢滑动
      mScroller.startScroll(scrollX, 0, delta, 0, 1000);
      invalidate();
      }

      @Override
      public void computeScroll() {
      if (mScroller.computeScrollOffset()) {
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
      postInvalidate();
      }
      }
-------------------- 本文结束感谢您的阅读 --------------------