android事件传递机制

今天记录一下学习ViewGroup以及View的事件分发和传递机制的过程,先看view的事件传递过程,在android开发中,手机的屏幕大小是一定的,有的时候我们的view是独自layout在手机屏幕上的,可是大部分情况下,是很多view叠加起来的,那么这个时候,就需要事件分发机制来控制view的触摸和点击事件等。

view的事件传递流程

这里,为了便于理解,我先写一个MyButton类,继承自Button类

新建自定义MyButton

public class MyButton extends Button {

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyButton onTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyButton onTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyButton onTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(event);
    }
}

这里自定义的MyButton只是重写了view中的onTouchEvent和dispatchTouchEvent这两个方法。

在布局中引入MyButton

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/my_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

   <com.example.shijian.MyButton
        android:id="@+id/id_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</LinearLayout>

为MyButton设置setOnTouchListener和setOnClickListener

public class MainActivity extends Activity {

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

        MyButton btn = (MyButton) findViewById(R.id.id_button);

        btn.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View arg0, MotionEvent event) {
                int action = event.getAction();
                switch (action) {
                case MotionEvent.ACTION_DOWN:
                    Log.d("shijian","MyButton ontouch ACTION_DOWN.....");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.d("shijian","MyButton ontouch ACTION_MOVE.....");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.d("shijian","MyButton ontouch ACTION_UP.....");
                    break;
                default:
                    break;
                }
                return false;
            }
        });


        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                Log.d("shijian","MainActivity onclick runs .....");
            }
        });
    }
}

这个时候,我们点击btn按钮,注意这个这次我们直接点击,不在该btn上进行移动的事件,发现log如下:
这里写图片描述
通过log可以发现,不管是down还是up,都是首先将事件交给dispatchTouchEvent来处理。,目前来看view的事件传递:dispatchTouchEvent –>onTouch –>onTouchEvent –>onClick,查看View的dispatchTouchEvent方法中有这样一句代码:

public boolean dispatchTouchEvent(MotionEvent event) {
    boolean result = false;
    //......
    if (onFilterTouchEventForSecurity(event)) {
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
             && (mViewFlags & ENABLED_MASK) == ENABLED
              && li.mOnTouchListener.onTouch(this, event))     {
              result = true;
            }

       if (!result && onTouchEvent(event)) {
             result = true;
       }

    //.......

    return result;
}

这里省略了部分代码,根据代码可以发现如果第一个if中li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)条件为真,那么onTouchEvent(event)方法将不会执行

  • li != null && li.mOnTouchListener != null 表示如果设置了setOnClickListener
  • (mViewFlags & ENABLED_MASK) == ENABLED 表示当前view是enable的
  • li.mOnTouchListener.onTouch(this, event) onTouchListener中onTouch方法的返回值

    上面三个条件,前两个MyButton都已经满足,第三个目前我们在onTouchListener.onTouch里返回的是false,那么当前的if则不成立,result继续保持为false,所以onTouchEvent会继续执行,此时onTouchEvent的返回值,就是dispatchTouchEvent的返回值。

修改onTouch的返回值为true

那么现在,我将onTouch的返回值改为true,再次点击MyButton,注意只是点击,没有移动。此时log如下:
这里写图片描述
可以看到,此时onTouchEvent和onClick方法并没有执行。根据上面的分析,当onTouch返回true的时候,onTouchEvent方法是不会执行的,那么为什么onClick也没有运行呢??这是因为onClick方法是在onTouchEvent方法中执行的。onTouchEvent没有执行,onClick当然也是不会执行的,我们来看onTouchEvent方法的源码:

public boolean onTouchEvent(MotionEvent event) {
    //......
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                       }

                        if (!mHasPerformedLongPress) {
                            // 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)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    break;

            //......

            return true;
        }

        return false;
    }
  • 在onTouchEvent方法中,如果viewFlags & CLICKABLE) == CLICKABLE ||
    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE
    也就是当前view是可点击的,则会返回true
  • 可以看到当前action是MotionEvent.ACTION_UP的时候,会执行performClick();方法,该方法就是执行onClick的,如下:
 public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        //当我们为当前MyButton设置onClickListener的时候
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            //执行当前onClick
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }
修改dispatchTouchEvent的返回值为true

这次我们将MyButton的dispatchTouchEvent(MotionEvent event)方法返回true;

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return true;
    }

再次点击btn按钮,log,如下:
这里写图片描述

发现只有dispatchTouchEvent方法运行了,即当dispatchTouchEvent方法返回true的时候,表示该事件被当前view消费掉了,将不在继续往下传递。View的dispatchTouchEvent方法默认返回的是false

改变dispatchTouchEvent中ACTION_DOWN返回true
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_DOWN.....");
            return true;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(event);
    }

