Android的Touch事件分發機制簡單探析


 轉自https://www.cnblogs.com/net168/p/4165970.html

前言

  Android中關於觸摸事件的分發傳遞是一個很值得研究的東西。曾不見你引入了一個ListView的滑動功能,ListView就不聽你手指的指喚來滾動了;也不知道為啥Button設置了onClick和onTouch,其中誰會先響應;或許你會問onTouch和onTouchEvent有什么區別,又該如何使用?這里一切的一切,只要你了解了事件分發機制,你會發現,解釋這都不是事兒!

相關Touch事件的方法

1、public boolean dispatchTouchEvent(MotionEvent ev)                ————事件分發方法,分發Event所調用

2、public boolean onInterceptTouchEvent(MotionEvent ev)    ————事件攔截方法,攔截Event所調用

3、public boolean onTouchEvent(MotionEvent event)       ————事件響應方法,處理Event所調用

擁有上述事件的類

1、Activity類(Activity及其各種繼承子類)

    dispatchTouchEvent()、onTouchEvent()

2、ViewGroup類(LinearLayout、FrameLayout、ListView等.....)

    dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()

3、View類(Button、TextView等.....)

    dispatchTouchEvent()、onTouchEvent()

PS:需要特別注意一點就是ViewGroup中額外擁有onInterceptTouchEvent()方法,其他兩個方法為這三種類所共同擁有。

方法的簡單用途解析

我們可以發現這三個方法的返回值都為boolean類型,其實它們就是通過返回值來決定下一步的傳遞處理方向。

1、dispatchTouchEvent()  ——用來分發事件所用

  該方法會將根元素的事件自上而下依次分發到內層子元素中,直到被終止或者到達最里層元素,該方法也是采用一種隧道方式來分發。在其中會調用onInterceptTouchEvent()和onTouchEvent(),一般不會去重寫。

  返回false則不攔截繼續往下分發,如果返回true則攔截住該事件不在向下層元素分發,在dispatchTouchEvent()方法中默認返回false。

2、onInterceptTouchEvent()  ——用來攔截事件所用

  該方法在ViewGroup源代碼中實現就是返回false不攔截事件,Touch事件就會往下傳遞給其子View。

  如果我們重寫該方法並且將其返回true,該事件將會被攔截,並且被當前ViewGroup處理,調用該類的onTouchEvent()方法。

3、onTouchEvent()  ——用來處理事件

  返回true則表示該View能處理該事件,事件將終止向上傳遞(傳遞給其父View)

  返回false表示不能處理,則把事件傳遞給其父View的onTouchEvent()方法來處理

實戰演練

  好了,基礎知識講完了,上面的東西看不懂不要緊,現在要睜大眼睛好好看了,因為下面開始用例子講解Touch的事件分發機制,相信能讓大家更好的理解這個分發機制。

在這個例子中,我們需要重寫四個類:

1、老板  ——>  MyActivity

2、經理  ——>  FrameLayout

3、組長  ——>  LineaLayout

4、員工  ——>  TextView

 軟件界面圖如下:

搞個通俗易懂的例子開開頭

老板對部長說:我要吃紅燒魚
部長對經理說:你做個紅燒魚
經理對組長說:你做個紅燒魚
組長對員工說:你做個紅燒魚
    ……(員工做呀做,沒把魚燒出來)
員工說:我都通宵加班了還是沒做出來,別扣我工資行嗎
組長說:你個笨蛋,白發你工資,下次不找你了,我自己搞
    ……(組長做呀做,還是沒做出來)
組長對經理說:我盡力了,還是沒做出來,千萬別炒我魷魚呀
經理說:你個廢物,這點都做不好,只能我自己來做了
    ……(經理做呀做,做成功了)
經理對部長說:紅燒魚做好了
部長回應說:不錯,下次有事還找你
部長對老板說:紅燒魚做好了
老板回應說:不錯,下次有事還找你
---------------------------
老板對部長說:我要吃水煮魚
部長對經理說:你做個水煮魚
經理自己想想:組長連紅燒魚都搞不定,這次就不找他了,我自己親自來做
  ……(經理做呀做,又成功了)
經理對部長說:紅燒魚做好了
部長回應說:不錯,下次有事還找你
部長對老板說:紅燒魚做好了
老板回應說:不錯,下次有事還找你
---------------------------

1、按常理,領導都會把任務向下分派,一旦下面的人把事情做不好,就不會再把后續的任務交給下面的人來做了,只能自己親自做,如果自己也做不了,就只能告訴上級不能完成任務,上級又會重復他的過程。

2、另外,領導都有權利攔截任務,對下級隱瞞該任務,而直接自己去做,如果做不成,也只能向上級報告不能完成任務。

流程演示

【1】、我們假設員工能力不足,也就是將TextView的onTouchEvent()方法設置返回false,表示其不能消費該事件。

事件傳遞的流程:

【2】、我們假設員工能處理該事件,也就是將TextView的onTouchEvent()方法設置返回true,表示其能處理該事件。

事件傳遞的流程:

【3】、我們假設員工和組長能力不足,即TextView和LinearLayout的onTouch()返回false,但是經理解決了該問題,即FrameLayout的onTouch()返回true

 

事件傳遞的流程:

