Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> View的事件分發

View的事件分發

編輯:關於Android編程

當你點擊一個view的時候,它的底層還有其他的View/ViewGroup,那麼這個點擊事件誰處理,它又是怎麼傳遞的在控件樹上?

我們知道點擊事件是從Activity->PhoneWindow->View/ViewGroup

點擊事件有三個非常重要的方法:dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent(View沒有onInterceptTouchEvent,ViewGroup才有)

dispatchTouchEvent:用於進行事件的分發,

onInterceptTouchEvent:用於攔截事件,可以攔截可以不攔截,在dispatchTouchEvent中調用

onTouchEvent:處理各種的點擊事件,在dispatchTouchEvent 中調用

這三個函數的關系:

 public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean comsume = false;
        if (onInterceptTouchEvent(ev)) {
            comsume = onTouchEvent(ev);
        }else {
            comsume = child.dispatchTouchEvent(ev);
        }
 reture comsume;}
 
對於一個ViewGroup來說,點擊事件產生之後會先傳給它(不管activity window先),然後ViewGroup的dispatchTouchEvent 會被調用,如果這ViewGroup攔截這個事件,這個ViewGroup的onTouchEvent會被調用,如果不攔截就會傳遞給它的子view,直到最後,假如最後是view的話,因為沒有onInterceptTouchEvent所以會直接調用onTouchEvent,假如這個view還是沒有消費這個事件,就會一層層往上調用onTouchEvent的方法,最後如果還是沒有消費的話會給回activity處理
小例子:
public class ViewGroupA extends LinearLayout{
    private static final String TAG = "ViewGroupA";
    public ViewGroupA(Context context) {
        super(context);
    }

    public ViewGroupA(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "ViewGroupA-----onInterceptTouchEvent: ");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "ViewGroupA -----onTouchEvent: ");
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        Log.d(TAG, "ViewGroupA-------dispatchTouchEvent: ");
        return super.dispatchTouchEvent(ev);
    }
}
ViewGroupB也是如此...不貼了

 

 

public class ViewC extends View{
    private static final String TAG = "ViewGroupA";
    public ViewC(Context context) {
        super(context);
    }

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

    public ViewC(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "ViewC onTouchEvent: ");
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "ViewC-----dispatchTouchEvent: ");
        return super.dispatchTouchEvent(ev);
    }
}



   
       

       

   


點擊ViewC打印的Log

 

 

09-04 02:10:21.996 1661-1661/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-04 02:10:21.996 1661-1661/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-04 02:10:21.996 1661-1661/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-04 02:10:21.996 1661-1661/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB-----onInterceptTouchEvent: 
09-04 02:10:21.996 1661-1661/com.example.jinxiong.viewaction D/ViewGroupA: ViewC-----dispatchTouchEvent: 
09-04 02:10:21.996 1661-1661/com.example.jinxiong.viewaction D/ViewGroupA: ViewC onTouchEvent: 
09-04 02:10:21.996 1661-1661/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------onTouchEvent: 
09-04 02:10:21.996 1661-1661/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA -----onTouchEvent: 
因為默認情況下。ViewGroup是不會攔截的,view在clickable 和 LongClickable都為false的情況下是不會消費事件的,這裡的就是不會消費的,所以onTouch方法都得到調用

 

這可以類比生活的事情,假設A是經理,B是組長,C是普通的職員,老總下發一個任務,經理覺得自己是管理者,這件事應該給機會下屬好好表現(onInterceptTouchEvent不攔截)所以就交給了組長B了,組長手上也有幾個人在管理著,覺得這事交個小的去辦就好了(onInterceptTouchEvent不攔截),所以這個任務就落到了C的身上,然後C發現自己做不了,太難了。就交回給組長B來處理,組長B也發現這件事太難了,完成不了,所以就交回個經理A

任務分發的時候是大佬到小弟,做事的時候就是小弟到大佬,大佬只會做一些小弟做不了的事情微笑

假如有一天經理大發慈悲,覺得這事還是親歷親為好,就把任務攔截下來,那麼下面的組長和普通職員就不會知道這件事,也沒法去做件事了

 

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "ViewGroupA-----onInterceptTouchEvent: ");
        return true;
    }
