Android View 事件分发
2021-05-13 本文已影响0人
是刘航啊
这里首先简单的写一个例子,方便理解事件触发的先后顺序
public class TouchView extends View {
public TouchView(Context context) {
super(context);
}
public TouchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TouchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("V10Activity", "onTouchEvent -> " + event.getAction());
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d("V10Activity", "dispatchTouchEvent -> " + event.getAction());
return super.dispatchTouchEvent(event);
}
}
在 Activity 中设置 OnTouchListener 「false」
v10Binding.vwTouch.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("V10Activity", "onTouch -> " + event.getAction());
return false;
}
});
v10Binding.vwTouch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("V10Activity", "onClick");
}
});
我们可以看到,首先执行的是 View 的「dispatchTouchEvent」方法,其次是「onTouch」方法,「onTouchEvent」方法,最后执行的是「onClick」方法,因此我们可以暂时得出一个结论:
在触碰到 View 的时候,首先会执行 dispatchTouchEvent 方法,其次方法的执行顺序为 onTouch -> onTouchEvent -> onClick
在 Activity 中设置 OnTouchListener 「true」
v10Binding.vwTouch.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("V10Activity", "onTouch -> " + event.getAction());
return true;
}
});
v10Binding.vwTouch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("V10Activity", "onClick");
}
});
在执行 dispatchTouchEvent 方法后,只执行了 onTouch 方法
dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
...
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//判断是否注册 OnTouchListener
//判断 OnTouchListener 中 onTouch 是否返回 true
//当 onTouch 返回 true 时,事件被 onTouch 处理了,不会执行 onTouchEvent 方法,说明 onTouch 的优先级高于 onTouchEvent
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
}
通过 dispatchTouchEvent 源码可以知道,当 onTouch 返回 true 时,事件被 onTouch 消费,并不会执行 onTouchEvent 和 onClick 方法。onTouch 的优先级高于 onTouchEvent 和 onClick。
onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
...
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
...
//如果不能点击则直接返回
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
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();
}
}
}
...
break;
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_CANCEL:
...
break;
case MotionEvent.ACTION_MOVE:
...
break;
}
}
...
}
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();
}
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;
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;
}
图中只展现了部分源码,通过 onTouchEvent 的源码,执行 onClick 有一个前提条件必须是可点击的,而 onClick 执行的方法是在 onTouchEvent 的 UP 事件中触发。说明 onTouchEvent 优先级高于 onClick
通过分析源码,可以验证我们之前打印的结果,之后会分析 ViewGroup 的事件分发
View 事件分发总结
- 事件分发首先会调用 View 的 dispatchTouchEvent 方法
- 事件分发执行的顺序为 onTouch -> onTouchEvent -> onClick,可以理解成 优先级 onTouch > onTouchEvent > onClick
- 当 OnTouchListener 为 true 时,会消费当前事件,只会执行 onTouch 方法,不会传递到 onTouchEvent 方法。当 OnTouchListener 为 false 时,则会将事件传递
- onClick 事件触发的前提条件是可点击的,onClick 触发在 onTouchEvent 的UP 事件中
View 事件分发大致就介绍到这里了,如果有什么写得不对的,可以在下方评论留言,我会第一时间改正。