一、点击事件流程组成
Activity + Window + DecorView + View事件分发机制 + View的点击事件处理过程
- 一个点击事件产生之后,先传递给Activity ——> Window(PhoneWindow具体实现) ——> DecorView
- View的点击事件分发机制
- View的点击事件处理过程
二、举例:一个button的点击事件
- 一个点击事件产生之后,先传递给Activity ——> Window(PhoneWindow具体实现) ——> DecorView,继承自FrameLayout,是一个ViewGroup,ViewGroup默认不拦截事件
- ——>顶级View,即在setContentView()我们自己所设置的View,一般最外层布局是LinearLayout、ConstraintLayout和RelativeLayout,也都是ViewGroup (这里已经进入View的分发机制)
- ——>此时遍历子View,向下分发,因为布局是我们自己写的,所以有几层布局,几个Button都是确定的,如果遇到嵌套布局,那ViewGroup继续遍历子View分发,开启下一轮分发流程
- ——>如果子View是能够接受事件,满足在播放动画 OR 点击事件落到子View区域内(在布局中所占的宽高位置)的条件,事件交由子View处理,走它的dispatchOnTouchEvent(),而Button等是继承自View,是一个单独的子View
- ——>如果Button等子View没有重写dispatchTouchEvent()等方法,那最终会使用View的对应方法,View没有拦截事件方法onInterceptTouchEvent(),所以会调用onTouchEvent()。(点击事件分发流程结束)
- ——>View的dispatchTouchEvent()如果设置有mOnTouchListener.onTouch(),走到这里onTouch()消耗事件就返回了,这里的OnTouchListener是一个接口,项目中很少见到,注意区别于OnClickListener,不是一个东西。(有View消耗事件,开始View的点击事件处理流程。)
- ——>如果用户没有设置OnTouchListener,则走到onTouchEvent(),在onTouchEvent()里才会使用到OnClickListener。
- ——>onTouchEvent() ——> performClickInternal() ——>mOnClickListener.onClick(this),此处是会回调button设置监听的代码
三、源码
//Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
//PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
//DecorView.java
//是setContentView()中设置view的父View
public boolean superDispatchTouchEvent(MotionEvent event) {
//DecorView继承自FrameLayout,FrameLayout继承自ViewGroup
return super.dispatchTouchEvent(event);
}
//ViewGroup.java
/**
* 此时到View事件的分发机制,下面是伪代码
* 父View收到点击事件后,开始分发事件,如果拦截事件,则调用onTouchEvent()方法处理事件,否则交由子View进行下一轮事件的分发。
* ViewGroup默认不拦截View(书籍)?返回false,现在version的版本有一定条件为true,怎么解释这个case
* 子View可能是ViewGroup吗?可能,比如xml里的嵌套LinearLayout,它是否拦截消耗事件由具体的代码实现,如果没有重写dispatchTouchEvent(),则用父类ViewGroup的对应方法。
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
//View.java
/**
* 这里的View不包含ViewGroup,传递到具体的单独View,如Button
* mOnTouchListener.onTouch(): 这里的OnTouchListener是一个接口,项目中很少见到,注意区别于OnClickListener,不是一个东西。优先级比onTouchEvent()高。
* 如果用户没有设置OnTouchListener或者 mOnTouchListener.onTouch()返回false,则走到onTouchEvent(),在onTouchEvent()里才会使用到OnClickListener,见下面方法实现。
*/
public boolean dispatchTouchEvent(MotionEvent event) {
//...
boolean result = false;
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
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;
}
//View.java
//最终会走onTouchEvent() ——> performClickInternal() ——>mOnClickListener.onClick(this)
public boolean onTouchEvent(MotionEvent event) {
//...
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
//...
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 (!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;
}
return true;
}
return false;
}
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;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
**li.mOnClickListener.onClick(this);**
result = true;
} else {
result = false;
}
//...
return result;
}
點擊查看更多內(nèi)容
為 TA 點贊
評論
評論
共同學習,寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章
正在加載中
感謝您的支持,我會繼續(xù)努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