这是android事件分法机制的最后一个重要的方法,一般自定义的视图都要往这里开刀,开个玩笑;ontouchevent方法消费事件的场合,无论消费的对象是viewgroup还是view都要调用该方法;那么默认的,view类在这个方法里做了什么呢?我们都知道OnClickListener和OnLongClickListener这两种事件监听器吧,对,你猜的不错,实现的机制就是在这个方法里面,让我们来看看吧。
第一步:分析viewFlags属性信息判断是否是可点击(clickable);第二步,如果当前的视图设置enable为false,但是视图是可点击的(clickable),我们还是认为它消费了该事件,否则是不消费,无论是否消费,在enable是false的情况下,它是直接返回的,也就不会响应onClick事件之类的;第三步,如果该视图将事件委托给了其他视图进行处理,那么让被委托的视图进行处理,自己就不处理了;接下来进入主题了:
//如果此视图是可点击的或者他的提示框是显示的,那么进入里面做进一步处理
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
//如果是提示框显示,那么让他处理按起事件
if ((viewFlags & TOOLTIP) == TOOLTIP) {
handleTooltipUp();
}
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
//记录该视图响应按下的事件
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
//如果事件没有超出边框,并且响应了按下事件,那么响应点击事件
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 (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
//如果已经响应了长点击事件,那么就不响应点击事件
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// 移除长点击回调
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
//响应点击事件
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
//执行点击操作
if (!post(mPerformClick)) {
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
//如果是事件来源是触屏产生的,那么mPrivateFlags3标记为PFLAG3_FINGER_DOWN
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
}
//重置为false
mHasPerformedLongPress = false;
if (!clickable) {
checkForLongClick(0, x, y);
break;
}
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
//按下时间大于taptimeout,那么进入长点击响应事件
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
checkForLongClick(0, x, y);
}
break;
case MotionEvent.ACTION_CANCEL:
if (clickable) {
setPressed(false);
}
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
break;
case MotionEvent.ACTION_MOVE:
if (clickable) {
drawableHotspotChanged(x, y);
}
//超出视图大小范围,那么不会响应点击或长点击事件
if (!pointInView(x, y, mTouchSlop)) {
// 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;
}