09-04 03:40:24.996 16657-16657/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-04 03:40:24.996 16657-16657/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-04 03:40:24.996 16657-16657/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA -----onTouchEvent: 
某一天組長或許也會這樣.....

 

當有一天普通職員能完成任務了,不需要組長,經理動手了

 

09-04 03:43:47.434 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-04 03:43:47.434 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-04 03:43:47.434 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-04 03:43:47.434 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB-----onInterceptTouchEvent: 
09-04 03:43:47.434 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewC-----dispatchTouchEvent: 
09-04 03:43:47.434 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewC onTouchEvent: 
09-04 03:43:47.522 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-04 03:43:47.522 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-04 03:43:47.522 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-04 03:43:47.522 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB-----onInterceptTouchEvent: 
09-04 03:43:47.522 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewC-----dispatchTouchEvent: 
09-04 03:43:47.522 20158-20158/com.example.jinxiong.viewaction D/ViewGroupA: ViewC onTouchEvent: 
                                                                             
一個是down事件,一個是up事件這裡

 

onTouchEvent ,setOnTouchListener ,setOnClickListener介紹

默認情況下,View不消費事件(clickable和longclickable都是false),onTouchEvent 返回的是false,當我在這個三個地方志設置了相關事件處理的邏輯,那麼View會執行哪一個尼,這裡就涉及到這三個優先級的問題。

舉個例子說明:

 

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewC viewC = (ViewC) this.findViewById(R.id.viewc);
//        Log.d(TAG, "isClickable: " + viewC.isClickable());
//        Log.d(TAG, "isLongClickable: " + viewC.isLongClickable());
//        viewC.setLongClickable(true);
//        viewC.setClickable(true);

        viewC.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.d(TAG, "ViewC---- OnTouchListener");
                return false ;
            }
        });
        viewC.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "ViewC---- OnClickListener");
            }
        });

    }
}
ViewC

 

 @Override
    public boolean onTouchEvent(MotionEvent event) {
//        Log.d(TAG, "ViewC onTouchEvent: ");
        boolean consume = super.onTouchEvent(event);
        Log.d(TAG, "ViewC onTouchEvent: " + consume);
        return consume;
    }
點擊ViewC log(down 和 up 事件 ,click事件是down和up都出現才為一個click事件)

 

 

09-04 04:27:19.169 27472-27472/com.example.jinxiong.viewaction D/ViewGroupA: ViewC---- OnTouchListener
09-04 04:27:19.169 27472-27472/com.example.jinxiong.viewaction D/ViewGroupA: ViewC onTouchEvent: true
09-04 04:27:19.269 27472-27472/com.example.jinxiong.viewaction D/ViewGroupA: ViewC---- OnTouchListener
09-04 04:27:19.269 27472-27472/com.example.jinxiong.viewaction D/ViewGroupA: ViewC onTouchEvent: true
09-04 04:27:19.269 27472-27472/com.example.jinxiong.viewaction D/ViewGroupA: ViewC---- OnClickListener
可以看到OntouchListener是先被執行的,這裡返回的OntouchListener 返回的是false,如果返回的是true,那麼只會打印兩個OntouchListener

 

然後才會去執行onTouchEvent ,可以看到還打印出來true ,這裡的值是因為我們setOnClickListener設置了,而這之後沒有執行setOnClickListener是因為只有一個down事件,

當up事件發生之後就會再一次執行,並且setOnClickListener得到執行,(其實用onTouchEvent 來監聽click事件的話,最好還是要實現另外的一個方法performClick())

\

事件序列

同一個事件序列是指從手機接觸屏幕到離開屏幕的那一個過程所產生的一系列事件,就是說,這個事件序列從down事件開始,經過0到多個move事件,到up事件結束。

以ViewGroup作為操作

①攔截全部,全部消耗

 

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "ViewGroupB------onTouchEvent: ");
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN: {
                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_DOWN");
                return true;
            }
            case MotionEvent.ACTION_MOVE: {
                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_MOVE");
                return true;
            }
            case MotionEvent.ACTION_UP: {
                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_UP");
                return true;
            }


        }
        return true;
    }

 

Log:

 

