注意,这里分析的是view 的事件分发,不是ViewGroup
相比ViewGroup 的事件分发,view的事件分发只有两个方法,相对简单
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("TAG", "view dispatchTouchEvent: ----->" + event.getAction());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG", "view onTouchEvent: ----->" + event.getAction());
return super.onTouchEvent(event);
}
dispatchTouchEvent 方法用于分发,onTouchEvent 用于处理,先来看第一个方法
public boolean dispatchTouchEvent(MotionEvent event) {
// 省略代码......
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//判断是否注册onTouch 事件,从这里可以知道,先调用的必然是onTouch
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//根据onTouch 的返回来判断是否调用onTouchEvent 方法
if (!result && onTouchEvent(event)) {
result = true;
}
}
//省略代码.......
return result;
}
通过上面的代码能知道view 注册事件的调用顺序,当view设置了onTouch 会首先调用,当onTouch 返回ture 时不会再调用onTouchEvent
//通过setOnTouchListener 的方式注册onTouch事件
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG", "onTouch: ----> 1" );
//当返回true 时 onTouchEvent 方法将不会调用
return true;
}
});
接下来分析 onTouchEvent 方法,假设这时 onTouch 返回false
public boolean onTouchEvent(MotionEvent event) {
// 获取当前点击的位置
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
//省略代码..........
switch (action) {
// 在ACTION_UP 事件中触发 onClick 事件
case MotionEvent.ACTION_UP:
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
private boolean performClickInternal() {
// Must notify autofill manager before performing the click actions to avoid scenarios where
// the app has a click listener that changes the state of views the autofill service might
// be interested on.
notifyAutofillManagerOnClick();
return performClick();
}
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
//如果设置监听了,调用,并返回 true
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
通过上面可以知道,view 的onClick 事件是在onTouchEvent 中 ACTION_UP 执行的,所以只有抬手后才会执行onClick。所以view 的事件执行顺序为:dispatchTouchEvent -> onTouch -> onTouchEvent -> onClick。所以当 onTouch 返回ture 是 onClick 事件将不会执行。最后来看下面这种情况,当继承view 重写 dispatchTouchEvent 直接返回ture
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("TAG", "view dispatchTouchEvent: ----->" + event.getAction());
return ture;
}
当直接在 dispatchTouchEvent 中返回 ture 那执行循序是怎么样的呢?
答案是只执行 dispatchTouchEvent 因为没有调用父类view 中的 dispatchTouchEvent 方法所以后续的方法都不会执行,如果又想执行后续方法又想返回ture 呢?可以如下
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("TAG", "view dispatchTouchEvent: ----->" + event.getAction());
//手动调用view 中的 dispatchTouchEvent
super.dispatchTouchEvent(event);
return ture;
}
总结:view 的事件分发相对简单,正常执行顺序为:dispatchTouchEvent -> onTouch -> onTouchEvent -> onClick 可以根据需求在不同的地方返回 ture 可以让后续的方法不在执行
网友评论