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

前言ide

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

相關Touch事件的方法spa

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

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

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

擁有上述事件的類繼承

一、Activity類(Activity及其各類繼承子類)事件

    dispatchTouchEvent()、onTouchEvent()get

二、ViewGroup類(LinearLayout、FrameLayout、ListView等.....)博客

    dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()

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

    dispatchTouchEvent()、onTouchEvent()

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

方法的簡單用途解析

咱們能夠發現這三個方法的返回值都爲boolean類型,其實它們就是經過返回值來決定下一步的傳遞處理方向。

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

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

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

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

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

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

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

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

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

實戰演練

  好了,基礎知識講完了,上面的東西看不懂沒關係,如今要睜大眼睛好好看了,由於下面開始用例子講解Touch的事件分發機制,相信能讓你們更好的理解這個分發機制。

在這個例子中,咱們須要重寫四個類:

一、老闆  ——>  MyActivity

二、經理  ——>  FrameLayout

三、組長  ——>  LineaLayout

四、員工  ——>  TextView

 軟件界面圖以下:

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

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

一、按常理,領導都會把任務向下分派,一旦下面的人把事情作很差,就不會再把後續的任務交給下面的人來作了,只能本身親自作,若是本身也作不了,就只能告訴上級不能完成任務,上級又會重複他的過程。

二、另外,領導都有權利攔截任務,對下級隱瞞該任務,而直接本身去作,若是作不成,也只能向上級報告不能完成任務。

流程演示

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

事件傳遞的流程:

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

事件傳遞的流程:

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

 

事件傳遞的流程:

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

事件傳遞的流程:

作個小結

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

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

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

四、記憶功能的信息只在一系列事件完成以前有效,如從ACTION_DOWN事件開始,直到後續事件ACTION_MOVE,ACTION_UP結束後,「記憶」的信息就會清除。也就是說若是某View處理ACTION_DOWN事件失敗了(onTouchEvent()返回false),那麼後續的ACTION_MOVE,ACTION_UP等事件就不會再傳遞到該View了,由其父View本身來處理。在下一次發生ACTION_DOWN事件的時候,仍是會傳遞到該View的。

附帶代碼

一、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;
    }

}

二、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;
    }
}

三、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;
    }
}

四、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;
    }
}

五、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;
    }
}

 

做者:enjoy風鈴
出處:http://www.cnblogs.com/net168/
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然下次不給你轉載了

相關文章
相關標籤/搜索