09-05 06:41:31.387 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-05 06:41:31.387 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-05 06:41:31.387 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-05 06:41:31.387 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB-----onInterceptTouchEvent: 
09-05 06:41:31.387 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------onTouchEvent: 
09-05 06:41:31.387 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------: 我消耗了ACTION_DOWN
09-05 06:41:31.471 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-05 06:41:31.471 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-05 06:41:31.471 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-05 06:41:31.471 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------onTouchEvent: 
09-05 06:41:31.471 14857-14857/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------: 我消耗了ACTION_UP

 

結論:ViewC的方法得不到調用,down事件中onInterceptTouchEvent事件得到調用,而跟他同一個事件序列的up事件就不會再次調用onInterceptTouchEvent,

boss有 A ,B 兩個相關聯的任務,把他分兩次給經理,當A完成之後才給B,經理A把兩個相關聯的任務分給組長B,組長B覺得自己可以完成,就攔截了其中的一個並且做完,剩下的一個不用問攔截不攔截,都必須要組長B完成,因為這兩個任務是相關的(處於同一個的事件序列),這裡就沒有C職員的事情了

②攔截全部,消耗一部分

①消耗down,其余不消耗

 

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "ViewGroupB------onTouchEvent: ");
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN: {
                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_DOWN");
                return true;
            }
//            case MotionEvent.ACTION_MOVE: {
//                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_MOVE");
//                return true;
//            }
//            case MotionEvent.ACTION_UP: {
//                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_UP");
//                return true;
//            }


        }
        return false;
    }

 

Log

 

09-05 07:02:21.223 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-05 07:02:21.223 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-05 07:02:21.223 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-05 07:02:21.223 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB-----onInterceptTouchEvent: 
09-05 07:02:21.223 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------onTouchEvent: 
09-05 07:02:21.223 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------: 我消耗了ACTION_DOWN
09-05 07:02:21.315 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-05 07:02:21.315 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-05 07:02:21.315 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-05 07:02:21.315 1444-1444/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------onTouchEvent: 

 

結論:ViewC的方法得不到調用,down事件中onInterceptTouchEvent事件得到調用,而跟他同一個事件序列的up事件就不會再次調用onInterceptTouchEvent,那麼up事件誰處理了,答案是被boss activity處理了

boss有 A ,B 兩個相關聯的任務,把他分兩次給經理,當A完成之後才給B,經理A分給兩個任務給組長B,一個重要的down,一個次要的其他,兩個事件是相關的,(後面會說為什麼一個重要的,一個不重要的),組長B覺得自己可以做就將重要的事件攔截下來,自己做,不需要職員C做,並且做完這件重要的事件,但是後面次要的事件組長卻沒有完成了,經理以為都完成了所以沒自己動手,直接給boss,boss一看就自己做了沒做的那部分

②不消耗down,其余消耗(move,up)

ViewGroupB

 

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "ViewGroupB------onTouchEvent: ");
        switch (event.getAction()) {

//            case MotionEvent.ACTION_DOWN: {
//                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_DOWN");
//                return true;
//            }
            case MotionEvent.ACTION_MOVE: {
                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_MOVE");
                return true;
            }
            case MotionEvent.ACTION_UP: {
                Log.d(TAG, "ViewGroupB------: 我消耗了ACTION_UP");
                return true;
            }


        }
        return false;
    }
ViewGroupA

 

 

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "ViewGroupA -----onTouchEvent: ");
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN: {
                Log.d(TAG, "ViewGroupA------: 我是ACTION_DOWN");
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                Log.d(TAG, "ViewGroupA------: 我是ACTION_MOVE");
                break;
            }
            case MotionEvent.ACTION_UP: {
                Log.d(TAG, "ViewGroupA------: 我是ACTION_UP");
                break;
            }


        }
        boolean flag = super.onTouchEvent(event);
        Log.d(TAG, "onTouchEvent: "+flag);
        return flag;
    }
Log:

 

 

09-05 07:08:27.959 6960-6960/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-05 07:08:27.959 6960-6960/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-05 07:08:27.959 6960-6960/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-05 07:08:27.959 6960-6960/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB-----onInterceptTouchEvent: 
09-05 07:08:27.959 6960-6960/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------onTouchEvent: 
09-05 07:08:27.959 6960-6960/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA -----onTouchEvent: 
09-05 07:08:27.959 6960-6960/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA------: 我是ACTION_DOWN
09-05 07:08:27.959 6960-6960/com.example.jinxiong.viewaction D/ViewGroupA: onTouchEvent: false

 

