[java] Android:onInterceptTouchEvent和dispatchTouchEvent的區別?


Answers

因為這是Google的第一個結果。 我想與大家分享Dave Smith在Youtube上的精彩演講:掌握Android Touch系統和幻燈片,請here 。 它讓我對Android Touch系統有了很深入的了解:

活動如何處理觸摸:

  • Activity.dispatchTouchEvent()
    • 總是先被調用
    • 將事件發送到附加到窗口的根視圖
    • onTouchEvent()
      • 如果沒有視圖消耗事件,則調用
      • 總是最後被調用

視圖如何處理觸摸:

  • View.dispatchTouchEvent()
    • 如果存在,首先將事件發送給偵聽器
      • View.OnTouchListener.onTouch()
    • 如果不消耗,則處理觸摸本身
      • View.onTouchEvent()

ViewGroup如何處理觸摸:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • 檢查它是否應取代兒童
      • ACTION_CANCEL傳遞給活動的孩子
      • 返回一次,消耗所有後續事件
    • 對於每個子視圖,按相反順序添加它們
      • 如果觸摸相關(視圖內),則child.dispatchTouchEvent()
      • 如果以前未處理,則分派到下一個視圖
    • 如果沒有孩子處理事件,聽眾有機會
      • OnTouchListener.onTouch()
    • 如果沒有聽眾,或沒有處理
      • onTouchEvent()
  • 截獲的事件跳過子步驟

他還在github.com/devunwired/上提供自定義觸摸示例代碼。

答案:基本上, dispatchTouchEvent()會在每個View圖層上調用,以確定View是否對正在進行的手勢感興趣。 在ViewGroupViewGroup可以在dispatchTouchEvent()方法中竊取touch事件,然後調用children的dispatchTouchEvent()ViewGroup只會在ViewGroup onInterceptTouchEvent()方法返回true時停止調度。 不同之處在於dispatchTouchEvent()調度onInterceptTouchEventonInterceptTouchEvent告訴它是否應該攔截(不派發MotionEvent給兒童)或不調度(派發給兒童)

你可以想像一個ViewGroup代碼或多或少地做了這個(非常簡化):

public boolean dispatchTouchEvent(MotionEvent ev) {
    if(!onInterceptTouchEvent()){
        for(View child : children){
            if(child.dispatchTouchEvent(ev))
                return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}
Question

Android中的onInterceptTouchEventdispatchTouchEvent什麼區別?

根據android開發人員指南,這兩種方法都可以用來攔截觸摸事件( MotionEvent ),但有什麼不同?

onInterceptTouchEventdispatchTouchEventonTouchEvent如何在視圖層次結構( ViewGroup )內一起交互?




public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume =false;
    if(onInterceptTouchEvent(ev){
        consume = onTouchEvent(ev);
    }else{
        consume = child.dispatchTouchEvent(ev);
    }
}



ViewGroup的onInterceptTouchEvent()始終是發生第一個事件的ACTION_DOWN事件的入口點。

如果您希望ViewGroup處理此手勢,請從onInterceptTouchEvent()返回true。 在返回true時,ViewGroup的onTouchEvent()將接收所有後續事件,直到下一個ACTION_UPACTION_CANCEL ,並且在大多數情況下, ACTION_DOWNACTION_UPACTION_CANCEL之間的觸摸事件是ACTION_MOVE ,通常會將其識別為滾動/ ACTION_MOVE手勢。

如果您從onInterceptTouchEvent()返回false, onInterceptTouchEvent()目標視圖的onTouchEvent() 。 對於隨後的消息,它將被重複,直到從onInterceptTouchEvent()返回true。

來源: http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html : http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html




補充答案

以下是對其他答案的一些視覺補充。 我的完整答案在here 。

dispatchTouchEvent()方法使用onInterceptTouchEvent()來選擇是否立即處理觸摸事件(使用onTouchEvent() )或繼續通知其子項的dispatchTouchEvent()方法。




dispatchTouchEvent在onInterceptTouchEvent之前處理。

使用這個簡單的例子:

   main = new LinearLayout(this){
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            System.out.println("Event - onInterceptTouchEvent");
            return super.onInterceptTouchEvent(ev);
            //return false; //event get propagated
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            System.out.println("Event - dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
            //return false; //event DONT get propagated
        }
    };

    main.setBackgroundColor(Color.GRAY);
    main.setLayoutParams(new LinearLayout.LayoutParams(320,480));    


    viewA = new EditText(this);
    viewA.setBackgroundColor(Color.YELLOW);
    viewA.setTextColor(Color.BLACK);
    viewA.setTextSize(16);
    viewA.setLayoutParams(new LinearLayout.LayoutParams(320,80));
    main.addView(viewA);

    setContentView(main);

你可以看到日誌會像:

I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent

因此,如果您正在處理這2個處理程序,請使用dispatchTouchEvent在第一個實例上處理該事件,該事件將轉至onInterceptTouchEvent。

另一個區別是,如果dispatchTouchEvent返回'false',則事件不會傳播給子代,在本例中為EditText,而如果您在onInterceptTouchEvent中返回false,則該事件仍然會派發到EditText




ViewGroup子類中的以下代碼將阻止其父容器接收觸摸事件:

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    // Normal event dispatch to this container's children, ignore the return value
    super.dispatchTouchEvent(ev);

    // Always consume the event so it is not dispatched further up the chain
    return true;
  }

我用這個自定義覆蓋來防止背景視圖響應觸摸事件。




小答案:

onInterceptTouchEvent在setOnTouchListener之前。




Related