Android事件分發機制 java
在android 普通view(不包含ViewGroup)和activity中主要有一下兩個方法處理事件: android
public boolean dispatchTouchEvent(MotionEvent ev) // 分發事件 public boolean onTouchEvent(MotionEvent event) // 處理事件
在ViewGroup中還多一個方法: ide
public boolean onInterceptTouchEvent(MotionEvent ev) // 攔截事件
一、在activity中,順序是:事件分發->事件處理,若是在事件分發時消費了某個事件(return true)則事件處理將不會接收到該事件。 this
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 事件分發 @Override public boolean dispatchTouchEvent(MotionEvent ev) { if(ev.getAction() == MotionEvent.ACTION_MOVE) { System.out.println("dispatchTouchEvent-->ACTION_MOVE"); return true; // 表示我消費了,不繼續分發 } return super.dispatchTouchEvent(ev); } // 處理事件 @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("onTouchEvent-->ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("onTouchEvent-->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("onTouchEvent-->ACTION_UP"); break; } return super.onTouchEvent(event); } }
以上代碼的結果: spa
onTouchEvent-->ACTION_DOWN code
dispatchTouchEvent-->ACTION_MOVE 事件
dispatchTouchEvent-->ACTION_MOVE get
dispatchTouchEvent-->ACTION_MOVE it
dispatchTouchEvent-->ACTION_MOVE io
dispatchTouchEvent-->ACTION_MOVE
dispatchTouchEvent-->ACTION_MOVE
dispatchTouchEvent-->ACTION_MOVE
dispatchTouchEvent-->ACTION_MOVE
dispatchTouchEvent-->ACTION_MOVE
dispatchTouchEvent-->ACTION_MOVE
onTouchEvent-->ACTION_UP
從結果上看:ACTION_MOVE並無到onTouchEvent中,由於咱們在dispatchTouchEvent判斷,若是是ACTION_MOVE則return true表示消耗掉該事件,事件就不會分發到onTouchEvent中,全部onTouchEvent只能接收到ACTION_DOWN和ACTION_UP事件。
二、普通view的事件分發
一個普通view的事件由dispatchTouchEvent分發事件,事件的順序是ACTION_DOWN、ACTION_MOVE、ACTION_UP,若是有一個事件被消費掉,其餘的事件不會執行到;分發事件由onTouch首先接收到,若是onTouch返回true了,表示消費掉了該事件,那麼該view的click事件將不會執行。
mButton.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { System.out.println("onTouch..."+event.getAction()); return true; // 消費了該事件,下面的click事件不會執行 } }); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 由於上面的onTouch把事件消費了,則這裏執行不到 System.out.println("onClick..."); } });
三、ViewGroup的事件分發:
ViewGroup的事件分發稍微麻煩點,以自定義的LinearLayout : MyLayout爲例;
事件發生,首先由MyLayout的dispatchTouchEvent進行事件的分發
而後到MyLayout的onInterceptTouchEvent攔截事件
一、onInterceptTouchEvent若是return true表示攔截該事件,並在MyLayout中處理
剩下的事件,事件繼續由MyLayout進行分發,分發機制同上面的view事件分發機制,但此次分發不會考慮分發給子view,也不會走onInterceptTouchEvent,由於系統已經知道
該套事件MyLayout已經攔截,因此直接在MyLayout中處理
二、若是MyLayout的onInterceptTouchEvent返回false(默認返回false[1]),表示不攔截事件,由MyLayout的子view(以Button爲例)的dispatchTouchEvent開始分發事件,分發機制就是上面的view事件分發機制。
public class MyLayout extends LinearLayout { public MyLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { System.out.println("dispatchTouchEvent"); return super.dispatchTouchEvent(ev); } // 事件發生, 首先由MyLayout的dispatchTouchEvent進行事件的分發 // 而後到MyLayout的onInterceptTouchEvent攔截事件 // 若是return true表示攔截該事件,並在MyLayout中處理 // 剩下的事件繼續由MyLayout進行分發,此次分發不會考慮分發給 // 子view,也不會走onInterceptTouchEvent,由於系統已經知道 // 該套事件MyLayout已經攔截,因此直接在MyLayout中處理 // 該demo的執行結果: // 第一套down、move、up : // dispatchTouchEvent // onInterceptTouchEvent // action_down... // dispatchTouchEvent // action_move... // dispatchTouchEvent // action_move... // dispatchTouchEvent // action_move... // dispatchTouchEvent // action_up... // 再一套: // dispatchTouchEvent // onInterceptTouchEvent // action_down... // dispatchTouchEvent // action_move... // dispatchTouchEvent // action_move... // dispatchTouchEvent // action_move... // dispatchTouchEvent // action_up... @Override public boolean onInterceptTouchEvent(MotionEvent ev) { System.out.println("onInterceptTouchEvent"); if(ev.getAction() == MotionEvent.ACTION_DOWN) { return true; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("action_down..."); break; case MotionEvent.ACTION_UP: System.out.println("action_up..."); break; case MotionEvent.ACTION_MOVE: System.out.println("action_move..."); break; } return true; } }
執行的結果:
第一套down、move、up :
dispatchTouchEvent
onInterceptTouchEvent
action_down...
dispatchTouchEvent
action_move...
dispatchTouchEvent
action_move...
dispatchTouchEvent
action_move...
dispatchTouchEvent
action_up...
再一套:
dispatchTouchEvent
onInterceptTouchEvent
action_down...
dispatchTouchEvent
action_move...
dispatchTouchEvent
action_move...
dispatchTouchEvent
action_move...
dispatchTouchEvent
action_up...
MyLinearLayout:
public class MyLinearLayout extends LinearLayout{ public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return true; // 這裏返回true, 則攔截掉事件,不會再往子view傳遞,繼續調用該view的dispachTouchEvent和onTouch } }
MainActivity:
public class MainActivity extends Activity { private MyLinearLayout mLayout; private Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLayout = (MyLinearLayout) findViewById(R.id.layout); mButton = (Button) findViewById(R.id.click); mLayout.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { System.out.println("layout touch " + event.getAction()); return false; } }); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 由於在MyLinearLayout中已經攔截了事件,因此這裏不會輸出 System.out.println("button click..."); } }); } }
執行結果:
layout touch 0
注意[1] : ListView的onInterceptTouchEvent默認返回的是true,表示攔截了事件。因此在listView中的Button按照普通設置click的方法是不能點擊的。