Android View的事件分发(四)-事件处理(onTouchEvent)

561 阅读3分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战

相关文章:
Android View的事件分发(一)-事件分发的过程
Android View的事件分发(二)-事件传递顺序
Android View的事件分发(三)-事件的分发(dispatchTouchEvent)
Android View的事件分发(四)-事件处理(onTouchEvent)
Android View的事件分发(五)-事件拦截(onInterceptTouchEvent)

上一篇文章,介绍了View事件的分发,也就是事件的传递过程,最重要的就是dispatchTouchEvent方法的调用以及对阻止父类拦截事件的方法,requestDisallowInterceptTouchEvent,最后通过源码分析,ViewGroup 的事件分发最终还是调用 View 的 dispatchTouchEvent 方法,那么View的分发上一篇也介绍到了,最后执行到onTouchEvent来处理事件,那么这篇文章,介绍onTouchEvent的事件处理。

onTouchEvent

View自己的onTouchEvent方法:

    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();
        //1.如果View是设置成不可用的(DISABLED)仍然会消费点击事件
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            // 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)
                    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
        }
        ...
        //2.CLICKABLE 和LONG_CLICKABLE只要有一个为true就消费这个事件
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    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) {
                            // 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)) {
                                    //3.在ACTION_UP方法发生时会触发performClick()方法
                                    performClick();
                                }
                            }
                        }
                        ...
                    break;
            }
            ...
            return true;
        }
        return false;
    }

上面源码中重要部分,都加注释,可以看出DISABLED这个标识,是标识View的可用状态,源码中可以看出,即使View是disabled状态,事件还是会被消费,那么消费的前提是什么呢?就是CLICKABLE和LONG_CLICKABLE有一个为true,那么就会被消费,也就是onTouchEvenet的返回值为true,这个在开篇的文章中已经介绍了,View 的onTouchEvent 方法默认都会消费掉事件(返回true),除非它是不可点击的(clickable和longClickable同时为false),View的longClickable默认为false,clickable需要区分情况,如Button的clickable默认为true,而TextView的clickable默认为false。另外在看下,在ACTION_UP中的方法performClick:

    /**
     * Call this view's OnClickListener, if it is defined.  Performs all normal
     * actions associated with clicking: reporting accessibility event, playing
     * a sound, etc.
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean performClick() {
        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;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

可以看出,如果View设置点击事件,也就是onClick事件,那么会回调onClick方法。

View的 clickable属性对不同的View的默认值是不一样的,默认情况下,Button的clickable是true,而TextView默认是false,但是如果TextView,设置了setOnClickListener那么,就默认将clickable设置成了true,setOnLongClickListener,也是同理。

 public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

    public void setOnLongClickListener(@Nullable OnLongClickListener l) {
        if (!isLongClickable()) {
            setLongClickable(true);
        }
        getListenerInfo().mOnLongClickListener = l;
    }

onTouchEvent事件的处理就介绍到这里,篇幅比较短,内容也不是很多,但是也是开发中遇到问题比较多的点。

今天更文,刚好是元宵,祝大家元宵节快乐,阖家欢乐。