Android事件分发机制解密二(View处理)

上一篇文章已经分析了事件从Activity->ViewGroup传递的过程,以及ViewGroup事件分发的过程,上一篇文章在这(滑稽.jpg)建议看完这篇再阅读本文,思路会清晰很多:

Android事件分发机制解密 一 ( ViewGroup分发)
在这里插入图片描述

这篇文章主要介绍View对于事件的处理,很明显View事件处理是在dispatchTouchEvent()方法中,直接上代码(本文分析的源码基于Android 10 ,各个版本的源码有些差异,但基本原理都一样):

1.1 View.dispatchTouchEvent()

/**
  * 源码分析:View.dispatchTouchEvent()
  */
public boolean dispatchTouchEvent(MotionEvent event) {
    
    
        //...仅贴出重点代码
        
        boolean result = false;

        //...

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
    
    
            //如果有嵌套滑动则做停止滑动操作
            stopNestedScroll();
        }
        //检查事件的安全性
        if (onFilterTouchEventForSecurity(event)) {
    
    
            //判断值一:View是否enabled 判断值二:是否是处理滚动条事件
            //也就是如果是处理滚动条的事件则rerult=true
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
    
    
                result = true;
            }
            //重点分析1:
            //判断值一:mListenerInfo是否为空通过getListenerInfo()赋值,getListenerInfo()调用的地方有很多,可以认为只要实现了Listener接口就不会为空
            //判断值二:li.mOnTouchListener的赋值在setOnTouchListener(OnTouchListener l) 方法中,只要子类实现了这个方法就不会为null
            //判断值三:View是否是enabled
            //判断值四:mOnTouchListener接口的onTouch方法是否返回true
            //结论:如果子类调用了setOnTouchListener方法且实现了mOnTouchListener接口onTouch()方法返回值为true 则 result=true
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
    
    
                result = true;
            }
            //重点分析2:如果result=true则onTouchEvent()方法不会走
            if (!result && onTouchEvent(event)) {
    
    
                result = true;
            }
        }
        
        //...
        
        return result;
    }

1.2 View.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();
        //判断View是否可以点击(点击,长点击,内容点击)
        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
        //一个disabled的View也会消耗点击事件,只是没有回应
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
    
    
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
    
    
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            return clickable;
        }
        
        //...
        //判断值一:控件可点击则进入switch 判断值二:控件为悬浮控件或可长按
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
    
    
            switch (action) {
    
    
                 // a. 若当前的事件 = 抬起view(主要分析)
                case MotionEvent.ACTION_UP:
                       //... 经过一系列的判断,此处省略
                       //创建PerformClick
                       if (mPerformClick == null) {
    
    
                             mPerformClick = new PerformClick();
                       }
                       // 执行performClick() performClickInternal内部会调用performClick() ->>分析1 
                       performClickInternal();
                    //...
                    mIgnoreNextUpEvent = false;
                    break;
                // b. 若当前的事件 = 按下view
                case MotionEvent.ACTION_DOWN:
                    //... 你懂的 一堆的判断
                    if (mPendingCheckForTap == null) {
    
    
                         mPendingCheckForTap = new CheckForTap();
                    }
                    mPendingCheckForTap.x = event.getX();
                    mPendingCheckForTap.y = event.getY();
                    //执行postDelayed方法
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
    
    
                        //...
                    }
                    break;
                // c. 若当前的事件 = 结束事件(非人为原因
                case MotionEvent.ACTION_CANCEL:
                    if (clickable) {
    
    
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    break;// d. 若当前的事件 = 滑动view
                case MotionEvent.ACTION_MOVE:
                    //...
                    if (!pointInView(x, y, touchSlop)) {
    
    
                        // 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;
        }
   return false;
}
/**
  * 分析1:performClick()
  */  
 public boolean performClick() {
    
    
 
        notifyAutofillManagerOnClick();
        
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
    
    
            //可以通过这个方法设置点击音效
            playSoundEffect(SoundEffectConstants.CLICK);

            //如果我们通过setOnClickListener()方法为控件View注册1个点击事件
            // 那么就会给mOnClickListener变量赋值(即不为空)
            // 则会往下回调onClick() 且 performClick()返回true
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
    
    
            result = false;
        }
        //...
        return result;
    }    

1.3 View事件处理总结

当用户点击控件时:
流程图
关键点:onTouch()的执行 的优先级高于 onClick()

1.4 案例Demo

public class MainActivity extends AppCompatActivity {
    
    

    private Button btn_click;

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn_click = findViewById(R.id.btn_click);

        btn_click.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                Log.e(TAG, "onClick");
            }
        });

        btn_click.setOnTouchListener(new View.OnTouchListener() {
    
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
    
    
                Log.e(TAG, "onTouch: " + event.getAction());

                return true;
            }
        });
    }
}

日志如下:
1.onTouch返回true

2021-03-18 14:54:12.109 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 0
2021-03-18 14:54:12.132 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:54:12.182 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:54:12.194 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:54:12.195 18268-18268/com.enjoy.clickevent E/MainActivity: onTouch: 1

2.onTouch返回false

2021-03-18 14:59:13.795 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 0
2021-03-18 14:59:13.808 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.824 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.841 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.857 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.891 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.895 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 2
2021-03-18 14:59:13.896 19243-19243/com.enjoy.clickevent E/MainActivity: onTouch: 1
2021-03-18 14:59:13.898 19243-19243/com.enjoy.clickevent E/MainActivity: onClick

相信看到这里结合两篇文章,大家应该对Android事件分发和处理有个大体的脉络了解,最后总结一下:

  1. Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。

  2. 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。

  3. 在ViewGroup的dispatchTouch()对事件进行分发,如果子View不处理则ViewGroup作为View来处理。

  4. 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。

如果想要深入研究还需跟着源码多看几遍,可以结合down和move事件的各种情况多分析。后面有空还会对事件的产生进行分析。如果觉得这篇文章对你有用欢迎点赞加收藏✨。

相关参考文章链接:
郭婶的事件分发机制完全解析
Android事件分发机制详解:史上最全面、最易懂

猜你喜欢

转载自blog.csdn.net/qq_32865887/article/details/114887309
今日推荐