目录
MontionEvent
View继承关系

分发,处理流程

事件分发 : ViewGroup.java -> dispatchTouchEvent
事件处理 : View.java -> dispatchTouchEvent
onTouch与onClick之间会产生事件冲突吗?
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG,"onClick");
}
});
btn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e(TAG,"onTouch");
return false;
}
});
若onTouch() 方法return false,则事件由onClick消费,否则由onTouch消费,onClick不会有反应。
以下为源码,解释原因
View -> dispatchTouchEvent -> performOnTouchCallback



事件在控件中是如何传递的?
Down事件
分发流程

若进行拦截 ViewGroup -> dispatchTouchEvent
分发或者处理(拦截相当于你是最后一个,事件到底处不处理)

dispatchTransformedTouchEvent//是否处理
chlid为空说明说明给自己处理
handled为true说明View处理了事件,为false则说明View未处理事件

若不进行拦截(分发)

final ArrayList<View> preorderedList = buildTouchDispatchChildList();
把子view进行排序


将View按顺序正着插入,再倒序取出
调用dispatchTransformedTouchEvent方法,传入chlid,调用孩子的dispatchTouchEvent(分发流程)
![]()
![]()
若子view全部不处理,则流程和拦截一样(流到前面拦截的方法)
进入if则进行三个条件的处理

newTouchTarget = mFirstTouchTarget
target.next = null
alreadyDispatchedToNewTouchTarget = true;

ViewGroup -> dispatchTouchEvent
Move事件 -> 不会再分发事件
move拦截 --- 处理事件冲突 --- 只能在move的时候处理
事件冲突产生的根本原因,如何解决事件冲突
内部拦截
子view不可以抢父容器的事件,父容器可以抢子view的事件。
内部拦截:getParent().requestDisallowInterceptTouchEvent(false);
子view一旦拿到事件,事件再由谁处理,就是子view说的算。
内部拦截:getParent().requestDisallowInterceptTouchEvent(true);


若为内部拦截,父容器必须不拦截Down事件

public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 内部拦截法:子view处理事件冲突
private int mLastX, mLastY;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
}
getParent().requestDisallowInterceptTouchEvent(true)意味着事件完全由ListVew掌控。
外部拦截
public class BadViewPager extends ViewPager {
private int mLastX, mLastY;
public BadViewPager(@NonNull Context context) {
super(context);
}
public BadViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
// 外部拦截法:父容器处理冲突
// 我想要把事件分发给谁就分发给谁
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// if (event.getAction() == MotionEvent.ACTION_DOWN){
// super.onInterceptTouchEvent(event);
// return false;
// }
// return true;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastX = (int) event.getX();
mLastY = (int) event.getY();
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
return true;
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
return super.onInterceptTouchEvent(event);
}
}
以上,ViewPager左右滑动时ListView不能上下滑动。ListView上下滑动时,ViewPager可以左右滑动。原因是
当用户明显想要水平滑动时,拦截事件让 ViewPager 处理
当用户明显想要垂直滑动时,不拦截事件让 ListView 处理
ViewPager拦截之后子View(ListView)便拿不到后续事件了


608

被折叠的 条评论
为什么被折叠?



