Android开发Android开发经验谈Android技术知识

Android防自定控件抖动

2019-12-24  本文已影响0人  Ad大成

在最近的项目中碰到需要用手指控制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中的内容向下移动,反之向上。

上一篇下一篇

猜你喜欢

热点阅读