点击button的时候,进行了move操作,log如下:
这里写图片描述
如果在dispatchTouchEvent中ACTION_DOWN返回true,则ontouch和onTouchEvent的ACTION_DOWN将不会得到执行。这个也很好理解,当个dispatchTouchEvent中CTION_DOWN返回true的时候,表示以后的所有ACTION_DOWN事件都交给dispatchTouchEvent自己来处理。

改变dspatchTouchEvent中ACTION_DOWN返回false
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_DOWN.....");
            return false;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(event);
    }

此时我们将dispatchTouchEvent中的ACTION_DOWN中返回了false,表示该Button不进行事件分发,故自己不做处理,该事件将会传递到下一个view,这一点,根据log可以得到证明。
这里写图片描述

改变dispatchTouchEvent中ACTION_MOVE返回true
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_MOVE.....");
            return true;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(event);
    }

此时log如下:
这里写图片描述

这里我在点击的时候同时进行了move操作,根据log可以得出,如果dispatchTouchEvent中ation_move返回true,那么,以后所有的move事件都会交给dispatchTouchEvent自己处理。并且onTouch和onTouchEvent是不会执行action_move分支的。
同样,当设置dispatchTouchEvent的action_up返回true的时候,表示所有的action_up事件都会交给当前的EdispatchTouchEvent来处理,Touch和onTouchEvent的action_up同样是不会执行的。

改变onTouchEvent的返回值

这次,我们将dispatchTouchEvent方法继续改为返回默认值,而将MyButton的onTouchEvent方法,返回true
这里写图片描述

通过log可以发现,这次我们的事件传递的顺序是没有变化的,唯一发生改变的就是btn的onclick方法没有执行。查看View中ontouchEvent中的源码,可以看到这样一句注释True if the event was handled, false otherwise.即返回true的时候表示该事件是被当前view所消费掉了,即不在继续向下传递。

小结

可以看到整个view的事件转发流程如下:
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

  • 如果当前view的dispatchTouchEvent方法返回true,则 表示以后的所有事件都交给 dispatchTouchEvent 自己处理
  • 如果dispatchTouchEvent方法中,action_down,action_move,action_up中任意一个返回true,则表示当前MotionEvent对应的行为都会交给dispatchTouchEvent自己来处理,onTouch和onTouchEvent中对应的行为是不会执行的。
  • 如果在onTouch方法中返回了true,那么onTouchEvent是不会执行的,由于onClick是在onTouchEvent方法中的performClick()方法中执行的,故此时onClick也是不会执行的。
  • 如果在onTouchEvent中返回了true,表示当前的onTouchEvent消耗了该事件,故不再向下传递,因此onClick同样不会执行。
  • 如果onTouch执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action
  • 如果某一个View的clickable或者longClickable有一个为true,则该View的onTouchEvent默认都会消耗该事件。

Viewgroup事件分发

下面将view和viewgroup的事件结合起来使用,看看其事件是怎么分发的,新建MyLayoutlinearlayout.java继承自LinearLayout

新建MyLayoutlinearlayout.java

public class MyLinearLayout extends LinearLayout {

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyLinearLayout dispatchTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyLinearLayout dispatchTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyLinearLayout dispatchTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyLinearLayout onTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyLinearLayout onTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyLinearLayout onTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.onTouchEvent(event);
    }
}

这里重写了onInterceptTouchEvent,dispatchTouchEvent,onTouchEvent这三个方法。

  • onInterceptTouchEvent表示当前ViewGroup是否拦截该事件,如果拦截,则不在往下分发。

在布局中引入Mylinearlayout

<com.example.shijian.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/my_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

   <com.example.shijian.MyButton
        android:id="@+id/id_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</com.example.shijian.MyLinearLayout>

我们都知道在ViewGroup中和view不同的是,会多了一个onInterceptTouchEvent方法,用来拦截事件,表示当前ViewGroup是否拦截当前事件,如果拦截,则会交给ViewGroup自己的onTouchEvent来处理,否会会向下传递,如果传递到其子view,当其子View的onTouchEvent都返回了false,那么该事件同样有该ViewGroup自己onTouchEvent处理。
下面我们证实上面的说法,现在将自定义的button放置到自定义的linearlayout当中,现在点击button,log如下:

这里写图片描述
可以看到,默认的事件传递顺序是下面的顺序:

  1. LinearLayout dispatchTouchEvent 分发事件
  2. LinearLayout onInterceptTouchEvent 是否拦截事件:默认是不拦截的
  3. dispatchTouchEvent 子view的dispatchTouchEvent分发事件
  4. 此时流程就和view的事件处理一致。

更改ViewGroup中onInterceptTouchEvent方法返回值

现在我们将onIntercept的返回值改为true,即当前viewgroup默认拦截事件。

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return true;
}

此时log如下:
这里写图片描述

可以看到,此时由于viewGroup默认拦截当前事件,所以该事件是不会传递到子view当中的。

注意,如果onIntercept方法中DOWN retrun true ,则DOWN,MOVE,UP子View都不会捕获事件;如果你在MOVE return true , 则子View在MOVE和UP都不会捕获事件。我们做个试验:

