欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android View 事件体系 一

程序员文章站 2022-03-25 08:20:00
Android View 事件体系 一这里我会从以下角度记录一下自己学习的关于最基础,但是也必须了解到的相关知识!基本知识了解-坐标系、参数获取View操作的相关知识View的滑动(下一篇记录,MotionEvent这里有点多啊)事件分发(单独记录可能性很大)坐标系要了解Android的事件分发,坐标系是必须学习的,否则无法理解其中的位置参数。这里的坐标系指两个:Android坐标系View坐标系这两个坐标系一起使得我们更好的控制View。具体的可以看下面两张图,具体给我们展现...

Android View 事件体系 一

这里我会从以下角度记录一下自己学习的关于最基础,但是也必须了解到的相关知识!

  • 基本知识了解-坐标系、参数获取
  • View操作的相关知识
  • View的滑动(下一篇记录,MotionEvent这里有点多啊)
  • 事件分发(单独记录可能性很大)

坐标系

要了解Android的事件分发,坐标系是必须学习的,否则无法理解其中的位置参数。

这里的坐标系指两个:

  • Android坐标系
  • View坐标系

这两个坐标系一起使得我们更好的控制View。具体的可以看下面两张图,具体给我们展现两个坐标系的作用。

Android坐标系

Android View 事件体系 一

View坐标系

Android View 事件体系 一

从上面这两张图,我们就可以出发获取平时我们需要使用获取的参数了。

1、View获取自身的宽高

width = getRight() - getLeft();
height = getBottom() - getTop();

当然如果google其实已经提供了这个参数的封装,那就是getHeight()和getWidth()。

2、View自身的坐标

  • getTop():获取View自身顶边到其父布局顶边的距离**(多级嵌套也只是到其直接的父布局,下面同理)**
  • getLeft():获取View自身左边到其父布局左边的距离
  • getRight():获取View自身右边到其父布局左边的距离
  • getBottom():获取View自身底边到其父布局顶边的距离
  • getTranslationX():获取View自身左上角相对父布局左上角的X轴偏移量
  • getTranslationY():获取View自身左上角相对父布局左上角的Y轴偏移量

需要注意的是前四个方法是不会改变的,就算你拖动了View,但是这个View后续拖动的时候获取的也还是拖动之前原始的位置,

而下面两个是拖动的时候一直改变的。

3、MotionEvent提供的方法

​ 上面VIew坐标系那个圆点就是我们点击事件出发的点,也就是我们手指按在手机屏幕上触摸的点。MotionEvent提供了获取焦点坐标的各种方法

  • getX():获取点击事件距离控件左边的距离,即视图坐标**(这个Android坐标系是按照父布局来的)**
  • getY():获取点击事件距离控件顶边的距离,即视图坐标
  • getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标**(这个Android坐标系是按照屏幕来的)**
  • getRawY(): 获取点击事件距离整个屏幕顶边的距离,即绝对坐标

括号里面的要注意啊,比如getRawY()常常会让人忽略到其到屏幕的距离还要加上状态栏高度和应用标题栏的高度!!!

View操作的相关知识

  • MotionEvent:表示手指触摸屏幕所产生的一系列事件.就是触屏事件(主要就这个,其余不展开)
  • TouchSlop:最小滑动距离,也就是系统能识别出来的被认为滑动的最小的距离.如果手指滑动距离小于这个数,系统就认为你不是滑动
  • VelocityTracker:滑动速度.
  • GestureDetector:手势识别
  • ScaleGestureDetector:放大缩小的手势识别

MotionEvent

Android View 事件体系 一

看上面的图可以知道,MotionEvent触屏事件,其实对于我们重要的就两个大方面,一个是事件类型,还有一个是位置获取,也就是平常我们需要获取的的参数。

事件类型:

其实事件类型是很重要的,并且随着Android的更新MotionEvent也随着更新,从最初只支持单点触碰和轨迹球的事件,都后续的支持手势,支持多点触屏,支持触碰笔,鼠标,键盘,操纵杆,游戏控制器等输入工具的输入。事件类型就是指代MotionEvent对象所代表的动作。比如典型的事件,Down,Move,Up,就是一个典型的事件流。一个触屏事件都是从down开始,Up结束,这个Up不仅仅指代Up还有cancel等等。但是Cancel需要了解的不一样,这个需要了解事件分发

多指触摸:

相关属性和函数

还有一个需要注意的就是多指触碰的事情了。因为这个时候要涉及另一个重要的点:Pointer。因为刚刚讨论了事件类型,但是如果是多指触屏呢?这个时候事件流是怎么走的呢?MotionEvent为了完成这个,引入了Pointer的概念,每一个Pointer对象记录属于自己的事件类型,而一个MotionEvent记录存储多个pointer。