結論:可以看到ViewGroupA被調用了,但是這裡設置了ViewGroupA並沒有消耗任何事件,所以ViewGroupA只是調用了一次,這裡不討論ViewGroupA

boss有 A ,B 兩個相關聯的任務,把他分兩次給經理,當A完成之後才給B,

上面說過為什麼經理分給組長B一件重要的事件down,一件次要的up或其他,這次組長也是覺得自己能做完經理給的重要任務,所以就攔截下來了,但是沒想到這件重要的事件居然完成不了,好了,經理就只好自己動手,但是經理也是沒完成(可以設置為完成(消耗),這裡我已沒完成做例子),就給回daboss activity ,所以它就來處理了,然後還有一個次要的任務,因為經理都沒有完成重要的事情,boss就不給次要人物給經理了,但如果剛剛我們的經歷完成了重要的事情,那麼boss還是會給次要的事情給經理的,但是經理不會再那麼笨給組長B了,因為組長剛剛都沒好好完成重要任務那麼在這個例子中,因為整體所有的事情都沒有人處理,所以boss接手所有的事件處理

從這裡我們知道down事件是個很特殊的事件,如果攔截事情但是沒處理這個down事件的話,同一個序列的剩下的事件你就沒資格處理了,都是給你的上級處理,上一層的ViewGroup(控制樹視圖的上一層)

③攔截全部,全部不消耗

 

09-05 07:38:19.723 1815-1815/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-------dispatchTouchEvent: 
09-05 07:38:19.723 1815-1815/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA-----onInterceptTouchEvent: 
09-05 07:38:19.723 1815-1815/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------dispatchTouchEvent: 
09-05 07:38:19.723 1815-1815/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB-----onInterceptTouchEvent: 
09-05 07:38:19.723 1815-1815/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupB------onTouchEvent: 
09-05 07:38:19.723 1815-1815/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA -----onTouchEvent: 
09-05 07:38:19.723 1815-1815/com.example.jinxiong.viewaction D/ViewGroupA: ViewGroupA------: 我是ACTION_DOWN
09-05 07:38:19.723 1815-1815/com.example.jinxiong.viewaction D/ViewGroupA: onTouchEvent: false
結論:你把所有攔截下來,影響的只是你的子view ,它永遠沒有機會得到事件處理的機會,你全部不處理,那麼你的父View就會接手,但是父view消耗不消耗這件事就看情況,父view不消耗的話,就往上傳,最終還是bossactivity處理。

 

因為攔截序列的其中一個事件,那麼剩下的事件都是默認交給這個攔截的view處理的,所以下面的情況都是跟上面攔截全部事件是一樣的

④攔截一部分,消耗全部

⑤攔截一部分,消耗一部分

⑥攔截一部分,全部不消耗

 

⑦不攔截,有機會消耗

①消耗全部

②消耗一部分,剩下的就會給activity消耗處理

③不消耗 ,全部都是activity消耗

⑧不攔截,沒機會消耗

總結:

事件如果被攔截了,如果你只是處理了down事件,那麼剩下的up,move 事件就交由activity處理,如果你down事件沒有處理,那麼這一事件序列將交由你的父view處理,

一個事件序列只能有一個view來消耗,但是你也可以在你當前view的事件處理中通過父/子view 的對象調用他們的onTouchEvent來打破這個規矩,

如果你攔截的只是一個down事件,那麼剩下的這一個序列事件的move,up都會交給你這個view處理,並且剩下的事件中機即move,up事件將不會調用到onInterceptTouchEvent這個方法,

ViewGroup默認是不攔截事件的,View中沒有onInterceptTouchEvent這個方法的,收到事件後是直接調用OnTouchEvent的,當一個View的clickable/longclickable中其中一個為true,那麼就會消耗事件

一個onClick事件是收到down和up事件,但你設置一個clickListener 這個view的clickable就會變為true

事件總是由父view分發給子view,可以通過requestDisallowInterceptTouchView藍干預父view 的事件分發,但是不能干預down事件

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved