看完你就瞭解了-Android事件分發機制

前言

懵懵懂懂搞了幾年安卓,對於事件分發這一塊,總是感覺沒有完全理解清楚。看了很多技術文章,自信感覺已經差不多了,然後一到面試,感覺總是說不出來。然後換了一套方式來理解,然後發現,事件分發也就是那麼回事。

舉例理解

舉一個例子來說明,某天你老闆看到個比較難的效果覺得很炫酷,然後就和產品經理隨口給派發了一個任務,產品經理把這個任務又給派發到組長。組長是個搞技術的,雖然自己有能力解決一些問題,但是發現自己有小弟,當仁不讓,任務給了程序員。然後程序員發現,整個技術部就我最小,我沒有小弟,然後只能苦逼的看任務。然後一看,發現自己搞不了。老老實實和組長說幹不了。然後組長也就一推四五六,就和產品經理說做不了,直接報告給老闆吧,這太難了。算了把,湊合湊合現在的也能用。老闆聽取到產品經理的建議以後放棄了任務。然後這次任務流程可以概括爲下面的圖。(ps:不經過任何攔截的流程)

上述圖是不經過任何攔截的流程,在事實上,也會有很多種可能:

  1. 老闆發佈的任務太難,產品經理一看,立即勸導老闆不要做這個,然後事件終止
  2. 組長是個比較會照顧人的或者說知道程序員是個新手幹不了,直接給完成了任務,然後反饋給產品經理,然後事件終止
  3. 程序員幹不了,又給組長,組長能力強給幹完了,然後反饋給產品經理,事件終止
  4. 程序員能力比較強直接給做了,然後報告給組長、組長又給產品經理然後然後事件終止了
  5. 程序員能力特強,完成以後給組長,組長一看直接給老闆,然後事件就到此終止了


上面描述了安卓的事件分發機制和處理機制,然後我們歸類一下方法
activity:
(1)dispatchTouchEvent                該方法用來分發事件,一般不會重寫這個方法 (給任務給組長)
(2)onTouchEvent                          用來處理事件,常見重寫(當返回爲true: 勸導老闆)


viewGroup:
(1)dispatchTouchEvent                該方法用來分發事件,一般不會重寫這個方法 (給任務給程序員)
(2)onInterceptTouchEvent           攔截事件(想不想做)
(2)onTouchEvent                         用來處理事件,常見重寫(當返回爲true: 自己做 | 接到程序員求助然後做)


view:
(1)dispatchTouchEvent               該方法用來分發事件,一般不會重寫這個方法 (判定自己是否能做)
(2)onTouchEvent                         用來處理事件,常見重寫(當返回爲true: 自己做)
 

代碼驗證

分別編寫activity、viewGroup、view進行驗證:

 

新建一個項目,先來寫ActivityA,代碼如下:

public class ActivityA extends AppCompatActivity {


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
       
        if (ev.getAction() == MotionEvent.ACTION_DOWN) { //判定是否爲點擊
            Log.e("測試--Down----->", "ActivityA:dispatchTouchEvent");
      
        } else if (ev.getAction() == MotionEvent.ACTION_UP) { //判定是否爲擡起
            Log.e("測試--UP------->", "ActivityA:dispatchTouchEvent");
        } else {
            Log.e("測試--Other---->", "ActivityA:dispatchTouchEvent");
        }
        return super.dispatchTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            Log.e("測試--Down----->", "ActivityA:onTouchEvent");
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            Log.e("測試--UP------->", "ActivityA:onTouchEvent");
        } else {
            Log.e("測試--Other---->", "ActivityA:onTouchEvent");
        }
        return super.onTouchEvent(event);
    }
}


同理,編寫ViewGroupB,如下

public class ViewGroupB extends LinearLayout {


    public ViewGroupB(Context context) {
        super(context);
    }

    public ViewGroupB(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ViewGroupB(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            Log.e("測試--Down----->","ViewGroupB:dispatchTouchEvent");
        } else if (ev.getAction() == MotionEvent.ACTION_UP) {
            Log.e("測試--UP------->","ViewGroupB:dispatchTouchEvent");
        } else {
            Log.e("測試--Other---->","ViewGroupB:dispatchTouchEvent");
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            Log.e("測試--Down----->","ViewGroupB:onInterceptTouchEvent");
        } else if (ev.getAction() == MotionEvent.ACTION_UP) {
            Log.e("測試--UP------->","ViewGroupB:onInterceptTouchEvent");
        } else {
            Log.e("測試--Other---->","ViewGroupB:onInterceptTouchEvent");
        }
        return super.onInterceptTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            Log.e("測試--Down----->","ViewGroupB:onTouchEvent");
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            Log.e("測試--UP------->","ViewGroupB:onTouchEvent");
        } else {
            Log.e("測試--Other---->","ViewGroupB:onTouchEvent");
        }
        return super.onTouchEvent(event);
    }
}