同时多指触屏还引入了两个新的事件,ACTION_POINTER_DOWN和ACTION_POINTER_UP。

  • ACTION_POINTER_DOWN:代表用户使用一个手指触摸到屏幕上,也就是说,在已经有一个触摸点的情况下,有新出现了一个触摸点

  • **ACTION_POINTER_UP:**代表用户的一个手指离开了触摸屏,但是还有其他手指还在触摸屏上

可以查看源码知道,MotionEvent中定义了属性记录到底有几个手指在屏幕上操作,但是这些属性都不推荐使用,已经废弃了(2.0版本以后)

 /** @deprecated */
    @Deprecated
    public static final int ACTION_POINTER_1_DOWN = 5;
    /** @deprecated */
    @Deprecated
    public static final int ACTION_POINTER_1_UP = 6;
    /** @deprecated */
    @Deprecated
    public static final int ACTION_POINTER_2_DOWN = 261;
    /** @deprecated */
    @Deprecated
    public static final int ACTION_POINTER_2_UP = 262;
    /** @deprecated */
    @Deprecated
    public static final int ACTION_POINTER_3_DOWN = 517;
    /** @deprecated */
    @Deprecated
    public static final int ACTION_POINTER_3_UP = 518;

而想要获取具体的信息可以查看以下函数:

   //多点触控必须使用这个方法获取事件类型
   public int getActionMasked() {
        throw new RuntimeException("Stub!");
    }
	//获取该事件是那个指针(手指)产生的
    public int getActionIndex() {
        throw new RuntimeException("Stub!");
    }
    //获取屏幕上手指个数
     public int getPointerCount() {
        throw new RuntimeException("Stub!");
    }
    //获取一个指针(手指)的唯一标识符ID,在手指按下和抬起之间ID始终不变
    public int getPointerId(int pointerIndex) {
        throw new RuntimeException("Stub!");
    }
    //通过指针(手指)的id获取当前状态的pointerIndex
     public int findPointerIndex(int pointerId) {
        throw new RuntimeException("Stub!");
    }
       public float getX(int pointerIndex) {
        throw new RuntimeException("Stub!");
    }

    public float getY(int pointerIndex) {
        throw new RuntimeException("Stub!");
    }

这里有一个**华点,**那就是getActionMasked(),要知道我们平时写的onTouchEvent的时候,往往是如下的:

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
        	// 手指按下
            break;
        case MotionEvent.ACTION_MOVE:
            // 手指移动
            break;
        case MotionEvent.ACTION_UP:
            // 手指抬起
            break;
        case MotionEvent.ACTION_CANCEL:
            // 事件被拦截 
            break;
    }
    return super.onTouchEvent(event);
}

这里使用的是getAction(),这个就不一样了.

action和index的获取

getAction()和getActionMasked()

首先必须明白的有一点,那就是多指触控的时候,必须使用getActionMasked()获取事件类型.先记住这个再说.

当多个手指按在屏幕上的时候,就出现一个问题,怎么获取并区分事件类型.之前我们看到上面已经废弃的那个,就是记录第几个手指按下的,但是这个已经废弃了,那现在怎么解决的呢?

在Android2.0的时候,开始引入了多点触控的技术,但是由于技术不成熟,硬件和驱动也跟不上,多数设备只能追踪三个点而已,因为直接简单的粗暴解决了,就是多添加几个常量来判定多点触控,就是之前讲的的Android2.0以后废弃的那些常量.

那么后来google工程师怎么解决了这个事情呢?

通过之前说的我们就可以知道,多点触控比单点触控多了两个事件类型.ACTION_POINTER_DOWN和ACTION_POINTER_UP

google工程师通过对这两个int类型常量进行拆分,最后八位表示事件类型,往前八位表示index,也就是手指.具体变化可以如下看看

手指按下 触发事件(数值)
第1个手指按下 ACTION_DOWN(0x00000000)
第2个手指按下 ACTION_POINTER_DOWN(0x00000105)
第3个手指按下 ACTION_POINTER_DOWN(0x00000205)
第4个手指按下 ACTION_POINTER_DOWN(0x00000305)

因为google工程师将index和事件类型结合,所以这个时候还用getAction获取就会有问题.因为多指触控的down默认值是0x00000005,而上面显示的都是0x000001/02/0305,这样识别不出来,所以这时候要使用getActionMasked()来获取事件类型了,它首先会把表示index的倒数第二的八位清除,这样就能获取到底是什么事件类型了.就可以识别了.

