Android event distribution mechanism decryption two (View processing)

The previous article has analyzed the process of event delivery from Activity->ViewGroup and the process of ViewGroup event distribution. The previous article is here (funny.jpg). It is recommended to read this article after reading this, and the idea will be much clearer:

Android event distribution mechanism decryption one (ViewGroup distribution)
Insert picture description here

This article mainly introduces View's handling of events. Obviously, View event processing is in the dispatchTouchEvent() method, directly uploading the code (the source code analyzed in this article is based on Android 10, the source code of each version is somewhat different, but the basic principles are the same):

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 event processing summary

When the user clicks on the control:
flow chart
key point: onTouch() has a higher priority than onClick()

1.4 Case 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;
            }
        });
    }
}

The log is as follows:
1. onTouch returns 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 returns 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

I believe that after seeing the two articles combined here, you should have a general understanding of Android event distribution and processing, and finally summarize:

  1. Android event distribution is first passed to the ViewGroup, and then passed to the View by the ViewGroup.

  2. In ViewGroup, the event delivery can be intercepted through the onInterceptTouchEvent method. The onInterceptTouchEvent method returns true to indicate that the event is not allowed to continue to be passed to the child View, and returns false to indicate that the event is not intercepted, and the default returns false.

  3. Distribute the event in dispatchTouch() of the ViewGroup. If the child View does not handle it, the ViewGroup will be treated as a View.

  4. If the passed events are consumed in the child View, no events will be received in the ViewGroup.

If you want to study in depth, you need to read the source code several times, you can combine the various situations of down and move events to analyze more. Later, when there is time, we will analyze the occurrence of the event. If you think this article is useful to you, please like and add to favorites✨.

Links to related reference articles:
Aunt Guo's event distribution mechanism is fully resolved.
Android event distribution mechanism is explained in detail: the most comprehensive and easy-to-understand in history

Guess you like

Origin blog.csdn.net/qq_32865887/article/details/114887309