更改onInterceptTouchEvent中DOWN的返回值为true

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_DOWN.....");
            return true;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.onInterceptTouchEvent(ev);
}

此时我在点击MyButton的时候同时进行滑动,log打印如下:
这里写图片描述
可以看到,此时子view是没有捕获到down,move和up这三个事件的。

更改onInterceptTouchEvent中MOVE返回值为true

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_MOVE.....");
            return true;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.onInterceptTouchEvent(ev);
    }

此时点击按钮,注意这里我故意在按钮上滑动一下,证明他不能接受到move和up事件。此时log如下:
这里写图片描述

根据log可以看到,当onIntercept中MOVE返回true的时候,子view是不能接收到move和up事件的。 看log可以发现onInterceptTouchEvent方法并不是每一次都会执行的,看下ViewGroup的dispatchTouchEvent方法,有这样一段代码:

if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
 }

可以看到当actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null时候才会执行onInterceptTouchEvent(ev),来判断是否拦截当前事件。

private TouchTarget addTouchTarget(View child, int pointerIdBits) {
        TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
}

addTouchTarget方法是dispatchTouchment的方法中调用的:

if (!canceled && !intercepted) {
//intercepted = onInterceptTouchEvent(ev)方法的返回值,即是否拦截事件
       //省略其他代码
       newTouchTarget = addTouchTarget(child, idBitsToAssign);
       alreadyDispatchedToNewTouchTarget = true;

}

当ViewGroup不拦截事件,并交给子View处理时候,mFirstTouchTarget != null成立,反之,如果ViewGroup开始拦截事件,此时mFirstTouchTarget != null就不会成立,并且当MotionEvent.ACTION_MOVE或者MotionEvent.ACTION_UP传递过来的时候,actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null该条件就不会成立,所以onInterceptTouchEvent方法将不会再次调用,并且同一序列中的其他事件都会交给其处理。

更改onInterceptTouchEvent中up值为true

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyLinearLayout onInterceptTouchEvent ACTION_UP.....");
            return true;
        default:
            break;
        }
        return super.onInterceptTouchEvent(ev);
}

点击MyButton并且进行move操作,此时log如下:
这里写图片描述

可以看到,这里子view接收到了action_down和action_move但是没有接收到action_up.

更改子View中onTouchEvent方法的返回值

这里我先将onIntercept方法的返回值改为false表示默认不拦截当前事件,并且将子view中的onTouchEvent方法的返回值改为false,此时log如下:
这里写图片描述
可以看到,当子view的onTouchEvent返回false的时候,如果没有子控件需要传递该事件,该事件有其父控件的onTouchEvent来处理

子view如何设置事件不被拦截

其实android为我们的子view提供了一个requestDisallowInterceptTouchEvent方法,该方法用来设置除去action_down以外的事件,不被拦截。因为在ViewGroup中requestDisallowInterceptTouchEvent方法和FLAG_DISALLOW_INTERCEPT的值有关。

public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
            return;
        }

        if (disallowIntercept) {
            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
        } else {
            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
        }

        if (mParent != null) {
            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
}

因为在每次事件是action_down的时候,都会在resetTouchState();方法中将该值重置,下面验证刚才的结论:

  • 在ViewGroup的onInterceptTouchEvent方法的action_move中返回true,并且在子view的dispatchTouchEvent方法中通过requestDisallowInterceptTouchEvent(true)设置事件不被拦截。
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
        getParent().requestDisallowInterceptTouchEvent(true);
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_DOWN.....");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_MOVE.....");
            break;
        case MotionEvent.ACTION_UP:
            Log.d("shijian","MyButton dispatchTouchEvent ACTION_UP.....");
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(event);
}

此时打印log如下:
这里写图片描述

可以看到虽然我们在ViewGroup中的action_Move里返回了true,但是在子view的dispatchTouchEvent 中通过getParent().requestDisallowInterceptTouchEvent(true);设置父控件不拦截出action_Down以外的事件。

  • 在ViewGroup的onInterceptTouchEvent方法的action_down中return true,且在子view的dispatchTouchEvent方法中通过requestDisallowInterceptTouchEvent(true)设置事件不被拦截

此时log如下:
这里写图片描述

可以看到此时,虽然我们同样的调用了requestDisallowInterceptTouchEvent(true)可是并没有起到什么作用。

总结

  1. 事件总是从外向内传递的,即事件总是先传递给父元素,然后由父元素分发给子view,可以在子view中通过requestDisallowInterceptTouchEvent方法来干预父元素的事件分发过程,但是ACTION_DOWN除外。
  2. ViewGroup默认不拦截任何事件。
  3. 如果某一个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回false),那么同一序列中的其他事件都不会再交给他处理,并且事件将重新交给他的父元素去处理,即父元素的onTouchEvent会被调用。
  4. 某个View一旦决定拦截事件,那么这个事件序列都只能有它来处理,并且它的onIntereptTouchEvent不会被调用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值