基础知识点:
对于触屏手机,我们在上面进行的任何点击、滑动,也就是触屏(Touch)活动,都会生成被封装到一种叫MotionEvent的事件里,Android系统用一下六种类型来描述用户的touch事件类型
ACTION_DOWN(按下)
ACTION_MOVE(屏幕滑动)
ACTION_UP(离开屏幕)
ACTION_POINTER_DOWN(多点触屏时产生,暂不讲解)
ACTION_POINTER_UP(多点触屏时产生,暂不讲解)
ACTION_CANCEL
MotionEvent里面包含丰富的数据信息,包括:Touch的位置(Touch location),Touch的触点数(也即手指数),Touch的时刻等等
一个完整的手势(gesture)是以action_down开始,以action_up结束
事件流:action_down--->action_move--->action_move--->action_move.....--->action_up(中间的action_move事件是否发生取决于手势是否发生移动)
想想你平常在桌面点击打开app和页面切换滑动时激发的事件流是否有所不同?
事件的分发,拦截,响应的机制(关键字:分发,拦截,响应,消费)
想象:想象一下贪官贪污的一种情况:在某某贫困县,每年国家都会给予一定的资金补助(分发过程),这笔钱(touch事件)经过省政府(根布局),市政府(父布局)都没人去贪污它(即没人去拦截它),那么这钱就继续往下分发,到了县政府这里来,此时某某县大领导(子view)想吃下这笔钱,那么他就不让这钱逃过他的手了,所以他进行了拦截,拦截下来后,就要对这钱进行“处理”呀,这时就进行了响应和消费,以后只要这官不被抓,那么每年国家拨下来的资金都分发到他这里就被拦截及消费了。当然还有另外一种情况(现实不大可能发生),就是整个过程没有人贪污,那么钱就一步一步分发下去,发到县以后,这些年贫困县发展起来了,决定不要这笔钱了(响应但不消费),所以就把钱还给市政府,市政府也决定不要这钱(响应但不消费),就又把钱交给省政府了
事件分发流程:从父布局到子布局:Activity--->RootView--->ViewGroup1--->ViewGroup1的子ViewGroup2 ---> Target View
事件的响应流程:从子布局到父布局:Target View--->ViewGroup2--->ViewGroup1--->RootView--->activity
几个规则:
所有的事件从activity的dispatchTouchevent()函数开始,从父布局到子布局往下分发,当然每个布局在任何事件传到它那里后都可进行拦截
所有的事件顺着界面布局层次往下分发,分发完就回溯(上面的分发和响应流程),这一过程中一旦出现了消费,流程就停在消费的view那一层,view必须把action_down事件消费掉后,后续的这个手势的其他的事件才会传到这个view。
如果整个过程没有view想消费这个事件,那么最终的回溯的结束点就是activity的onTouchEvent()。
几个涉及的重要方法(view或viewgroup里的方法):
public boolean dispatchTouchEvent(Motion ev):事件分发方法,把当前事件分发给下一子布局,是一个递归的调用函数,返回true则表示事件被处理
public boolean onInterceptTouchEvent(MotionEvent event):事件拦截方法,根据事件类型进行拦截,比如一个scrollview想拦截action_move这个事件,返回true则表示进行了拦截
public boolean onTouchEvent(MotionEvent event):事件响应处理方法,可以在方法中把事件消费了,也可不消费,那么就回溯给上级去响应和处理,返回true则表示事件被消费
整个分发和响应过程的规则可以用以下的伪代码(摘自《Android开发艺术探索》)表示:
public boolean dispatchTouchEvent(MotionEvent event){
boolean consume=false;
if(onInterceptTouchEvent(event)){
consume=onTouchEvent(event);
} else {
consume=ChildView.dispatchTouchEvent(event);
}
return consume;
}
以下根据实例讲述过程:本截图引用自全英文档http://trinea.github.io/download/pdf/android/PRE_andevcon_mastering-the-android-touch-system.pdf
讲解:此布局是在frameLayout里面嵌套一个普通的view,当你按下的时候,会触发down事件,事件就从activity出发,由于此时没有任何的布局对此感兴趣(即没拦截和消费),那么分发完后回溯会activity的onTouchEvent,后续发生的move和up事件,这时Android系统会这么认为:既然你整个布局所有组件连按下去的事件都不感兴趣,后续发生的事件肯定也是不感兴趣,所以事件move和up都不往下进行分发和回溯了,直接在activity自己调用onTouchEvent去处理
讲解:此布局是在frameLayout里面嵌套一个button,当你按下的时候,会触发down事件,事件就从activity出发,由于此时button对此事件感兴趣,那么button的onTouchEvent就会对此进行消费,事件被消费后就不会再回溯了,后续发生的move和up事件,button也是感兴趣,也进行了消费,所以整个流程就只走到了button的onTouchEvent方法
讲解:此布局是在Scrollview里面嵌套一个button,当你按下的时候,会触发down事件,事件就从activity出发,由于此时button对此事件感兴趣,而scrollView对此事件不感兴趣,那么button的onTouchEvent就会对此进行消费,事件被消费后就不会再回溯了,后续发生的move和up事件,scrollview和button都感兴趣,但是由于scrollview先拿到事件,进行拦截和消费,所以事件流就没走到button那里,那为什么还会走回activity的onTouchEvent呢,scrollview进行拦截,但不进行消费,也就是scrollview的onTouchEvent返回值是false,所以才会回溯给activity。