事件分发所解决的问题:
onTouch和onTouchEvent有什么区别,又该如何使用?
为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?
ListView的Item中有Button,onItemClick为什么会失效?等等。。。
什么是事件分发:
事件分发就是当用户点击了屏幕,那么就触发了一个事件event,而这个事件在每一层的传递与分发过程就叫做事件分发。
三个主要的方法:
dispatchTouchEvent接收事件、onTouchEvent消费事件 ;
onInterceptTouchEvent截断事件并交给onTouchEvent处理掉(这个方法仅存在ViewGroup,换言之,只有ViewGroup能拦截事件)。
分发过程:
只要你触摸到了一个控件,那么事件主要按照Activity→PhoneWindow→ViewGroup→View的顺序传递,
如果View,ViewGroup不处理事件,事件将原路返回,最终交给Activity处理。
首先在activity中执行dispatchTouchEvent接收事件,activity不存在onInterceptTouchEvent方法,
所以不能截断事件,当然也不能执行截断事件后的onTouchEvent,这时候activity就把事件直接传递给了
它所属的phonewindow,phonewindow再直接调用根View、即DecorView的dispatchTouchEvent方法,
DecorView本质是一个FrameLayout,接收到事件就传给子ViewGroup
ViewGroup的dispatchTouchEvent,viewgroup默认onInterceptTouchEvent为false即不拦截,直接把
事件传递给了view的dispatchTouchEvent,view接收到这个事件因为没onInterceptTouchEvent不能
截断并且处于最下层已经不能再往下传了,所以只能执行onTouchEvent消费了这个事件,但是在这之前
他会判断这个view是否符合三个条件:
1,控件注册了setOnTouchListener事件
2,控件的enable为true
3,onTouch方法里返回true
满足以上三个条件执行完onTouch后dispatchTouchEvent方法返回true,传递结束,否则返回false,事件传递给
控件的onclick和onTouchEvent(TextView的click默认返回为false,Button,checkBox默认返回为true)
onTouchEvent返回为false的话则把事件传递给与其父view的onTouch,以此类推
结论:
(所以以上流程任一项返回了true,则事件终止传递,自定义一个layout重写以上任意方法返回true亦会终止。)
下面是一个activity复写dispatchTouchEvent方法拦截点击事件,隐藏输入法的例子:
//这个方法判断是否点击了editText外部
public static boolean isShouldHideInput(View v, MotionEvent event) {
if (v != null && (v instanceof EditText)) {
int[] leftTop = { 0, 0 };
v.getLocationInWindow(leftTop);
int left = leftTop[0], top = leftTop[1], bottom = top
+ v.getHeight(), right = left + v.getWidth();
if (event.getX() > left && event.getX() < right
&& event.getY() > top && event.getY() < bottom) {
return false;
} else {
return true;
}
}
return false;
}
//这个方法用于隐藏输入法
public static Boolean hideInputMethod(Context context, View v) {
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
return imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (HideInput.isShouldHideInput(v, ev)) {
if(HideInput.hideInputMethod(this, v)) {
return true; //返回true,拦截事件,其他控件不响应,注释则不拦截点击事件。
}
}
}
return super.dispatchTouchEvent(ev);
}