一、事件分發機制java
用戶點擊產生一個MotionEvent,系統把這個事件分發給具體的view消費android
二、MotionEvent介紹json
手勢接觸屏幕產生的事件 app
MotionEvent對象操做有:ide
一、MotionEvent.ACTION_DOWN 手勢按下,是全部觸發操做中最開始的動做佈局
二、MotionEvent.ACTION_UP 手勢向上,是全部觸發操做最後的動做spa
三、MotionEvent.ACTION_MOVE 手勢按下後沒有收回code
四、MotionEvent.ACTION_CANCEL 事件結束 理論上是ACTION_UP 後執行,但也有多是非人爲因素致使的.xml
三、事件分發的本質對象
將MotinEvent傳遞到某個具體的view處理的過程
四、事件分發的對象及分發順序
Activity ViewGroup View
Activity: 控制生命週期、處理事件
統籌視圖的現實和添加、經過其它回調方法與Window、View交互.
View:全部UI組件的基類
ViewGroup:一組view的組合,其自己也是view的子類,是android佈局的全部父類.比view多了可包含子view和定義佈局參數的功能.
五、事件分發的過程協助者
協助者:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()
dispatchTouchEvent():用來進行事件的分發,當點擊事件可以傳遞給當前view,該方法就會被調用.返回結果受到當前View的TouchEvent和下級view的dispatchTouchEvent方法影響,表示是否消費當前的事件
onTouchEvent:在dispatchTouchEvent方法內部調用,用來處理事件,返回結果表示是否消耗當前事件,若是不消耗,則在同一個事件序列中,當前view沒法再接受到後續的事件隊列。並且onTouchEvent返回值由clickable和longclickable共同決定
onInterceptTouchEvent():在dispatchTouchEvent()內部調用.用來表示是否攔截當前的事件.若是當前view攔截了某個事件,那麼在同一個事件序列當中,此方法不會被調用,返回結果表示是否攔截當前事件。
六、代碼說明演示
ViewGroup 表明 -》新建類DemoLayout 繼承LinearLayout
public class DemoLayout extends LinearLayout { private static final String TAG = "DemoLayout"; public DemoLayout(Context context) { super(context); } public DemoLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public DemoLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public DemoLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { // 父容器的分發事件 Log.i(TAG, "----父容器的分發事件"+ev.getAction()); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_CANCEL: break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //父容器的攔截事件 Log.i(TAG, "----父容器的攔截事件"+ev.getAction()); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_CANCEL: break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { //父容器的觸摸事件 Log.i(TAG, "----父容器的觸摸事件"+ev.getAction()); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_CANCEL: break; } return super.onTouchEvent(ev); } }
View 表明-》 新建類View集成Button
public class View extends android.support.v7.widget.AppCompatButton { private static final String TAG = "View"; public View(Context context) { super(context); } public View(Context context, AttributeSet attrs) { super(context, attrs); } public View(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.i(TAG, "----子容器的分發事件"+ event.getAction()); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG, "----子容器的觸摸事件"+event.getAction()); return super.onTouchEvent(event); } }
操做準備:在layout佈局中建立DemoLayout佈局,包含View控件
<com.example.sunyuanfei.myapplication.DemoLayout android:layout_width="500dp" android:layout_height="500dp" android:clickable="true" android:background="#000000"> <com.example.sunyuanfei.myapplication.View android:id="@+id/view_touch" android:layout_width="match_parent" android:layout_height="match_parent" android:text="事件分發"/> </com.example.sunyuanfei.myapplication.DemoLayout>
初次運行看下運行流程:
沒有攔截時
父容器把事件交給了子容器處理 dispatchTouchEvent進行事件分發 onInterceptTouchEvent沒有攔截 子容器執行dispatchTouchEvent事件分發,再執行onTouchEvent事件
攔截後 代碼和流程
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { //父容器的攔截事件 Log.i(TAG, "----父容器的攔截事件"+ev.getAction()); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_CANCEL: break; } return true; }
DemoLayout dispatchTouchEvent進行事件分發 onInterceptTouchEvent攔截 父容器執行onTouchEvent,(備註:父容器clickable默認值是false即表明onTouchEvent 返回false,後續操做系列Action_UP不會執行,以下圖,因此在xml中要聲明clickable="true").從上看出攔截事件onIntercepeTpuchEvent不會再執行,只會被消費一次
若子容器不消費onTouchEvent,則事件會傳遞給父容器,並且不會接受後續操做,父容器onInterceptTouchEvent也不會再執行.一樣若子容器在xml聲明clickable="false"也就表明着子容器的onTouchEvent返回false。(備註操做:事件系列操做指的是按下ACTION_DOWN、移動ACTION_MOVE、擡起ACTION_UP,所謂後續操做如圖子容器的ACTION_UP就沒有再執行了)
@Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG, "----子容器的觸摸事件"+event.getAction()); return false; }
若子容器的onTouchEvent返回false,onClick方法也是不可以執行的,由於回調方法onClick()是在onTouchEvent中執行的
總結