关于 MotionEvent 的笔记

本文详细介绍了MotionEvent在Android中的使用,包括pointerId、pointerIndex的概念和区别,ACTION和ActionMask的内部机制,以及批处理(Batch)的原理。通过对MotionEvent的重要方法和事件类型的解析,阐述了如何高效地处理多点触摸事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

  • MotionEvent 不仅仅用来表示 触摸事件, 还可以是 鼠标/trackball/pen 等等事件.

  • 一个MotionEvent包含的信息: ACTION_DOWN 这种, 坐标, 压力, 大小, 方向.

  • 两个相邻的 MotionEvent 的 时间间隔差不多是 10–40 ms 之间.

    • 即, 这个时间段 其实也够了;
    • 即 每秒种保证了 25—100 次的刷新, 即使ui上的更新, 也感觉比较顺滑的了.

关于pointerId和pointerIndex

  • 每一个MotionEvent 都 包含了 当前所有 触摸点(Pointer)的信息—-即使它没有动.
    • 即如果有多个手指在屏幕上面, 即使只有一个手指动, 但是这个MotionEvent中包含了所有的pointer的信息(坐标/压力等).
    • 有两个不同的东西:
      • pointerId
        • 在一个pointer的down到up之间, 其值恒定;
        • 其值可能会大于 pointers的总数;
          • 因为中途可能会有pointer离开, 其对应的pointerId就空了(当然可能会被新down下去的pointer拿去用);
        • pointerId失效后, 可能会被新down下去的pointer拿去用.
      • pointerIndex
        • 一个pointer在down到up之间时, 其pointerIndex值是可能会有变化的.
        • 比如, 总共有4个pointers.
          一个pointer的pointerIndex为3,
          但此时pointerIndex为1的手指up了, 那么总共变成了3个pointers了,
          此时这个pointer的pointerIndex可能就会被调整为2了.
关于pointerId
  • 每一个触摸点 都有一个 pointerid—-这个是唯一的, 一直有效的.(但在up/pointer_up后会失效, 可能会别新按下去的手指拿去)
  • 获取 pointerId 的办法:

    int getPointerId(int pointerIndex)//参数是0什么的, 最大为: int getPointerCount()-1
    ----注意, 在每一个gesture中, 一个手指的 pointerIndex 可能会不同, 但在 pointerId 却是一样的.
              一个 pointerId 的有效期: 从这个手指 down/pointer_down 到 up/pointer_up 这一段时间之内.
        注意: pointerId 也是按 0,1,2,3...之类的序号排下去的.
              一个手指的pointerId 在 up/pointer_up 后就无效了, 
                    其可能会被 其它新 按下去的 手指拿去用. 
                ----这个要注意!
                ----即 pointerId 并非在 一个gesture 中一直唯一表明某一个手指的, 
                        而仅仅是指 一个手指down/pointer_down 到 up/pointer_up 这一段时间之内 的id值.
    
关于pointerIndex
  • pointerIndex 则是从0–getPointerCount() 之间;
  • 一个 pointer 的 index 其 会随着 情况的变化而变化, —-即不恒定.
    • 比如,
      总共有4个pointers.
      一个pointer的pointerIndex为3,
      但此时pointerIndex为1的手指up了, 那么总共变成了3个pointers了,
      此时这个pointer的pointerIndex可能就会被调整为2了.
  • 以下这些方法的参数用的都是 pointerIndex, 而不是 pointer id:

    getX(int), 
    getY(int), 
    getAxisValue(int), 
    getPointerId(int),  //参数是pointerIndex, 返回的是 pointerId
    getToolType(int)
    
  • 关于 pointerIndex 和 pointerId 之间的转换:

    • 方法: getPointerId(int) , 我们通过 pointer index 来得到这个 pointer的id
    • 方法: findPointerIndex(int) , 我们可以 通过 pointer id 得到 其 index.

列几个重要的方法吧(有些前面已列过了)

如下:

int getPointerCount()
int getPointerId(int pointerIndex)
int findPointerIndex(int pointerId)
int getPointerId(int pointerIndex)
int getPointerIdBits()
            这个方法是, 拿到当前这个MotionEvent的所有手指的id号.----这些id号 按位运算 放到 一个 int数据中.
            比如, 有3个手指, 其对应的id为 2,3,5, 则返回的是  0000 0000 0010 1100, 即, 0x2c了.
           ----注意的是 pointerId 其实很朴素, 就是0, 1, 2, 3.....之类排下去的.

关于 Action 和 ActionMask 和 ActionIndex:

