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.
// 如果当前View是Disabled状态且是可点击则会消费掉事件
return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
// 如果设置了mTouchDelegate,则会将事件交给代理者处理,直接return true
if (mTouchDelegate != null && mTouchDelegate.onTouchEvent(event)) return true;
// 如果我们的View可以点击或者可以长按,则……注意if范围内,最终一定是 return true ;
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
if ((mPrivateFlags & 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) {
// 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)) performClick();
}
}
if (mUnsetPressedState == null) mUnsetPressedState = new UnsetPressedState();
if (prepressed) {
mPrivateFlags |= PRESSED;
refreshDrawableState();
postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
case MotionEvent.ACTION_DOWN:
if (mPendingCheckForTap == null) mPendingCheckForTap = new CheckForTap();
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
break;
case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
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 = mTouchSlop;
if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button 判断当前触摸点有没有移出我们的View
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
break;
}
return true;
}
return false;
}关于对ACTION_UP的处理
如果mPrivateFlags包含PRESSED或者PREPRESSED则进入执行体,也就是无论是115ms内或者之后抬起都会进入执行体。
如果mHasPerformedLongPress没有被执行,则调用removeLongPressCallback()移除长按的检测;
如果mPerformClick为null则初始化一个实例,然后立即通过handler添加到消息队列尾部
- 如果添加失败则直接执行 performClick()
- 如果添加成功,则在mPerformClick的run方法中执行performClick()
下面看一下performClick()方法:
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}久违了~我们的mOnClickListener ;
如果prepressed为true,为mPrivateFlags设置表示为PRESSED,刷新背景,125毫秒后执行mUnsetPressedState.run();
否则mUnsetPressedState.run()立即执行;也就是不管咋样,最后mUnsetPressedState.run()都会执行;
看看这个UnsetPressedState主要干什么:
private final class UnsetPressedState implements Runnable {
public void run() {
setPressed(false);
}
}
public void setPressed(boolean pressed) {
if (pressed) mPrivateFlags |= PRESSED;
else mPrivateFlags &= ~PRESSED;
refreshDrawableState();
dispatchSetPressed(pressed);
}把我们的mPrivateFlags中的PRESSED取消,然后刷新背景,把setPress转发下去。