Android的事件分发:分为View和ViewGroup。
View的事件分发,更贴切的可以说是事件处理,毕竟View已经是“最小的”了。
上图:
ViewGroup的事件分发,可以分发给下面的子View,所有在分发过程中有一个拦截onInterceptTouchEvent()操作。
上图:
Activity的事件分发处理流程:
解决滑动冲突:
事件冲突就是,一个事件有多个对个人想要处理,假如处理的对象不是我们想给的对象,就发生了滑动冲突。
解决时间冲突呢,就是把事件指定给想要的对象。
举个例子:Viewpage嵌套listview ,ViewPage横向滑动,listview纵向滑动。
public class SlideConflictViewPage extends ViewPager {
public SlideConflictViewPage(@NonNull Context context) {
super(context);
}
public SlideConflictViewPage(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);
// return true;
}
}
如果调用super.onInterceptTouchEvent(ev),Google帮我们做了处理。
现在我们重写ViewPage的onIntercapteTouchEvent()方法,这么我们是ViewPage嵌套listview,这种一般我们叫外部拦截法。
外部拦截:
public boolean onInterceptTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mLastX = (int) ev.getX();
mLastY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int xSpace = x-mLastX;
int ySpace = y-mLastY;
// 横向移动,拦截不让传入到子view
if (Math.abs(xSpace) > Math.abs(ySpace)) {
return true;
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
内部拦截:
就是在子view中不让父view拦截
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// 在子view中把disallowIntercept设置为TRUE,intercepted = FALSE
// 这样就不会拦截了
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
在子view中把disallowIntercept设置为TRUE,intercepted = FALSE,这样就不会拦截了。
那么如何在子view中设置呢?
在listview中这样设置:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return super.dispatchTouchEvent(ev);
}
我们发现listview还是不能滑动,这是因为
父view的onInterceptTouchEvent的覆写,
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
super.onInterceptTouchEvent(ev);
return false;
}
return true;
这时候的listview可以滑动了,但是呢,ViewPage不能滑动,因为我们在子view中把disallowIntercept设置为TRUE,我们可以在子view的MotionEvent.ACTION_MOVE事件中设置为FALSE就行了。这时候,listview的ev.getAction状态就是ACTION_CANCEL了。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int specX = x-mLatsX;
int specY = y-mLatsY;
// 横向滑动
if (Math.abs(specX)>Math.abs(specY)){
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
mLatsX =x;
mLatsY = y;
return super.dispatchTouchEvent(ev);
}
总结: