今天记录一下学习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如下:
可以看到,默认的事件传递顺序是下面的顺序:
- LinearLayout dispatchTouchEvent 分发事件
- LinearLayout onInterceptTouchEvent 是否拦截事件:默认是不拦截的
- dispatchTouchEvent 子view的dispatchTouchEvent分发事件
- 此时流程就和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)可是并没有起到什么作用。
总结
- 事件总是从外向内传递的,即事件总是先传递给父元素,然后由父元素分发给子view,可以在子view中通过requestDisallowInterceptTouchEvent方法来干预父元素的事件分发过程,但是ACTION_DOWN除外。
- ViewGroup默认不拦截任何事件。
- 如果某一个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回false),那么同一序列中的其他事件都不会再交给他处理,并且事件将重新交给他的父元素去处理,即父元素的onTouchEvent会被调用。
- 某个View一旦决定拦截事件,那么这个事件序列都只能有它来处理,并且它的onIntereptTouchEvent不会被调用。