上一篇文章已经分析了事件从Activity->ViewGroup传递的过程,以及ViewGroup事件分发的过程,上一篇文章在这(滑稽.jpg)建议看完这篇再阅读本文,思路会清晰很多:
Android事件分发机制解密 一 ( ViewGroup分发)
这篇文章主要介绍View对于事件的处理,很明显View事件处理是在dispatchTouchEvent()方法中,直接上代码(本文分析的源码基于Android 10 ,各个版本的源码有些差异,但基本原理都一样):
1.1 View.dispatchTouchEvent()
/**
* 源码分析:View.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent event) {
//...仅贴出重点代码
boolean result = false;
//...
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
//如果有嵌套滑动则做停止滑动操作
stopNestedScroll();
}
//检查事件的安全性
if (onFilterTouchEventForSecurity(event)) {
//判断值一:View是否enabled 判断值二:是否是处理滚动条事件
//也就是如果是处理滚动条的事件则rerult=true
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//重点分析1:
//判断值一:mListenerInfo是否为空通过getListenerInfo()赋值,getListenerInfo()调用的地方有很多,可以认为只要实现了Listener接口就不会为空
//判断值二:li.mOnTouchListener的赋值在setOnTouchListener(OnTouchListener l) 方法中,只要子类实现了这个方法就不会为null
//判断值三:View是否是enabled
//判断值四:mOnTouchListener接口的onTouch方法是否返回true
//结论:如果子类调用了setOnTouchListener方法且实现了mOnTouchListener接口onTouch()方法返回值为true 则 result=true
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//重点分析2:如果result=true则onTouchEvent()方法不会走
if (!result && onTouchEvent(event)) {
result = true;
}
}
//...
return result;
}
1.2 View.onTouchEvent()
/**
* 源码分析:View.onTouchEvent()
*/
public boolean onTouchEvent(MotionEvent event) {
//...仅贴出重点代码
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
//判断View是否可以点击(点击,长点击,内容点击)
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
//一个disabled的View也会消耗点击事件,只是没有回应
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
return clickable;
}
//...
//判断值一:控件可点击则进入switch 判断值二:控件为悬浮控件或可长按
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
// a. 若当前的事件 = 抬起view(主要分析)
case MotionEvent.ACTION_UP:
//... 经过一系列的判断,此处省略
//创建PerformClick
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
// 执行performClick() performClickInternal内部会调用performClick() ->>分析1
performClickInternal();
//...
mIgnoreNextUpEvent = false;
break;
// b. 若当前的事件 = 按下view
case MotionEvent.ACTION_DOWN:
//... 你懂的 一堆的判断
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
//执行postDelayed方法
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
//...
}
break;
// c. 若当前的事件 = 结束事件(非人为原因
case MotionEvent.ACTION_CANCEL:
if (clickable) {
setPressed(false);
}
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
break;
)
// d. 若当前的事件 = 滑动view
case MotionEvent.ACTION_MOVE:
//...
if (!pointInView(x, y, touchSlop)) {
// Outside button
// Remove any future long press/tap checks
removeTapCallback();
removeLongPressCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
}
//...
break;
}
return true;
}
return false;
}
/**
* 分析1:performClick()
*/
public boolean performClick() {
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
//可以通过这个方法设置点击音效
playSoundEffect(SoundEffectConstants.CLICK);
//如果我们通过setOnClickListener()方法为控件View注册1个点击事件
// 那么就会给mOnClickListener变量赋值(即不为空)
// 则会往下回调onClick() 且 performClick()返回true
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
//...
return result;
}
1.3 View事件处理总结
当用户点击控件时:
关键点:onTouch()的执行 的优先级高于 onClick()
1.4 案例Demo
public class MainActivity extends AppCompatActivity {
private Button btn_click;
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_click = findViewById(R.id.btn_click);
btn_click.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "onClick");
}
});
btn_click.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e(TAG, "onTouch: " + event.getAction());
return true;
}
});
}
}
日志如下:
1.onTouch返回true
2021-03-18 14:54:12.109 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 0
2021-03-18 14:54:12.132 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:54:12.182 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:54:12.194 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:54:12.195 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 1
2.onTouch返回false
2021-03-18 14:59:13.795 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 0
2021-03-18 14:59:13.808 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.824 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.841 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.857 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.891 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.895 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.896 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 1
2021-03-18 14:59:13.898 19243-19243/com.enjoy.clickevent E/MainActivity: onClick
相信看到这里结合两篇文章,大家应该对Android事件分发和处理有个大体的脉络了解,最后总结一下:
-
Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。
-
在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。
-
在ViewGroup的dispatchTouch()对事件进行分发,如果子View不处理则ViewGroup作为View来处理。
-
子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。
如果想要深入研究还需跟着源码多看几遍,可以结合down和move事件的各种情况多分析。后面有空还会对事件的产生进行分析。如果觉得这篇文章对你有用欢迎点赞加收藏✨。
相关参考文章链接:
郭婶的事件分发机制完全解析
Android事件分发机制详解:史上最全面、最易懂