當咱們手指按下時,Android採用層層傳遞-冒泡的方式處理點擊事件。例如,如今公司來了個小項目,老闆一看分配給經理作,經理一看分配給小組長,小組長一看好簡單,分配給組員。若是在這個傳遞過程當中(也就是還爲分配到最底部時),某一層以爲我來負責這個比較好的話就會攔截掉這個消息,而後把它處理了,下面的就收不到有消息的這個通知。若是一直到了底層的話,組員若是能完成,就完成它。若是不能完成,那麼就報告給組長,說組長我作不來,邊學邊作要影響進度。組長一看我也作不來,就給經理,經理一看我也不會,就給老闆。這樣也就一層層的傳遞了。
以上的意思也即:消息從上到下依次傳遞,若是在傳遞的過程當中被攔截了就中止下傳。若是沒有被攔截,就一直傳遞到底部,若是底部不可以消耗該消息,那麼就又一層層的返回來,返給上層,直到被消耗或者是到達最頂層。在此過程當中,存在三個重要的方法:spa
dispathTouchEvent(MotionEvent ev)
負責事件的分發,它的返回值就是表示是否消耗當前事件;
onInterceptTouchEvent(MotionEvent ev)
用於判斷是否攔截該消息,若是當前View攔截了某個時間,那麼在同一個事件序列中,此方法不會被再次調用。返回結果表示是否攔截當前事件 。
onTouchEvent(MotionEvent ev)
處理事件。返回結果表示是否消耗當前事件,若是不消耗,則在同一時間序列中,當前View沒法再次接收到事件。
對於一個根ViewGroup來講,點擊事件產生後,首先會傳遞給它,並調用它的dispath方法。若是這個ViewGroup的onIntercept方法返回true就表示它要攔截當前事件,false就表示不攔截,這個時候事件就會繼續傳遞給子元素,接着調用子元素的dispath方法,一直重複以上過程到事件被處理。code
下面介紹一下常見的問題及解決方法:blog
1、滑動衝突事件
View的滑動衝突產生願意大概能夠分爲三種:io
好比說一個常見的,外部一個ListView,裏面一個ScrollView,滑動時出現衝突?event
此時通常是採用外部攔截法(即結合onInterceptTouchEvent、onTouchEvent)來進行解決。具體方法以下:class
(1)外部攔截法容器
外部攔截法就是指全部的點擊時間都通過父容器的攔截處理,若是父容器須要此事件就攔截,若是不須要此事件就不攔截。經過重寫父容器的onInterceptTouchEvent方法:List
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; return intercepted;
注:ACTION_DOWN事件父類容器就必須返回false,由於若是父類容器攔截了的話,後面的Move等全部事件都會直接由父類容器處理,就沒法傳給子元素了。UP事件也要返回false,由於它自己來講沒有太多的意義,可是對於子元素就不一樣了,若是攔截了,那麼子元素的onClick事件就沒法觸發。request
內部攔截法
這種方法指的是父容器不攔截任什麼時候間,全部的事件都傳遞給子元素,若是子元素須要此事件就直接消耗掉,不然就交給父容器進行處理。它須要配合requestDisallowInterceptTouchEvent方法才能正常工做。咱們須要重寫子元素的dispatch方法。
case MotionEvent.ACTION_DOWN: parent.requestDisallowInterceptTouchEvent(true); break; MotionEvent.ACTION_MOVE: if(父容器須要此類點擊事件) { parent.requestDisallowInterceptTouchEvent(false); } break; return super.dispatchTouchEvent(event);
這種方法的話父類容器須要默認攔截除了ACTION_DOWN之外的其餘時間,這樣當子元素調用request方法的時候父元素才能繼續攔截所需的事件。
其餘的
若是以爲上面兩個方式太複雜,看暈了,其實也能夠本身根據項目的實際須要來指定本身的策略實現。例如根據你手指按的點的位置來判斷你當前觸碰的是哪一個控件,以此來猜想用戶是不是要對這個控件進行操做。若是點擊的是空白的地方,就操做外部控件便可。
,具體可參考ViewPager中的滑動衝突