自定义控件、滑动冲突解决
View基础知识
- View的位置参数
- MotionEvent和TouchSlop对象
- VelocityTracker
- GestureDetector和Scroller对象
1. View的位置参数
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);
接管目标View的onTouchEvent方法,在待监听View的onTouchEvent方法中添加如下实现:
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为正值,反之为负值。