《Android开发艺术探索》——View事件体系

字数 982阅读 589

自定义控件、滑动冲突解决

View基础知识

  1. View的位置参数
  2. MotionEvent和TouchSlop对象
  3. VelocityTracker
  4. GestureDetector和Scroller对象

1. View的位置参数

[图片来源](http://blog.csdn.net/jason0539/article/details/42743531)
[图片来源](http://blog.csdn.net/jason0539/article/details/42743531)

2. MotionEvent和TouchSlop

注意:各个方法相对目标不一样。
view获取自身坐标:getLeft(),getTop(),getRight(),getBottom()
view获取自身宽高:getHeight(),getWidth()
motionEvent获取坐标:getX(),getY(),getRawX(),getRawY()

  • MotionEvent
    Touch事件中,典型的事件有如下几种:
  • ACTION_DOWN —— 手指接触屏幕
  • ACTION_MOVE —— 手指在屏幕上移动
  • ACTION_UP —— 手指从屏幕离开
  • TouchSlop
    TouchSlop是系统所能识别出的被认为是滑动的最小距离,换句话说,当手指在屏幕上滑动时,当两次滑动之间的距离小于这个常量,那么系统不认为是在进行滑动操作。通过以下方式获取这个常量值:
ViewConfiguration.get(getContext()).getScaledTouchSlop();

这个常量有神马意义呢?
可以使用这个常量判断是否达到滑动条件,处理滑动时,做一些过滤。

3. VelocityTracker、GestureDetector和Scroller对象

  • VelocityTracker
    速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度。
    构造方法中初始化获取VelocityTracker对象
VelocityTracker mVelocityTracker = VelocityTracker.obtain();

onTouchEvent方法中添加追踪事件

mVelocityTracker.addMovement(event);

ACTION_UP事件中获取当前的速度。注意这里计算的是1000ms时间间隔移动的像素值,假设像素是100,即速度是每秒100像素。手指从右向左滑动,速度为负值。

mVelocityTracker.computeCurrentVelocity(1000);
float xVelocity = mVelocityTracker.getXVelocity();
float yVelocity = mVelocityTracker.getYVelocity();

最后,当不需要它的时候需要调用clear方法来重置并回收内存。

mVelocityTracker.clear();
mVelocityTracker.recycle();
  • GestureDetecor
    手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。

创建一个GestureDetecor对象并实现OnGestureListener接口,根据需要实现单击等方法:

GestureDetector mGestureDetector = new GestureDetector(this);
// 解决长按屏幕后无法拖动的现象
mGestureDetector.setIsLongpressEnabled(false);

接管目标ViewonTouchEvent方法,在待监听ViewonTouchEvent方法中添加如下实现:

boolean consume = mGestureDetector.onTouchEvent(event);
return consume;

建议
如果只是监听滑动操作,建议在onTouchEvent中实现;如果要监听双击这种行为,则使用GestureDetector 。

  • Scroller
    弹性滑动对象,用于实现View的弹性滑动。
    View的scrollTo/scrollBy方法来滑动时,过程是瞬间完成的。使用Scroller则有过渡滑动的效果。注意,Scoller本身无法让View弹性滑动,它需要和View的computerScroller方法配合使用。
    构造方法初始化
Scroller mScroller = new Scroller(getContext());

缓慢滑动到指定位置,一般在ACTION_UP 方法中执行,松手回弹效果。

private void smoothScrollBy(int dx, int dy) {
    mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
    invalidate();
}
@Override
public void computeScroll() {
    if (mScroller.computeScrollOffset()) {
        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
        postInvalidate();
    }
}

Scroller原理

原理图

当在MotionEvent.ACTION_UP事件触发时,调用startScroll方法,并调用invalidate/postInvalidate方法,会导致View重绘,执行View.draw方法。在此方法中会调用View.computeScroll方法,此方法是空实现,需要我们自己处理逻辑。具体逻辑是:先判断computeScrollOffset,如果为true,表示滚动未结束。则执行scrollTo方法,再次调用postInvalidate,如此反复执行,直到结束。

computeScrollOffset方法计算了一小段时间间隔内偏移的距离,即CurrX,CurrY。并返回是否滚动结束的标记。true表示未结束,false表示结束。

View的scrollTo/scrollBy方法操作的View的内容滑动。
getScrollX返回的是View的左边缘到其内容左边缘的距离。相对于View的左边缘
getScrollY返回的是View的上边缘到其内容上边缘的距离。

如果View的内容向左滑,滑出View的左边界,getScrollX为正值,反之为负值。
如果View的内容向上滑,滑出View的上边界,getScrollY为正值,反之为负值。

getScrollX和getScrollY的变化示意图

参考链接:
Android Scroller完全解析,关于Scroller你所需知道的一切 - 郭霖

推荐阅读更多精彩内容