目录
一、Android 9 sdk 28
setOnClickListener()
在线代码:Android 9 SDK 28 View.java
先看看setOnClickListener
到底干了啥?
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
这个getListenerInfo()
,返回了个什么东西?如下:
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
是一个ListenerInfo
对象,看下ListenerInfo这个类的定义:
可以看出ListenerInfo是View的静态内部类,它里面包含了OnClickListener类型的变量mOnClickListener
。
总结下:在View中有一个成员变量:ListenerInfo mListenerInfo;
,ListenerInfo类中持有OnClickListener对象。我们平常设置的OnClickListener,就保存在ListenerInfo对象里面。
mOnClickListener在哪里被调用的?
看下图:在performClick()函数中被调用。
performClick()又在哪里被调用?
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()被performClickInternal()调用了,那performClickInternal()又被谁调用了?
鼎鼎大名的onTouchEvent()
出现了。
总结:在onTouchEvent的ACTION_UP
中,调用了performClickInternal(),然后在performClickInternal()中调用了performClick(),最终调用了OnClickListener。
View.onTouchEvent()又是被谁调用的?
如上图,onTouchEvent()方法是在dispatchTouchEvent()方法中调用的。Android 9上,dispatchTouchEvent()方法代码已经很多了,不容易看懂,我们找老版本的代码看看,简单易懂。
二、Android 1.6 sdk 4
在线代码:Android 1.6 sdk 4 View.java
setOnClickListener()方法:
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
这里的mOnClickListener
就是View的成员变量。mOnClickListener在哪里被调用了?
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
还是performClick()方法。那performClick()方法又在那里被调用了?
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
if ((mPrivateFlags & PRESSED) != 0) {
// 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) {
// This is a tap, so remove the longpress check
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
performClick();
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
}
break;
case MotionEvent.ACTION_DOWN:
mPrivateFlags |= PRESSED;
refreshDrawableState();
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
postCheckForLongClick();
}
break;
case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
break;
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press checks
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
} else {
// Inside button
if ((mPrivateFlags & PRESSED) == 0) {
// Need to switch from not pressed to pressed
mPrivateFlags |= PRESSED;
refreshDrawableState();
}
}
break;
}
return true;
}
return false;
}
onTouchEvent()的ACTION_UP
事件处理中,调用了performClick()方法。onTouchEvent()方法又是在哪里被调用的?
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
onTouchEvent()方法在dispatchTouchEvent方法里面被调用。
关于dispatchTouchEvent方法的分析,后面的博客继续讲。