【4】假設我們的組長在事件分發到他那里的時候,決定攔截下來不交給下面的員工,也就是onInterceptTouchEvent()返回為true,並且他也成功完成了任務,即onTouchEvent()返回值為true。

事件傳遞的流程:

做個小結

1、很明顯,這些流程就是dispatchTouchEvent()的處理結果,但是前提是我們不去完全的重新實現這個方法,也就是保證需要return super.dispatchTouchEvent(ev);來確定父類的方法有被調用。而這些事件將會由上而下的逐層傳遞,直到傳遞到最底層的View元素,此時將會調用該View的onTouchEvent()方法來處理該事件;返回true來表示對該事件已經成功處理,如果返回false則並沒有成功處理事件,將會把事件逐層向上傳遞,交給上層View的onTouchEvent()方法處理,以此類推,直至某一View成功處理該事件,或者到頂層View處理仍然返回false則放棄對該事件處理,事件消失。

2、如果在事件向下傳遞的過程中,被中途攔截,也就是View的onInterceptTouchEvent()方法返回true,那么該事件將停止向下傳遞,並交給該層的onTouchEvent()方法處理,無論處理成功與否,底層View將再也不會接收到該事件。PS:若處理失敗,則會交由上層View的onTouchEvent()方法處理。

3、dispatchTouchEvent()具有記憶的功能,如果第一次事件向下傳遞到某View,它把事件繼續傳遞交給它的子View,它會記錄該事件是否被它下面的View給處理成功了,(怎么能知道呢?如果該事件會再次被向上傳遞到我這里來由我的onTouchEvent()來處理,那就說明下面的View都沒能成功處理該事件);當第二次事件向下傳遞到該View,該View的dispatchTouchEvent()方法機會判斷,若上次的事件由下面的view成功處理了,那么這次的事件就繼續交給下面的來處理,若上次的事件沒有被下面的處理成功,那么這次的事件就不會向下傳遞了,該View直接調用自己的onTouchEvent()方法來處理該事件。

4、記憶功能的信息只在一系列事件完成之前有效,如從ACTION_DOWN事件開始,直到后續事件ACTION_MOVE,ACTION_UP結束后,“記憶”的信息就會清除。也就是說如果某View處理ACTION_DOWN事件失敗了(onTouchEvent()返回false),那么后續的ACTION_MOVE,ACTION_UP等事件就不會再傳遞到該View了,由其父View自己來處理。在下一次發生ACTION_DOWN事件的時候,還是會傳遞到該View的。

附帶代碼

1、MyActivity

復制代碼
public class MyActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("test", "【老板】下達任務:" + Util.actionToString(ev.getAction()) + ",找個人幫我完成,任務往下分發。"); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { boolean relust = false; Log.i("test", "【老板】完成任務:" + Util.actionToString(event.getAction()) + ",【經理】太差勁了,以后不再找你干活了,我自來搞定!是否解決:" + Util.canDoTaskTop(relust)); return relust; } }
復制代碼

2、MyFrameLayout:

復制代碼
public class MyFrameLayout extends FrameLayout { public MyFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("test", "【經理】下達任務:" + Util.actionToString(ev.getAction()) + ",找個人幫我完成,任務往下分發。"); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean relust = false; Log.i("test", "【經理】是否攔截任務:" + Util.actionToString(ev.getAction()) + ",攔下來?" + relust); return relust; } @Override public boolean onTouchEvent(MotionEvent event) { boolean relust = true; Log.i("test", "【經理】完成任務:" + Util.actionToString(event.getAction()) + ",【組長】太差勁了,以后不再找你干活了,我自來搞定!是否解決:" + Util.canDoTask(relust)); return relust; } }
復制代碼

3、MyLinearLayout

復制代碼
public class MyLinearLayout extends LinearLayout { public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("test", "【組長】下達任務:" + Util.actionToString(ev.getAction()) + ",找個人幫我完成,任務往下分發。"); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean relust = true; Log.i("test", "【組長】是否攔截任務:" + Util.actionToString(ev.getAction()) + ",攔下來?" + relust); return relust; } @Override public boolean onTouchEvent(MotionEvent event) { boolean relust = true; Log.i("test", "【組長】完成任務:" + Util.actionToString(event.getAction()) + ",【員工】太差勁了,以后不再找你干活了,我自來搞定!是否解決:" + Util.canDoTask(relust)); return relust; } }
復制代碼

4、MyTextView

復制代碼
public class MyTextView extends TextView { public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub  } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.i("test", "【員工】下達任務:" + Util.actionToString(event.getAction()) + ",我沒手下了,唉~自己干吧"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { boolean relust = false; Log.i("test", "【員工】完成任務:" + Util.actionToString(event.getAction()) + ",【員工】現在只能靠自己了!是否解決:" + Util.canDoTask(relust)); return relust; } }
復制代碼

5、Util(工具類)

復制代碼
public class Util { public static String actionToString(int action){ String result = null; switch(action){ case MotionEvent.ACTION_DOWN: result = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: result = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: result = "ACTION_UP"; break; } return result; } public static String canDoTask(boolean can){ String result = null; if(can){ result = "完美解決該任務!"; } else{ result = "這活搞不定,交給老大完成吧。"; } return result; } public static String canDoTaskTop(boolean can){ String result = null; if(can){ result = "完美解決該任務!"; } else{ result = "這活搞不定,放棄該任務。"; } return result; } }
復制代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM