編輯:關於Android編程
說到android事件的分發機制,真的是感覺既熟悉又陌生,因為每次需要用到的時候查看相關的源碼,總能找到一些所以然來,但是要根據自己理解從頭到尾說一遍,卻一點都說不上。總結原因吧,感覺是自己不善於總結,過目就忘,並沒有把心思放在上面,自然也就沒有一點概念咯~~所以在這裡主要是把自己理解的一些東西記錄下來,不涉及源代碼。
好吧,接下來簡單說說android事件分發流程吧,說到事件分發,首先應該想到的是兩個類,View和ViewGroup,ViewGroup是繼承自View實現的,ViewGroup是控件容器,主要作用是包含具體控件,可以把ViewGroup想象成為一個盒子,裡面按規則包含各種各樣的控件,而包含控件的ViewGroup又可以把它當一個基本的控件單元,包含在另外一個ViewGroup中。實際上一個觸摸事件是由上層的父類傳遞進來的,而最基本的ViewGroup的觸摸事件則是由Activity傳入的,Activity也有dispathTouchEvent()和onTouchEvent()方法,Activity接收到觸摸事件時,會調用內部ViewGroup的dispathTouchEvent()方法。
事件分發流程和View相關的方法主要有兩個:dispathTouchEvent()和onTouchEvent(), dispathTouchEvent()是分發事件的意思,onTouchEvent()才是真正處理事件的地方,實際上在View的dispathTouchEvent()方法中是通過調用onTouchEvent()處理事件的。在這裡和觸摸相關的還有一個onTouch方法,這個是使用控件的setOnTouchListene()設置的,它是滿足一定條件時在onTouchEvent()方法之前調用的。
ViewGroup相關的方法除了dispathTouchEvent()和onTouchEvent(),還有一個onInterceptTouchEvent()方法,它表示的是攔截事件的意思,默認返回值是false,表示不攔截事件。另外一個就是onTouch方法了,這個也是使用控件的setOnTouchListene()設置的。
Activity_main.xml
MainActivity.java
public class MainActivity extends Activity { private MyViewGroup mvgV2 = null; private MyView mvV2 = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(Config.show_onTouch) { mvgV2 = (MyViewGroup) findViewById(R.id.mvg_v2); mvV2 = (MyView) findViewById(R.id.mv_v2); mvgV2.setOnTouchListener(new MyTouchListener()); mvgV2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); mvV2.setOnTouchListener(new MyTouchListener()); mvV2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log("MainActivity" + ": " + "dispatchTouchEvent"); } // return super.dispatchTouchEvent(ev); if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log("MainActivity" + ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log("MainActivity dispatchTouchEventreturn" + ": " + flag); } return flag; } @Override public boolean onTouchEvent(MotionEvent event) { if(Config.show_main) { MyLog.log("MainActivity" + ": " + "onTouchEvent"); } // return super.onTouchEvent(event); if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log("MainActivity" + ": " + "onTouchEvent ACTION_UP"); break; } } boolean flag = super.onTouchEvent(event); if(Config.show_default) { MyLog.log("MainActivity onTouchEventreturn" + ": " + flag); } return flag; } private class MyTouchListenerimplements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { if(v.getId() == R.id.mvg_v2) { if(Config.show_main) { MyLog.log(v.getTag().toString()+ ": " + "onTouch"); } if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(v.getTag().toString()+ ": " + "onTouchACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_UP"); break; } } return false; } else if(v.getId() == R.id.mv_v2) { if(Config.show_main) { MyLog.log(v.getTag().toString()+ ": " + "onTouch"); } if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(v.getTag().toString()+ ": " + "onTouch ACTION_UP"); break; } } return false; } return false; } } }
MyView.java
public class MyView extends TextView { public MyView(Contextcontext) { super(context); } public MyView(Contextcontext, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag); } return flag; } @Override public boolean onTouchEvent(MotionEvent event) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onTouchEvent"); } // return super.onTouchEvent(event); if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP"); break; } } boolean flag = super.onTouchEvent(event); if(Config.show_default) { MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag); } return flag; } }
MyViewGroup.java
public class MyViewGroup extends LinearLayout { public MyViewGroup(Contextcontext, AttributeSet attrs) { super(context, attrs); } public MyViewGroup(Contextcontext) { super(context); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag); } return flag; // return false; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent"); } // return true; if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP"); break; } } boolean flag = super.onInterceptTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString()+ " onInterceptTouchEventreturn: " + flag); } return flag; } @Override public boolean onTouchEvent(MotionEvent event) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onTouchEvent"); } if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP"); break; } } boolean flag = super.onTouchEvent(event); if(Config.show_default) { MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag); } return flag; } }
測試默認情況下時間分發流程,把Config.java的修改如下:
public staticfinal boolean show_onTouch= false; // 顯示onTouch方法內容 public staticfinal boolean detail = false; // 顯示詳細的action內容 public staticfinal boolean show_main= true; // 顯示主要內容 public staticfinal boolean show_default= true; // 顯示默認返回值
1. 保持返回值為默認值,分別設置如下:
MainActivity:
dispathTouchEvent():super.dispathTouchEvent()
onTouchEvent():super. onTouchEvent()
MyViewGroup:
dispathTouchEvent():super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouchEvent():super.onTouchEvent()
MyView:
dispathTouchEvent():super.dispathTouchEvent()
onTouchEvent():super.onTouchEvent()
其他地方不做修改,運行程序。
見下圖:
由此可見,觸摸事件最先傳遞到MainActivity,使用dispathTouchEvent()進行分發,內部調用了viewgroup1的dispathTouchEvent(),之後調用onInterceptTouchEvent()判斷是否攔截事件,這裡返回false,緊接著調用viewgroup2的dispathTouchEvent(),通過遍歷viewgroup2的子控件,最後找到了view2控件,調用view2的dispatchTouchEvent()方法,內部調用了onTouchEvent()方法,所有的方法都返回了false,所以又繼續返回父控件,最後到MainActivity的onTouchEvent()。為什麼是這樣的流程需要看下源碼,裡面可以很清晰了解具體的流程,這裡只想簡單說明事件分發的流程。
1.默認情況下
dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()返回值都是false,表示不消費touch事件。
2.view的事件分發過程:dispatchTouchEvent()->onTouchEvent()
3.viewgroup事件分發過程:
dispatchTouchEvent()->onInterceptTouchEvent()->onTouchEvent()
上述結果都是在各個處理流程返回默認值的時候成立,也就是沒有控件消費事件的時候。
在activity_main.xml中對viewgroup2和view2設置,加入如下代碼:
android:clickable="true"
把Config.java的修改如下:
public staticfinal boolean show_onTouch= true; // 顯示onTouch方法內容 public staticfinal boolean detail = false; // 顯示詳細的action內容 public staticfinal boolean show_main= true; // 顯示主要內容 public staticfinal boolean show_default= false; // 顯示默認返回值
各方法返回值:
MainActivity:
dispathTouchEvent():super.dispathTouchEvent()
onTouchEvent():super. onTouchEvent()
MyViewGroup:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent ()
onTouch(): false
onTouchEvent():super.onTouchEvent()
MyView:
dispathTouchEvent():super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch():false
onTouchEvent():super.onTouchEvent()
由圖可見,view2的onTouch已經觸發了,它是在dispatchTouchEvent()和onTouchEvent()之間調用的,調用順序是:
dispatchTouchEvent()->onTouch()->onTouchEvent()。
另外,由於設置了view2的clickable屬性,它的onTouchEvent()是返回true的。所以viewgroup2的onTouch()並沒有觸發,需要把MyView的onTouchEvent()返回值設置成false才會觸發。這裡就不貼測試結果了,viewgroup2的調用順序是這樣的:
dispatchTouchEvent()->onInterceptTouchEvent()->onTouch()->onTouchEvent(),結合View和ViewGroup的例子說明,onTouch()是在onTouchEvent()之前調用的。
因為View的onTouchEvent()返回true,表示已經消費了改事件,所以事件不會向上投遞了,後面的ViewGroup的onTouchEvent()也不會調用。
1.View調用順序是:dispatchTouchEvent()->onTouch()->onTouchEvent()。
2.ViewGroup調用順序:
dispatchTouchEvent()->onInterceptTouchEvent()->onTouch()->onTouchEvent()
3.如果設置了View的clickable屬性,那麼onTouchEvent()返回true,代表消費該事件,後續將不會繼續投遞該事件。
單純把MyView的onTouchEvent()的返回值設置成false,其他條件和測試2保持不變,代碼如下:
@Override public boolean onTouchEvent(MotionEvent event) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onTouchEvent"); } // return super.onTouchEvent(event); if(Config.detail) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onTouchEvent ACTION_UP"); break; } } boolean flag = super.onTouchEvent(event); if(Config.show_default) { MyLog.log(getTag().toString()+ "onTouchEvent return: " + flag); } return false; }
可以看到,當view2的onTouchEvent()返回false時,表示事件沒有被消費,所以繼續向上投遞,viewgroup2的onTouch()和onTouchEvent()都會觸發。因為viewgroup2設置了clickable屬性,所以它的onTouchEvent()是返回true的,所以事件就不會繼續向上投遞了,如果把ViewGroup的onTouchEvent()返回值手動改成false,那麼viewgroup1的onTouchEvent()還是會觸發的。
1.當一個View設置了clickable為true屬性之後,他的onTouchEvent()會返回true,表示消費了touch事件。
設置返回值:
MainActivity:
dispathTouchEvent():super.dispathTouchEvent()
onTouchEvent():super. onTouchEvent()
MyViewGroup:
dispathTouchEvent():super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch():false
onTouchEvent():super.onTouchEvent()
MyView:
dispathTouchEvent():super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch():true
onTouchEvent():super.onTouchEvent()
其他條件和測試3一樣
可以看到把view2的onTouch返回true,則接下來的onTouchEvent()也不會調用,因為這樣設置代表view2已經處理了事件,那麼之後onTouchEvent()也不會執行了。
1.當onTouch ()返回true之後,後續的onTouchEvent()不會觸發
把Config.java的修改如下:
public staticfinal boolean show_onTouch= false; // 顯示onTouch方法內容 public staticfinal boolean detail = true; // 顯示詳細的action內容 public staticfinal boolean show_main= false; // 顯示主要內容 public staticfinal boolean show_default= false; // 顯示默認返回值
設置如下返回值
MainActivity:
dispathTouchEvent(): super.dispathTouchEvent()
onTouchEvent(): super.onTouchEvent()
MyViewGroup:
dispathTouchEvent():true
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): false
onTouchEvent(): super.onTouchEvent()
MyView:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): false
onTouchEvent(): super.onTouchEvent()
ViewGroup的dispathTouchEvent()代碼如下:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } // boolean flag =super.dispatchTouchEvent(ev); // // if(Config.show_default) { // MyLog.log(getTag().toString() +" dispatchTouchEvent return: " + flag); // } // // return flag; return true; }
當viewgroup1的dispathcTouchEvent()返回true之後,代表dispathcTouchEvent()消費了改事件,那麼事件不會繼續往下投遞,所以後面的viewgroup2和view2都不會接受到此事件,另外一個對於viewgroup1來說,接下來的onTouchEvent()也不會執行,因為事件已經被消費掉了。
1.ViewGroup的dispathcTouchEvent()返回true,代表關注該事件,後續的touch事件由它處理。
在測試5的基礎上修改
MainActivity:
dispathTouchEvent(): super.dispathTouchEvent()
onTouchEvent(): super.onTouchEvent()
MyViewGroup:
dispathTouchEvent():false
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
MyView:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
當把ViewGroup的dispathTouchEvent()設置為false,表示當前的ViewGroup並不關心該事件,那麼下次事件的ACTION_MOVE和ACTION_UP它也不會處理,直接交給了MainActivity進行處理,但是當ACTION_UP觸發之後,表示一個完整事件已經完成,那麼當ACTION_DOWN再次觸發的時候,事件還是會投遞到ViewGroup,當ViewGroup的dispathTouchEvent()返回false,表示不關系此事件,那麼後續的ACTION_MOVE和ACTION_UP會直接分發給MainActivity進行處理。
1. 一個完成的觸摸事件一般包括ACTION_DOWN,ACTION_MOVE,ACTION_UP等,ACTION_MOVE,ACTION_UP在一個觸摸事件中不一定會出現。
2. ViewGroup的dispathTouchEvent()返回false,之後的action不會繼續投遞到該ViewGroup進行處理。這些action包括ACTION_MOVE,ACTION_UP等,直到下一次ACTION_DOWN事件出現
設置如下返回值
MainActivity:
dispathTouchEvent(): super.dispathTouchEvent()
onTouchEvent(): super.onTouchEvent()
MyViewGroup:
dispathTouchEvent():uper.dispathTouchEvent()
onInterceptTouchEvent():false
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
MyView:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
修改ViewGroup的代碼如下:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString()+ "dispatchTouchEvent return: " + flag); } return flag; // return false; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent"); } // return true; if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE"); return true; // break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP"); break; } } // boolean flag =super.onInterceptTouchEvent(ev); // if(Config.show_default) { // MyLog.log(getTag().toString() +" onInterceptTouchEvent return: " + flag); // } return false; }
流程和測試1保持默認值是一樣的。
在測試7的條件下設置如下:
MainActivity:
dispathTouchEvent(): super.dispathTouchEvent()
onTouchEvent(): super.onTouchEvent()
MyViewGroup:
dispathTouchEvent():uper.dispathTouchEvent()
onInterceptTouchEvent():true
onTouch(): super.onTouch()
onTouchEvent():true
MyView:
dispathTouchEvent(): super.dispathTouchEvent()
onInterceptTouchEvent():super.onInterceptTouchEvent()
onTouch(): super.onTouch()
onTouchEvent(): super.onTouchEvent()
由於viewgroup1的onInterceptTouchEvent()返回true,表示截取該事件,所以事件不會繼續向下投遞,直接有viewgroup1的onTouchEvent()進行處理,如果viewgroup1的
onTouchEvent()返回true,那麼下次觸摸事件還會繼續進入到viewgroup1,由viewgroup1進行處理。
1. onInterceptTouchEvent()方法表示是否事件,默認返回值是false,表示不攔截事件。
2. onInterceptTouchEvent()返回true表示攔截事件,事件不會繼續向下投遞,直接交給onTouchEvent()處理。
ViewGroup的onInterceptTouchEvent()返回true之後,表示事件被攔截,那麼事件將不會繼續分發下去,有沒有辦法設置父類不攔截事件呢,ViewGroup給我們提供了一個方法:
requestDisallowInterceptTouchEvent()方法,如果我們想父類不攔截touch事件,那麼只需要設置成requestDisallowInterceptTouchEvent(true)即可。
requestDisallowInterceptTouchEvent()這個方法不能在ViewGrop的dispathTouchEvent()裡面設置,因為ViewGroup在ACTION_DOWN時會把FLAG_DISALLOW_INTERCEPT設置為false,相當於之前設置的requestDisallowInterceptTouchEvent()並沒有起作用,這個時候如果事件被攔截,那麼事件將不會繼續分發到子View,事件還是會被攔截,相當於requestDisallowInterceptTouchEvent()設置並沒有起作用。
基於上述測試8代碼修改,把MyViewGroup修改為:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent"); } // return true; if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEventACTION_MOVE"); return true; // break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "onInterceptTouchEvent ACTION_UP"); break; } } // boolean flag =super.onInterceptTouchEvent(ev); // if(Config.show_default) { // MyLog.log(getTag().toString() +" onInterceptTouchEvent return: " + flag); // } return false; }
這裡只是在ACTION_MOVE截取了,子類不會接收到這個touch事件。
由於在ViewGroup代碼中設置了onInterceptTouchEvent()的ACTION_MOVE返回true,所以ACTION_MOVE後面的action子View都不會接受到,本次ACTION_MOVE就直接由ViewGroup的onTouchEvent()進行消費了,我們在onTouchEvent()返回true,那麼這個touch事件的其他action以後都由這個ViewGroup進行處理。onInterceptTouchEvent()在之後的action都不會觸發。
onInterceptTouchEvent()返回true會截取該事件,由截取該事件的View進行處理,而且onInterceptTouchEvent()在下次ACTION_DOWN來臨之前都不會重復調用。
在上述測試的基礎上修改MyView代碼:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if(Config.show_main) { MyLog.log(getTag().toString() + ": " + "dispatchTouchEvent"); } if(Config.detail) { switch(ev.getAction()) { case MotionEvent.ACTION_DOWN: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_DOWN"); getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: MyLog.log(getTag().toString()+ ": " + "dispatchTouchEvent ACTION_UP"); break; } } boolean flag = super.dispatchTouchEvent(ev); if(Config.show_default) { MyLog.log(getTag().toString() + " dispatchTouchEvent return: "+ flag); } return flag; }
和上述測試結果對比可以指,ViewGroup對ACTION_MOVE的處理還是會分發到子View,這是由於子View調用了getParent().requestDisallowInterceptTouchEvent(true);告訴父類不要截取事件,所以之後的action還是會分發到子View上。
requestDisallowInterceptTouchEvent(true)可以讓父控件不截取事件。
onTouch()觸發條件
添加onTouch()方法,在activity_main.xml中對viewgroup2和view2設置,加入如下代碼:
android:clickable="true"
但是我加入了之後還是不會觸發onTouch(),查看了源碼之後才發現新版本和舊版本原來存在差異,在新版本中代碼是這樣的:
ListenerInfoli = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; }
而這個ListenerInfo的初始化可以在setOnClickListener()設置,當然其他地方會有設置。
public void setOnClickListener(@Nullable OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }
所以可以給這個控件設置一個點擊監聽器,就可以出發onTouch()方法了。
在MainActivity中加入如下代碼:
mvgV2 = (MyViewGroup) findViewById(R.id.mvg_v2); mvV2 = (MyView) findViewById(R.id.mv_v2); mvgV2.setOnTouchListener(new MyTouchListener()); mvgV2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); mvV2.setOnTouchListener(new MyTouchListener()); mvV2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } });
1. 默認情況下dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()返回值都是false,表示不消費touch事件。
2.view的事件分發過程:dispatchTouchEvent()->onTouchEvent()
3.viewgroup事件分發過程:
dispatchTouchEvent()->onInterceptTouchEvent()->onTouchEvent()
4.對於View和ViewGroup來說,onTouch()的調用在onTouchEvent()之前。
5. 如果設置了View的clickable屬性,那麼onTouchEvent()返回true
6.返回值true表示關注此事件,如果一個View返回true,後續的事件都會分發給該View處理。
7.如果一個View返回false,表示不關注此事件,那麼該事件後續的action比如ACTION_MOVE,ACTION_UP都不會分發給該View。
8.onInterceptTouchEvent()用來判斷是否截取事件,默認不截取,如果返回true,表示截取該事件,那麼之後的touch事件直接交由該View的onTouchEvent()進行處理。
9.可以通過requestDisallowInterceptTouchEvent()設置父類是否截取事件,傳入true表示不截取事件。
10.requestDisallowInterceptTouchEvent()不能在ViewGroup的dispathTouchEvent中設置,因為ACTION_DOWN到來是會重新把FLAG_DISALLOW_INTERCEP設置成false。
最後附上一張流程圖:
測試代碼
Android應用三種線程 UI Thread Binder Thread Background Thread之間的區別與聯系UI Thread我們先來看一下App與Li
文件main.java復制代碼 代碼如下:package com.HHBrowser.android;import android.app.Activity;import
如何添加覆蓋物,實現周邊搜索,以及對覆蓋物的點擊出現介紹等效果。效果圖:我們的需求是,當用戶點擊衣食住行,或者對對附近搜索是,從服務器返回數據(經緯度,商家信息,介紹等)
由於項目需要,我們需要一個可以橫向滾動的,又可以豎向滾動的 表格。而且又要考慮大數據量(行)的展示視圖。經過幾天的研究終於搞定,做了一個演示。貼圖如下: &nb