总体来说本章知识点还是比较简单的,主要就是View的基础知识和事件分发,滑动冲突解决方案。
首先作者介绍了View的一些基础知识,我们了解到View是Android所有控件的基类,ViewGroup继承View,可以理解为一个控件组,其内部可以包含一系列子View。View在父容器中由top、left、 right、bottom四个属性描述自身位置,x、y、translationX和translationY属性描述View在父容器中的位移信息。MotionEvent描述View点击事件的类型,TouchSlop是系统所能识别出的被认为是滑动的最小距离。VelocityTracker用来追踪手指在View上滑动过程中的速度。GestureDetector用来辅助检测用户的单击,滑动,长按,双击等行为。Scroller用来实现View的连续滑动。
接着作者介绍了View滑动的三种实现方式。第一种为使用scrollTo/scrollBy,scrollTo和scrollBy只能改变View内容的位置而不能改变View在布局中的位置。第二种为使用动画,View动画只是对View的影像做操作,并没有真正的改变View的位置参数,属性动画则会修改View的位置参数。第三种为改变布局参数,即改变LayoutParams。
接下来作者介绍了三种连续滑动动画的实现方法,第一种使用Scroller,第二种使用动画,第三种通过一个计时器去刷新View位置。重点看下Scroller,Scroller其实并未实现真正的持续滑动动画,它只负责计算在动画运动中View的位置信息,然后将位置信息交给scrollTo方法去刷新View的位置,对View毫无引用。
下面是本章的重头戏,作者讲解了View的事件分发机制,当一个点击操作发生时,事件最先传递到当前Activity,然后交给Activity所属的Window进行分发,Window再传递到DecorView,DecorView就是当前Activity内的最顶级View,接下来就要从ViewGroup和View两个类的源码去深扒View的事件分发机制。对View事件分发源码阅读后,总结出此图:
![](https://img.haomeiwen.com/i3845889/745ee938cd2a7b6f.jpg)
了解了View的事件分发原理,当然就要知道如何处理事件分发过程中存在的冲突。面对View滑动冲突,首先我们要清楚的知道什么情况下要让那个View响应我们的滑动指令,之后再去通过事件拦截,达到我们想要的结果。作者针对View滑动冲突,给出了两种解决方案。第一种是外部拦截法,即点击事件先经过父容器的拦截方法,如果父容器需要此事件就拦截,不需要此事件就不拦截。父容器onInterceptTouchEvent方法伪代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
intercepted = false;
break;
}
case MotionEvent.ACTION_MOVE: {
if (父容器需要当前点击事件) {
intercepted = true;
} else {
intercepted = false;
}
break;
}
case MotionEvent.ACTION_UP: {
intercepted = false;
break;
}
default:
break;
}
mLastXIntercept = x;
mLastYIntercept = y;
return intercepted;
}
第二种方法是内部拦截法,指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器去处理。首先需要重写子元素的dispatchTouchEvent方法,在该方法中调用父容器的requestDisallowInterceptTouchEvent方法处理拦截,伪代码如下:
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
parent.requestDisallowInterceptTouchEvent(true);
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (父容器需要此类点击事件)){
parent.requestDisallowInterceptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
父容器的onInterceptTouchEvent方法同样需要重写,伪代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}
网友评论