原文:openeim key事件本身就key down,up,long pressed这几种 ...
/*
前面OpenEIM 与大家看了key事件的处理流程,相信大家对此已经有了新的认识,这篇文章我打算带领大家来看看稍微复杂些的touch事件的处理流程。说它复杂是因为key事件本身就key down,up,long pressed这几种,而touch事件支持多指触摸,给人的感觉好像同时在发生多个touch事件一样,所以要处理的手指是多个而不是固定的一个,逻辑上当然也就复杂些了。不过本质上还都是down、up、long pressed,touch的话还有move事件。接下来让我们直接进入本文的正题。
*/
/**
* Pass the touch screen motion event down to the target view, or this openeim
* view if it is the target.
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
if (onFilterTouchEventForSecurity(event)) { // 一般都成立
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) { // 先在ENABLED状态下尝试调用onTouch方法
return true; // 如果被onTouch处理了,则直接返回true
}
// 从这里我们可以看出,当你既设置了OnTouchListener又设置了OnClickListener,那么当前者返回true的时候,
// onTouchEvent没机会被调用,当然你的OnClickListener也就不会被触发
if (onTouchEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
return false; // 上面的都没处理,则返回false
}
/**
* Implement this method to handle touch screen motion events.
* <p>
* If this method is used to detect click actions, it is recommended that
* the actions be performed by implementing and calling
* {@link #performClick()}. This will ensure consistent system behavior,
* including:
* <ul>
* <li>obeying click sound preferences
* <li>dispatching OnClickListener calls
* <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when
* accessibility features are enabled
* </ul>
*
* @param event The motion event.
* @return True if the event was handled, false otherwise.
*/
public boolean onTouchEvent(MotionEvent event) { // View对touch事件的默认处理逻辑
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) { // DISABLED的状态下
if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false); // 复原如果之前是PRESSED状态
}
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE || // CLICKABLE或LONG_CLICKABLE的view标记为对事件处理了,
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); // 只不过是以do nothing的方式处理了。
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) { // 如果有TouchDelegate的话,优先交给它处理
return true; // 处理了返回true,否则接着往下走
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE || // View能对touch事件响应的前提要么是CLICKABLE要么是LONG_CLICKABLE
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP: // UP事件
// 如果外围有可以滚动的parent的话,当按下时会设置这个标志位
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;
// 这行代码就是我们上篇博客中说的,设置了FocusableInTouchMode后,View在点击的时候就会
// 尝试requestFocus(),并将focusToken设置为true
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus(); // 能进来这个if,一般都会返回true
}
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); // 设置为按下状态,会刷新View的状态
}
if (!mHasPerformedLongPress) { // 如果没有长按发生的话
// This is a tap, so remove the longpress check
removeLongPressCallback(); // 移除长按callback
// Only perform take click actions if we were in the pressed state
if (!focusTaken) { // 看到没,focusTaken是false才会进去下面的if语句
// 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)) { // 如果post失败了,则直接调用performClick()方法
performClick(); // 这2行代码会触发onClickListener
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState(); // unset按下状态的
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
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) { // 如果是在可以滚动的container里面的话
mPrivateFlags |= PFLAG_PREPRESSED; // 设置pre按下标志位
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
} // 延迟pressed feedback
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true); // 否则直接显示pressed feedback
checkForLongClick(0); // 并启动长按检测
}
break;
case MotionEvent.ACTION_CANCEL: // 针对CANCEL事件的话,恢复各种状态,移除各种callback
setPressed(false);
removeTapCallback();
removeLongPressCallback();
break;
case MotionEvent.ACTION_MOVE: // MOVE事件
final int x = (int) event.getX();
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) { // 如果移动到view的边界之外了,
// Outside button
removeTapCallback(); // 则取消Tap callback,这样当你松手的时候onClick不会被触发
if ((mPrivateFlags & PFLAG_PRESSED) != 0) { // 当已经是按下状态的话
// Remove any future long press/tap checks
removeLongPressCallback(); // 移除长按callback
setPressed(false); // 恢复按下状态
}
}
break;
}
return true; // 最后返回true,表示对touch事件处理过了,消费了
}
return false; // 既不能单击也不能长按的View,返回false,表示不处理touch事件
}