zoukankan      html  css  js  c++  java
  • android触控,先了解MotionEvent(一)

    http://my.oschina.net/banxi/blog/56421

    这是我个人的看法,要学好android触控,了解MotionEvent是必要,对所用的MotionEvent常用的API要比较深入的了解.

    下面是我个人的学习过程记录:

    android.view.MotionEvent

    MotionEvent源代码可以在ocs看到,当然你也可以在SDK中下载源代码,或者其他地方,如:

    https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/MotionEvent.java


    MotionEvent事件对象

    一般我们是在View的onTouchEvent方法中处理MotionEvent对象的.

    public boolean onTouchEvent(MotionEvent event)

    在这里我们需要从一个MotionEvent对象中获得哪些信息呢?

    (1)首先应该是事件的类型吧?

    可以通过getAction(),在android2.2之后加入多点触控支持之后使用getActionMasked()方法.

    这两个方法的区别见后文.

    主要的事件类型有:

    ACTION_DOWN: 表示用户开始触摸.

    ACTION_MOVE: 表示用户在移动(手指或者其他)

    ACTION_UP:表示用户抬起了手指

    ACTION_CANCEL:表示手势被取消了,一些关于这个事件类型的讨论见:http://stackoverflow.com/questions/11960861/what-causes-a-motionevent-action-cancel-in-android

    还有一个不常见的:

    ACTION_OUTSIDE: 表示用户触碰超出了正常的UI边界.

    但是对于多点触控的支持,Android加入了以下一些事件类型.来处理,如另外有手指按下了,

    有的手指抬起来了.等等:

    ACTION_POINTER_DOWN:有一个非主要的手指按下了.

    ACTION_POINTER_UP:一个非主要的手指抬起来了

    (2)事件发生的位置,x,y轴

    getX() 获得事件发生时,触摸的中间区域在屏幕的X轴.

    getY() 获得事件发生时,触摸的中间区域在屏幕的X轴.

    在多点触控中还可以通过:

    getX(int pointerIndex) ,来获得对应手指事件的发生位置. 获得Y轴用getY(int pointerIndex)

    (3)其他属性

    getEdgeFlags():当事件类型是ActionDown时可以通过此方法获得,手指触控开始的边界. 如果是的话,有如下几种值:EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM

    一些讨论

    (1)首先是MotionEvent 中getAction()与getActionMasked()的区别:

    首先看代码:

    01/**
    02* Bit mask of the parts of the action code that are the action itself.
    03*/
    04public static final int ACTION_MASK = 0xff;
    05
    06/**
    07* Return the kind of action being performed.
    08* Consider using {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #getActionMasked} and {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #getActionIndex} to retrieve
    09* the separate masked action and pointer index.
    10* @return The action, such as {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_DOWN} or
    11* the combination of {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_POINTER_DOWN} with a shifted pointer index.
    12*/
    13public final int getAction() {
    14return mAction;
    15}
    16
    17/**
    18* Return the masked action being performed, without pointer index information.
    19* Use {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #getActionIndex} to return the index associated with pointer actions.
    20* @return The action, such as {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_DOWN} or {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_POINTER_DOWN}.
    21*/
    22public final int getActionMasked() {
    23return mAction & ACTION_MASK;
    24}
    上面的代码是基于android2.2的,注释是android4.X中最新的.

    他们有什么区别呢?如果mAction的值是在0x00到0xff之间的话。getAction()返回的值,和

    getActionMasked()的返回的值是一样的。

    (Q1)那什么时候返回的值是一样的呢?即当mAction值大于0xff时,那什么时候会大于0xff呢?

    这就是是当有多点触控时。当有多点触控时。

    mAction的低8位即0x00到0xff用来表示动作的类型信息。

    例如:MotionEvent#ACTION_DOWN的值是 0,即0x00。

    MotionEvent#ACTION_UP的值是 1,即0x01。

    等等。

    但是,我们知道Android是支持多点触控的,那么怎么知道这个一个MotionEvent是哪一个

    触控点触发的呢?那么就还需要MotionEvent带有触控点索引信息。

    Android的解决方案时在;mAction的第二个8位中存储。

    例如,如果mAction的值是0x0000,则表示是第一个触控点的ACTION_DOWN操作。

    如果mAction的值是0x0100呢,则表示是第二个触控点的ACTION_DOWN操作。

    第三个的ACTION_DOWN呢?相信你可以推出来是0x0200。

    总而言之,mAction时的低8位(也就是0-7位)是动作类型信息。

    mAction的8-15位呢,是触控点的索引信息。(即表示是哪一个触控点的事件)。

    (Q2),为什么不用两个字段来表示。

    如 int mAction,int mPointer,

    mAction表示动作类型,mPointer表示第几个触控点。

    因为,动作类型只要0-255就可以了,动作类型,mPointer也是。

    只要一个字段(32位),否则需要两个字段(32*2=64位),即可以节约内存。又可以方便提高处理速度。

    不过通常我们都是以不同的字段来存储不同的信息。但是在计算机内部他们还是变成了0,1。

    计算机始终还是以位来存储信息的。如果我们多我熟悉以位为基本单位来理解信息的存储。对于理解android中的很多变量是很有帮助的。因为他其中的很多东西使用的这样的节约内在的技巧。

    如onMeasure中的MeasureSpec。

    =================== the above is update at 2013-01-24=====

    先看关于这两个方法注释:

    我简单的翻译如下:

    01/**
    02* action码的位掩码部分就是action本身
    03*/
    04public static final int ACTION_MASK = 0xff;
    05
    06/**
    07返回action的类型,考虑使用getActionMasked()和getActionIndex()来获得单独的经过掩码的action和触控点的索引.
    08@return action例如ACTION_DOWN或者ACTION_POINTER_DOWN与转换的触控点索引的合成值
    09*/
    10public final int getAction() {
    11return mAction;
    12}
    13
    14/**
    15返回经过掩码的action,没有触控点索引信息.
    16通过getActionIndex()来得到触控操作点的索引.
    17@return action,例如ACTION_DOWN,ACTION_POINTER_DOWN
    18
    19
    20*/
    21public final int getActionMasked() {
    22return mAction & ACTION_MASK;
    23}

    在上面的两个方法中注释出现差异的地方是对于ACTION_POINTER_DOWN的描述:

    通过getAction()返回的ACTION_POINTER_DOWN的是与转换触控点索引的合成值.

    而getActionMasked()则就是一个ACTION_POINTER_DOWN的值:

    这么来看我们知道一个action的代码值还包含了action是那个触控点的索引值:

    现在我们对比来看看ACTION_MASK和ACTION_POINTER_INDEX_MASK

    1
    public static final int ACTION_MASK = 0xff;
    2
    public static final int ACTION_POINTER_INDEX_MASK = 0xff00;
    还没有看出来什么吗?
    您把ACTION_MASK看成是0x00ff

    就知道了吧.

    也就是说,一个MotionEvent中的action代码,

    前8位是实实在在包含表示哪一个动作常量.

    后八位呢就是包含了触控点的索引信息.

    因为ACTION_MASK = 0x00ff所以,经过ACTION_MASK掩码过后的action码就没有索引信息了.

    如何得索引值呢?

    原理:

    先将action跟0xff00相与清除前8位用于存储动作常量的信息,

    然后将action右移8位就可以得到索引值了.

    我们就可以自己想办法得到索引信息了.

    即先对action用ACTION_POINTER_INDEX_MASK进行掩码处理,

    即 maskedIndex = action&ACTION_POINTER_INDEX_MASK = action&0xff00

    这各掩码也就是将action这个数的前8位清零.

    然后再将maskedIndex向右移8位就能够得到索引值了.

    再看看android真实是怎么做的吧,

    用于右移8位的常量.

    1/**
    2* Bit shift for the action bits holding the pointer index as
    3* defined by {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_POINTER_INDEX_MASK}.
    4*/
    5public static final int ACTION_POINTER_INDEX_SHIFT = 8;
    再年得到索引值方法源代码,如下:

    1public final int getActionIndex() {
    2return (mAction & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
    3}

    为什么要有索引信息?

    因为,这样说吧,android中,当有触摸事件发生时(假设已经注册了事件监听器),调用你注册监听器中的方法onTouch(,MotionEvent ev);传递了一个MotionEvent的对象过来.

    但是,想想,上面只传递进来一个MotionEvent过来,如果只是单点触控那是没有问题.

    问题就是当你多个手指触控的时候也是只传递这一个MotionEvent进来,

    这个时候,你当然想知道每个手指的所对应的触控点数据信息啦.

    所以MotionEvent中有就要索引信息了.

    事件是你可以很容易通过API看到,MotionEvent还包含了移动操作中其它历史移动数据.

    方便处理触控的移动操作.

    android sdk对于这个类的描述中就有这么一句:

    For efficiency, motion events with ACTION_MOVE may batch together multiple movement samples within a single object.

    我翻译下:"出于效率的考虑,事件代码为ACTION_MOVE的Motion,会在一个MotionEvent对象中包含多个移动数据采样."

    现在我们对于MotionEvent有了初步的了解了.

    PS:

    我发现android4中MotionEvent中的代码大多变成了原生代码了:

    例如如getX(int)在2.2中是这样的:

    1public final float getX(int pointerIndex) {
    2return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_X];
    3}
    但到了4.x是这样的了:
    view sourceprint?
    1public final float getX(int pointerIndex) {
    2return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
    3}
    是不是进步了呢?哈哈!

  • 相关阅读:
    Nginx虚拟主机配置实例(Nginx VirtualHost Example)
    考研机试 33.密码翻译
    考研机试 26.10进制 VS 2进制
    考研机试 25.剩下的树
    input默认值设置
    http 304优化,了解客户端缓存
    (转)8款在线CSS优化工具/组织和压缩CSS
    照片从模糊到清晰的渐变加载显示方法
    JS压缩工具Closure Compiler 和 YUICompressor的对比
    【转】高性能web开发 如何加载JS,JS应该放在什么位置?
  • 原文地址:https://www.cnblogs.com/leaven/p/3462455.html
Copyright © 2011-2022 走看看