美文网首页实践知识点
android事件分发(1):view

android事件分发(1):view

作者: 众少成多积小致巨 | 来源:发表于2019-11-17 10:39 被阅读0次

    1、简述

    事件分发 ,是手机对手指触摸事件处理的过程;也就是寻找触摸事件处理者,并进行处理的过程

    对于android开发者来说,事件分发没有使用过,也是了解过,其实android的事件分发是分为多个模块操作:硬件检测—framework层事件拦截—window内部处理—ViewGroup处理— View处理;我们常常需要处理过程也就是最后两个,笔者觉得先从view出发,开始撸起

    2、基础知识

    触摸事件是一系列事件,主要处理的事件ACTION_DOWN、ACTION_MOVE、ACTION_CANCEL、ACTION_UP、ACTION_POINTER_DOWN、ACTION_POINTER_UP,事件是以ACTION_DOWN开始,以ACTION_UP或者ACTION_CANCEL事件结束

    ACTION_DOWN ----> 其它事件 -----> ACTION_UP/ACTION_CANCEL

    2.1 事件类型

    ACTION_DOWN : 第一个手指按下事件
    ACTION_UP: 最后一个手指抬起事件
    ACTION_CANCEL:取消事件,也即是不能得到后续事件
    ACTION_MOVE : 移动事件
    ACTION_POINTER_DOWN:多个手指时按下事件
    ACTION_POINTER_UP:多个手指时,抬起事件

    对于触摸事件,响应动作,有回调和监听

    2.2 常用处理动作

    View.OnTouchListener: 触摸监听
    View.OnClickListener: 单击事件监听
    View.OnLongClickListener: 长按事件监听
    View.onTouchEvent:事件处理回调

    2.3 view事件处理方法

    dispatchTouchEvent(MotionEvent ev): 事件处理开始方法
    onInterceptTouchEvent(MotionEvent ev):在 dispatchTouchEvent方法内调度,拦截事件,ViewGroup内方法
    onTouchEvent(MotionEvent event):回调事件处理方法

    3、具体处理流程

    对于处理流程,我们从View源码中dispatchTouchEvent方法分析,只分析了我认为有用的信息

    3.1 dispatchTouchEvent

           if (onFilterTouchEventForSecurity(event)) {
                if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                    result = true;
                }
                //noinspection SimplifiableIfStatement
                ListenerInfo li = mListenerInfo;
                if (li != null && li.mOnTouchListener != null
                        && (mViewFlags & ENABLED_MASK) == ENABLED
                        && li.mOnTouchListener.onTouch(this, event)) {
                    result = true;
                }
    
                if (!result && onTouchEvent(event)) {
                    result = true;
                }
            }
    
            if (!result && mInputEventConsistencyVerifier != null) {
                mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
            }
    

    可以看出

    • 有个安全策略,如果满足,则view可以继续处理,否则我们不用管(mInputEventConsistencyVerifier是个不可变量);
    • view处理事件首先查看有没有OnTouchListener对象,存在的话进行处理,处理结果为true,则流程结束
    • OnTouchListener对象为空或者处理结果为false,则调用回调方法onTouchEvent

    重点:OnTouchListener监听一定会执行,如若处理结果为false,则onTouchEvent才会执行

    3.2 onTouchEvent

    有没有问咋没有onInterceptTouchEvent方法,view没有这个方法

    1. TouchDelegate对象处理
            if (mTouchDelegate != null) {
                if (mTouchDelegate.onTouchEvent(event)) {
                    return true;
                }
            }
    

    可以通过下面方法设置

       public void setTouchDelegate(TouchDelegate delegate) {
            mTouchDelegate = delegate;
        }
    
    1. ACTION_DOWN 事件处理
               case MotionEvent.ACTION_DOWN:
                        if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
                            mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
                        }
                        mHasPerformedLongPress = false;
                        if (!clickable) {
                            checkForLongClick( ViewConfiguration.getLongPressTimeout(), x,y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
                            break;
                        }
                        if (performButtonActionOnTouchDown(event)) {
                            break;
                        }
                       
                        boolean isInScrollingContainer = isInScrollingContainer();
    
                     
                        if (isInScrollingContainer) {
                            mPrivateFlags |= PFLAG_PREPRESSED;
                            if (mPendingCheckForTap == null) {
                                mPendingCheckForTap = new CheckForTap();
                            }
                            mPendingCheckForTap.x = event.getX();
                            mPendingCheckForTap.y = event.getY();
                            postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                        } else {
                            setPressed(true, x, y);
                            checkForLongClick( ViewConfiguration.getLongPressTimeout(),x, y,TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
                        }
                        break;
    
    • checkForLongClick:检测长按事件,通过handler的postDelay延迟执行和remove来取消执行, 执行的长按任务为CheckForLongPress对象,其中执行了OnLongClickListener监听
         public void run() {
                if ((mOriginalPressedState == isPressed()) && (mParent != null)
                        && mOriginalWindowAttachCount == mWindowAttachCount) {
                    recordGestureClassification(mClassification);
                    if (performLongClick(mX, mY)) { // performLongClick 方法中执行了 OnLongClickListener
                        mHasPerformedLongPress = true;
                    }
                }
            }
    
    1. ACTION_UP关键代码分析
                           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)) {
                                        performClickInternal();
                                    }
                                }
                            }
    
    • 通过PerformClick对象或者执行performClickInternal方法来回调OnClickListener监听
    1. ACTION_CANCEL事件
                   case MotionEvent.ACTION_CANCEL:
                        if (clickable) {
                            setPressed(false);
                        }
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                        break;
    

    取消长按事件执行,重置标志

    3、流程总结

    • 执行优先级

    OnTouchListener.onTouch > onTouchEvent > OnClickListener.onClick or OnLongClickListener.onLongClick

    • 如果事件传到view,那么OnTouchListener.onTouch一定执行;如果执行结果为false,才执行onTouchEvent
    • OnClickListener.onClick or OnLongClickListener.onLongClick是在onTouchEvent方法中执行,且是有且只会执行其中一种

    请继续查看 android事件分发(2):ViewGroup

    相关文章

      网友评论

        本文标题:android事件分发(1):view

        本文链接:https://www.haomeiwen.com/subject/pusyictx.html