关于Action的简述:
  • 内部用于标注事件类型的方式是:—-有一个int型数据用于记录 一个pointer的pointerIndex和action.

    • 低版本直接做为MotionEvent的数据成员:

      private int mAction;
      
    • 高版本MotionEvent中需要通过jni来查询:

      private static native int nativeGetAction(long nativePtr)
      
    • 这个 int 型数据 实现上只用到了 低两位, 比如:

      0x0101 ---- 高8位, 表示 pointerIndex
                  低8位, 表示 事件类型, ACTION_DOWN/ACTION_UP/ACTION_POINTER_DOWN 之类.
      
  • 相关几个方法:

    int getAction()
            返回的 0x0101 这种.
    int getActionMasked()
            返回的是 低8位, 即 事件类型, ACTION_DOWN/ACTION_UP/ACTION_POINTER_DOWN 之类.
    int getActionIndex() 
            返回的是 高8位, 即pointerIndex.
                发生了 ACTION_POINTER_DOWN 和 ACTION_POINTER_UP 的 pointer 的 index.
    
  • 内部标识位 的 详解:

    • action 类型:

      public static final int ACTION_MASK             = 0xff; //用于获取低8位.
      
      public static final int ACTION_DOWN             = 0;
      public static final int ACTION_UP               = 1;
      public static final int ACTION_MOVE             = 2;
      public static final int ACTION_CANCEL           = 3;
      public static final int ACTION_OUTSIDE          = 4;
      public static final int ACTION_POINTER_DOWN     = 5;
      public static final int ACTION_POINTER_UP       = 6;
      public static final int ACTION_HOVER_MOVE       = 7;
      public static final int ACTION_SCROLL           = 8;
      public static final int ACTION_HOVER_ENTER      = 9;
      public static final int ACTION_HOVER_EXIT       = 10;
      
    • pointerIndex:

      public static final int ACTION_POINTER_INDEX_MASK  = 0xff00; //用于获取低2字节中的高8位.
      public static final int ACTION_POINTER_INDEX_SHIFT = 8; //因为要从高8位移到低8位.
      
      以下这些都过时了----而且是错的, 不要按这个来了.
      public static final int ACTION_POINTER_1_DOWN   = ACTION_POINTER_DOWN | 0x0000; = 0x0005;
      public static final int ACTION_POINTER_2_DOWN   = ACTION_POINTER_DOWN | 0x0100; = 0x0105;
      public static final int ACTION_POINTER_3_DOWN   = ACTION_POINTER_DOWN | 0x0200; = 0x0205;
      public static final int ACTION_POINTER_1_UP     = ACTION_POINTER_UP | 0x0000; = 0x0006;
      public static final int ACTION_POINTER_2_UP     = ACTION_POINTER_UP | 0x0100; = 0x0106;
      public static final int ACTION_POINTER_3_UP     = ACTION_POINTER_UP | 0x0200; = 0x0206;
      public static final int ACTION_POINTER_ID_MASK  = 0xff00;
      public static final int ACTION_POINTER_ID_SHIFT = 8;
      
  • 注意一点:

    • ACTION_DOWN 的点 离开时 可能是以 ACTION_POINTER_UP 离开
      • —-因为可能并不是最后一个点离开的.
    • 而 ACTION_POINTER_DOWN 的点, 可能以 ACTION_UP 离开的
      • —-因为虽然并不是第0个点按下去, 但可能是最后一个点离开的.
    • 所以, * getAction 拿到的 ACTION_UP 并不总是 第0个按下去点的 离开事件.*

关于 Batch

概述:
  • 由于, 我们每次通过 MotionEvent 的去处理 多点触摸事件, 很麻烦:

    int getPointerCount()
    int getPointerId(int pointerIndex)
    int findPointerIndex(int pointerId)
    int getPointerId(int pointerIndex)
    
  • 所以, 思路上: 将 多个 motion event 放到一个 MotionEvent中.

    • —-即 批处理 的思想.
    • —-注意:
      • 这个 批处理 只在 事件类型为 ACTION_MOVE 时 才有的,
      • 而 ACTION_DOWN/ACTION_UP/ACTION_CANCEL/ACTION_POINTER_DOWN/ACTION_POINTER_UP 都是没有的.
详述:
  • 一个 MotionEvent 中包含的事件有:

    • 最后一个motion event事件 + getHistorySize个历史 motion event事件.
  • 这个方法, 注意一下:

    float nativeGetAxisValue(long nativePtr, int axis, int pointerIndex, int historyPos)
    
  • 涉及的方法有:

    float getX()
            本质是: nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
                        即, 返回的是 **第0个手指** **最后一个**motion event 的x轴位置, 
    float getX(int pointerIndex)
            本质是: nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
                        即, 返回的是 **第pointerIndex个** 手指 **最后一个**motion event 的x轴位置.
    
    float getHistoricalX(int pointerIndex, int pos)
            本质是: nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, pos);
                        即, 返回的是 第pointerIndex个 手指 第pos个 motion event 的x轴位置.
    
    final long getHistoricalEventTime(int pos)
        ----这里我就不多贴出来了, 实际上, 不仅仅可以 获得历史 motion event 的 坐标/时, 
                        还可以 获得 压力 等等信息.
                这里就不多说了.
    
    int getHistorySize()
            获得 这个 MotionEvent 中 包含了多少个 历史motion event事件.
            只有当 事件类型为 ACTION_MOVE 时, 其返回才不为0; 否则返回0.
    
  • 处理示例:

    void printSamples(MotionEvent ev) {
        final int historySize = ev.getHistorySize();
        final int pointerCount = ev.getPointerCount();
        for (int h = 0; h < historySize; h++) {
            System.out.printf("At time %d:", ev.getHistoricalEventTime(h));
            for (int p = 0; p < pointerCount; p++) {
                System.out.printf("  pointer %d: (%f,%f)", 
                        ev.getPointerId(p), ev.getHistoricalX(p, h), ev.getHistoricalY(p, h));
            }
        }
        System.out.printf("At time %d:", ev.getEventTime());
        for (int p = 0; p < pointerCount; p++) {
            System.out.printf("  pointer %d: (%f,%f)", 
                        ev.getPointerId(p), ev.getX(p), ev.getY(p));
        }
    }
    注意:
        先处理 历史motion event事件(序号越为0, 时间就越早), 
        最后 再来处理 最近的 motion event事件.
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值