Android防自定控件抖动

作者: Ad大成 | 来源:发表于2019-12-24 00:05 被阅读0次

    在最近的项目中碰到需要用手指控制View移动的需求,实现的过程中发现View会随着手指的移动而抖动,并且抖动程度随着拖动距离的增大而增大。简化代码片段如下

    view.setOnTouchListener(new OnTouchListener() { 
            float lastY;
            @Override
            public boolean onTouch(View v, MotionEvent event) {
            if(event.getAction() == MotionEvent.ACTION_DOWN)
                    lastY = event.getY();//获得起始纵坐标
                if(event.getAction() == MotionEvent.ACTION_MOVE ){
                    //执行这一步时view会发生Y轴上的抖动并且抖动程度会随着ACTION_MOVE的执行次数而加剧
                    view.setTranslationY(event.getY()-lastY);
                    //这一步用来探明抖动的原因来自于event.getY()异常
                    Log.i("Y", event.getY()+"");
                }
            }
            return false;   
    });
    
    

    笔者将view匀速向上拖移,Log输出如下:

    03-28 08:32:26.050: I/Y(2234): 76.75454
    03-28 08:32:26.210: I/Y(2234): 73.17617
    03-28 08:32:26.340: I/Y(2234): 75.82344
    03-28 08:32:26.650: I/Y(2234): 71.12374
    03-28 08:32:27.100: I/Y(2234): 74.87508
    03-28 08:32:27.250: I/Y(2234): 70.071075
    03-28 08:32:27.510: I/Y(2234): 73.92541
    03-28 08:32:27.650: I/Y(2234): 67.97551
    03-28 08:32:27.800: I/Y(2234): 74.02449
    03-28 08:32:27.890: I/Y(2234): 66.935455
    03-28 08:32:28.530: I/Y(2234): 73.063614
    03-28 08:32:28.620: I/Y(2234): 65.8301
    03-28 08:32:28.920: I/Y(2234): 71.153694
    03-28 08:32:29.120: I/Y(2234): 64.841194
    03-28 08:32:29.830: I/Y(2234): 70.1468
    03-28 08:32:30.070: I/Y(2234): 63.8322
    
    

    可以看出确实为event.getY()出了问题,于是翻阅getY()的源码如下:

     /**
         * {@link #getY(int)} for the first pointer index (may be an
         * arbitrary pointer identifier).
         *
         * @see #AXIS_Y
         */
        public final float getY() {
            return nativeGetAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
        }
    

    由于nativeGetAxisValue()是native方法所以我们无法直接在在eclipse中看到具体的方法定义,不过至少我们知道getY()返回的值是和AXIS_Y有关的,所以继续查阅AXIS_Y的说明,如下:

    public static final int AXIS_Y
     
    Added in API level 12
    Axis constant: Y axis of a motion event.
     
    ·For a touch screen, reports the absolute Y screen position of the center of the touch contact area. The units are display pixels.
    ·For a touch pad, reports the absolute Y surface position of the center of the touch contact area. The units are device-dependent; use getMotionRange(int) to query the effective range of values.
    ·For a mouse, reports the absolute Y screen position of the mouse pointer. The units are display pixels.
    ·or a trackball, reports the relative vertical displacement of the trackball. The value is normalized to a range from -1.0 (up) to 1.0 (down).
    ·For a joystick, reports the absolute Y position of the joystick. The value is normalized to a range from -1.0 (up or far) to 1.0 (down or near).
    
    

    从"For a touch screen, reports the absolute Y screen position of the center of the touch contact area"中得知AXIS_Y表示的是触摸点相对于触摸区域的绝对Y轴坐标,也就是pointer相对于所触摸View的absoluteY,读到这里大概就明白了为什么会出现View的抖动问题:

        因为View是移动的,在获取相对坐标的过程中,getY()=AXIS_Y=absoluteY+viewTranslationY(这就是为什么抖动程度会随着拖动距离的增加而增加),从Log日志中可以发现,抖动是逐帧间隔形式的,即一次 AXIS_Y=absoluteY之后接着便是AXIS_Y=absoluteY+viewTranslationY。由于无法看到nativeGetAxisValue()的具体定义,所以暂时并不清楚造成这种结果的具体原因。
    

    解决办法很简单,只要把getY()换成getRawY()即可,后者的源码获取的是pointer相对于整个screen的绝对坐标,这里就不贴出来了。

    分析告诉我们,getX/Y()不适用于绑定在动态组件上的onTouchListener。

    接下来分析一下# 图解MotionEvent中getRawX、getRawY与getX、getY以及View中的getScrollX、getScrollY

    1.getRawX、getRawY与getX、getY的区别

    在编写android的自定义控件,或者判断用户手势操作时,往往需要使用MotionEvent中的getRawX()、getRawY()与getX()、getY()取得触摸点在X轴与Y轴上的距离,这四个方法都返回一个float类型的参数,单位为像素(Pixel)。getRawX()、getRawY()返回的是触摸点相对于屏幕的位置,而getX()、getY()返回的则是触摸点相对于View的位置。

    以下两张图直观的表现了这几个方法的区别,在屏幕中央放置了一个Button,并为它注册了OnTouchListener,图中绿圆点为触摸点位置。

    image
    image
    2.View中的getScrollX、getScrollY

    getScrollX()与getScrollY()的值由调用View的scrollTo(int x, int y)或者scrollBy(int x, int y)产生,其中scrollTo是将View中的内容移动到指定的坐标x、y处,此x、y是相对于

    View的左上角,而不是屏幕的左上角。scrollBy(int x, int y)则是改变View中的相对位置,参数x、y为距离上一次的相对位置。

    文字解释总是不好理解的,那么我们就直接上图吧,直观一些。

    image image image
               (图1)                                                         (图2)                                                                (图3)
    

    1.图1中,屏幕中心放置了一个button,而button的内容被放置在了它的左上角。

    2.调用button的scrollTo(-100, -100)方法,结果如图2所示,button内的内容被移至相对button左上角(-100,-100)的位置

    3.对图2的button调用scrollBy(-100,-100)方法,结果如图3所示,button内的内容被移至相对于图2的(-100,-100)位置

    这时的getScrollX()与getScrollY()的值为:

    <pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">06-15 15:44:56.072 20471-20471/com.test.yangy.studiotest V/ScrollActivity﹕ btn scroll X=-200
    06-15 15:44:56.072 20471-20471/com.test.yangy.studiotest V/ScrollActivity﹕ btn scroll Y=-200</pre>

    值得注意的是,当View中的内容向右移动时,getScrollX()的值为负数,同理,向scrollTo与scrollBy的x中传入负数,view中的内容向右移动,反之向左。

    当View中的内容向下移动时,getScrollY()的值为负数,同理,向scrollTo与scrollBy的y中传入负数,view中的内容向下移动,反之向上。

    相关文章

      网友评论

        本文标题:Android防自定控件抖动

        本文链接:https://www.haomeiwen.com/subject/gixxoctx.html