Android 事件分发机制

Android 事件分发机制

口诀: 3个主体6件事,3个流程负责制

  • 三个主体: Activity , ViewGroup, View
  • 六件事:dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent,requestDisallowInterceptTouchEvent,onTouchListener,onClickListener
  • 三个流程: MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP
  • 负责制:负责到底只要事处理了流程中的ACTION_DOWN的事件的主体return true,后面的整个都要交给它处理

流程图:https://www.jianshu.com/p/e99b5e8bd67b

流程图的解释请参考,参考的说明里面:https://www.jianshu.com/p/e99b5e8bd67b
这里需要注意的: Android 事件分发机制

  • 1: activity 如果dispatchTouchEvent不是return的super 那么都会丢弃,不会再往下传递,也不会触发onTouchEvent的事件
  • 2: 执行优先级 onTouchListener >onTouchEvent>onClickListener 如果onTouchListener 的回调onTouch里面 返回的是true那么不会执行onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
            
   case MotionEvent.ACTION_UP:
...
    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
        // This is a tap, so remove the longpress check
    removeLongPressCallback();

     // Only perform take click actions if we were in the pressed state
     if (!focusTaken) {
        // Use a Runnable and post this rather than calling
        // performClick directly. This lets other visual state
        // of the view update before click actions start.
        if (mPerformClick == null) {
            mPerformClick = new PerformClick();
        }
        if (!post(mPerformClick)) {
            performClickInternal();
        }
    }
}
    ...
}
mMyView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("kodulf","View on click listener");
            }
        });

        mMyView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.d("kodulf","View on touch listener");
                return false;
            }
        });
执行结果:
 D/kodulf: part: =============Activity method:[-dispatchTouchEvent -] action:DOWN 
 D/kodulf: part: -- LinearLayout -- method:[- dispatchTouchEvent  -] action:DOWN 
 D/kodulf: part: -- LinearLayout -- method:[-MotionEvent -] action:DOWN 
 D/kodulf: part: -- LinearLayout -- method:[- dispatchTouchEvent  -] action:DOWN 
 D/kodulf: part: -- LinearLayout -- method:[-MotionEvent -] action:DOWN 
 D/kodulf: part: ++++ My View  method:[- dispatchTouchEvent  -] action:DOWN 
 D/kodulf: View on touch listener
 D/kodulf: part: ++++ My View  method:[-onTouchEvent -] action:DOWN 
 D/kodulf: part: =============Activity method:[-dispatchTouchEvent -] action:UP 
 D/kodulf: part: -- LinearLayout -- method:[- dispatchTouchEvent  -] action:UP 
 D/kodulf: part: -- LinearLayout -- method:[-MotionEvent -] action:UP 
 D/kodulf: part: -- LinearLayout -- method:[- dispatchTouchEvent  -] action:UP 
 D/kodulf: part: -- LinearLayout -- method:[-MotionEvent -] action:UP 
 D/kodulf: part: ++++ My View  method:[- dispatchTouchEvent  -] action:UP 
 D/kodulf: View on touch listener
 D/kodulf: part: ++++ My View  method:[-onTouchEvent -] action:UP 
 D/kodulf: View on click listener    
  • 3: requestDisallowInterceptTouchEvent 这个必须是使用getParent().requestDisallowInterceptTouchEvent. 但是它对ACTION_DOWN 是无效的,因为如果没有ACTION_DOWN传递过来的话,它怎么执行呢?

实例

1.0 事件分发的 demo

1.1 ViewPager 和 SlidingPanelLayout 的滑动冲突的解决

 @Override
    public boolean onTouchEvent(MotionEvent ev) {

        float rawX = ev.getRawX();
        int width = getWidth();
        if(rawX >=width/10)
            if(ev.getAction() == MotionEvent.ACTION_DOWN)
            getParent().requestDisallowInterceptTouchEvent(true);

        return super.onTouchEvent(ev);
    }
  • 2: 也可以自定SlidingPanelLayout 在他的事件拦截里面执行:
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        float rawX = ev.getRawX();
        int width = getWidth();
        if(rawX <width/10||rawX>(width*9)/10)
            return super.onInterceptTouchEvent(ev);
        else
            return false;
    }
  • 3: 顺便说一下:DrawerLayout 是不需要去写这个的,因为他内部嵌入了