例如:
0x00000105 & 0x000000ff = 0x00000005

所以其实来说,getActionMasked()使用这一个就行了,因为它对于单指触控也是一样的,但是如果为了兼容旧版本,还是可以使用getAction的.

index和pointID的变化规则

在2.2版本以后,可以通过getActionIndex()轻松获取事件的索引(index),但是这个索引变化有以下特点:

  • 从0开始,自动增长
  • 如果之前落下的手指抬起,后面手指的index会随之减小
  • index变化趋向于第一次落下的数值(落下手指时,前面有空缺会优先填补空缺)
  • 对move事件无效
  1. 从0开始,自动增长

    手指按下 触发事件(数值)
    第1个手指按下 ACTION_DOWN(0x00000000)
    第2个手指按下 ACTION_POINTER_DOWN(0x00000105)
    第3个手指按下 ACTION_POINTER_DOWN(0x00000205)
    第4个手指按下 ACTION_POINTER_DOWN(0x00000305)
  2. 如果之前落下的手指抬起,后面手指的index会随之减小

    手指按下 触发事件(数值)
    第1个手指按下 ACTION_DOWN(0x00000000)
    第2个手指按下 ACTION_POINTER_DOWN(0x00000105)
    第3个手指按下 ACTION_POINTER_DOWN(0x00000205)
    第2个手指抬起 ACTION_POINTER_UP (0x00000106)
    第3个手指抬起 ACTION_POINTER_UP (0x00000106)

    这里可以看到最后两个01,为什么呢?因为当第二个手指抬起的时候,这个时候第三个手指index随之减小,填补到前面,变成了01了,所以这个时候第三个手指抬起,又变成了01抬起.

  3. index变化趋向于第一次落下的数值(落下手指时,前面有空缺会优先填补空缺)

    手指按下 触发事件(数值)
    第1个手指按下 ACTION_DOWN(0x00000000)
    第2个手指按下 ACTION_POINTER_DOWN(0x00000105)
    第3个手指按下 ACTION_POINTER_DOWN(0x00000205)
    第2个手指抬起 ACTION_POINTER_UP (0x00000106)
    第4个手指按下 ACTION_POINTER_DOWN(0x00000105)
    第3个手指抬起 ACTION_POINTER_UP (0x00000206)

    这个就是当第二个手指抬起的时候,第三个手指index应该减小,但是这个时候第四个手指按下,直接填补了第二个手指的空缺,而不是第三个手指减小index,第四个填补第三个的位置.

  4. 对move事件无效

    这个又是一个华点,之前我们看过关于Android2.0时候的简单粗暴的实现多点触控的方式,但是你会发现,里面没有添加move的属性啊!为什么呢?因为move太难以监控了,很难无歧义的实现单独追踪每一个手指.

    这个index只有在down和up的时候有用,在move的时候时没有用的,但是事件追踪非常重要的一个环节就是move,所以这就很尴尬,这个时候就必须要使用到pointID.这个属性了,这个属性和index不一样,最重要的一点就是,pointID在down和up之间的时候是始终不变的,利用这一点就可以对move事件进行追踪了,使用这个唯一的标识符.

move事件

要了解多指触控的move事件,首先要了解getX(int pointerIndex)中的pointerIndex,这里又冒出来一个pointerIndex,这个与之前说的actionIndex又什么区别呢?

其实没区别,只不过就只是这里像单独给move使用的actionindex,那么怎么通过pointID进行事件追踪呢?靠下面两个方法:

方法 简介
getPointId(int pointerindex) 获取一个指针(手指)的唯一标识符,在手指按下和抬起之间ID始终不变
findPointIndex(int pointID) 通过pointID获取当前状态下的pointIndex,之前通过pointIndex获取其他内容

后面还有以下内容,有兴趣的可以去了解一下,但是google的

[https://developer.android.com/reference/android/view/MotionEvent官方文档]:

太长了,看的脑壳痛,这个有兴趣的可以去了解

  • 批处理(历史数据)
  • 设备类型
  • 一致性保证
  • 总结(属性、函数等总结)

到此为止也就结束了,那么拜拜了,记录自己的学习,也与各位大佬们分享自己的学习。

具体参考博客如下:

Android技能树 — View事件体系小结 https://juejin.im/post/6844903561894068231#heading-3

MotionEvent详解 https://blog.csdn.net/vansbelove/article/details/78416791

Android多点触控详解 https://blog.csdn.net/l707941510/article/details/81300333

google官方 https://developer.android.com/reference/android/view/MotionEvent

本文地址:https://blog.csdn.net/weixin_43900919/article/details/108824698

相关标签: android