再編寫ViewC,代碼如下

public class ViewC extends View {


    public ViewC(Context context) {
        super(context);
    }

    public ViewC(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            Log.e("測試--Down----->","ViewC:dispatchTouchEvent");
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            Log.e("測試--UP------->","ViewC:dispatchTouchEvent");
        } else {
            Log.e("測試--Other---->","ViewC:dispatchTouchEvent");
        }
        return super.dispatchTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            Log.e("測試--Down----->","ViewC:onTouchEvent");
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            Log.e("測試--UP------->","ViewC:onTouchEvent");
        } else {
            Log.e("測試--Other---->","ViewC:onTouchEvent");
        }
        return super.onTouchEvent(event);
    }
}

將其寫入嵌套在一起,activity_test.xml。代碼如下

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorFE6016"
        android:gravity="center"
        android:orientation="vertical">

    <com.hysf.ym.custorm.meView.fenfa.ViewGroupB
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:background="@color/color7F6F51"
            android:gravity="center">

        <com.hysf.ym.custorm.meView.fenfa.ViewC
                android:layout_width="150dp"
                android:layout_height="150dp"
                android:background="@color/colorFD9C1A" />


    </com.hysf.ym.custorm.meView.fenfa.ViewGroupB>


</RelativeLayout>

得到的效果如下:

最外面橘色的是ActivityA,大一點的灰色的是ViewGroupB,中間偏黃色的是ViewC,現在我們點擊ViewC ,得到如下流程:
 

從打印的結果,我們可以很清楚到看到事件的流程:

      首先ActivityA得到了事件,由它的dispatchTouchEvent方法來分發事件,傳給了ViewGroupB,然後由於ViewGroupB的onInterceptTouchEvent方法沒有做出攔截,因此事件傳遞給了ViewC,而ViewC的onTouchEvent沒有處理,就又調用給ViewGroupB,繼ViewGroupB的onTouchEvent也沒有處理,然後傳給ActivityA的onTouchEvent方法,然後按下操作的流程就結束了,至於up事件又調用了ActivityA的dispatchTouchEvent和onTouchEvent,我們稍後再說。

實驗驗證:


ActivityA

測試1:單獨設置ActivityA的onTouchEvent方法返回值設置爲true,測試結果如下
 


測試2:單獨設置ActivityA的onTouchEvent方法返回值設置爲false,測試結果如下:

 ViewGroudB:

測試1:單獨設置ViewGroupB的onInterceptTouchEvent方法返回值設置爲true,測試結果如下:

 在測試1的基礎上,設置ViewGroupB的onTouchEvent方法返回值設置爲true,測試結果如下:
 

在測試1的基礎上,設置ViewGroupB的onTouchEvent方法返回值設置爲false,測試結果如下: 
 

 

 

測試2:單獨設置ViewGroupB的onInterceptTouchEvent方法返回值設置爲false,測試結果如下:

 在測試2的基礎上,設置ViewGroupB的onTouchEvent方法返回值設置爲true,測試結果如下:
 

在測試2的基礎上,設置ViewGroupB的onTouchEvent方法返回值設置爲false,測試結果如下: 
 

ViewC

測試1:單獨設置ViewC的onTouchEvent方法返回值設置爲true,測試結果如下
 


測試2:單獨設置ViewC的onTouchEvent方法返回值設置爲false,測試結果如下:

 

 總結:
 

  • ACTION_DOWN可以理解成所有事件的一種,或者理解成探路的
  • 當ACTION_DOWN判斷好具體是怎麼dispatch,怎麼消費的以後,對應的ACTION_MOVE和ACTION_UP就會按照消費的順序,進行執行
  • 當然,如果中間有攔截,也是按消費的路線進行
  • 對於事件的攔截,我們主要重寫就是OnInterceptTouchEvent和onTouchEvent方法。兩句就可以總結:

 (1)對於事件的傳遞,返回結果爲true,表示攔截,不再往下傳遞,爲false,不攔截,繼續往下傳遞。主要針對的就是                                  OnInterceptTouchEvent方法。
        (2)對於事件的處理,返回結果爲true,表示攔截,不再往上傳遞(即我處理的很完美,不需要你再來審覈我!),返回結果爲                      false(沒有成功處理事件),繼續向上傳遞。(onTouchEvent方法。)
   

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章