从OnClickListener理解事件分发机制

阅读之前建议先看从源码分析Android 事件分发机制_z936689039的博客-CSDN博客,可以更方便的理解里面奥妙

setOnCLickLinstener,只要写过 Android 的同学应该都见过,大家都知道是点击事件监听,但是是怎么实现的呢?对,你没有猜错,就是回调
你在 onClick(View view)中写的方法,就是一个回调方法,然后通过这个方法实现了点击监听,那么问题来了,这个方法它没有在activity或者fragment里头出现,那它是怎么实现的呢,下面直接从源码的角度看这玩意咋跑的

 tvKes.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

先整个点击事件监听,然后ctrl点那个setOnClickListener方法进去看,然后我们会看到

   public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }
这段代码意思就是说将点击状态设置为true,其中setOnClickListener 方法就如同我们调用时的那样,传入一个 OnClickListener 对象作为参数,那我们来看一看 OnClickListener 是个啥子
 public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }
果然如我们所料,这个是个接口来的。
同时我们注意到getListenerInfo()这个方法,哎,就是玩,点进去看是什么鬼

//getListenerInfo()返回一个 ListenerInfo,如果 mListenerInfo 已经存在,
//就返回,如果不存在,就 new 一个返回
    @UnsupportedAppUsage
    ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }


  static class ListenerInfo {

        @UnsupportedAppUsage
        ListenerInfo() {
        }

        /**
         * Listener used to dispatch focus change events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        @UnsupportedAppUsage
        protected OnFocusChangeListener mOnFocusChangeListener;

        /**
         * Listeners for layout change events.
         */
        private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;

        protected OnScrollChangeListener mOnScrollChangeListener;

        /**
         * Listeners for attach events.
         */
        private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;

        /**
         * Listener used to dispatch click events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        @UnsupportedAppUsage
        public OnClickListener mOnClickListener;

        /**
         * Listener used to dispatch long click events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        @UnsupportedAppUsage
        protected OnLongClickListener mOnLongClickListener;

        /**
         * Listener used to dispatch context click events. This field should be made private, so it
         * is hidden from the SDK.
         * {@hide}
         */
        protected OnContextClickListener mOnContextClickListener;

        /**
         * Listener used to build the context menu.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        @UnsupportedAppUsage
        protected OnCreateContextMenuListener mOnCreateContextMenuListener;

        @UnsupportedAppUsage
        private OnKeyListener mOnKeyListener;

        @UnsupportedAppUsage
        private OnTouchListener mOnTouchListener;

        @UnsupportedAppUsage
        private OnHoverListener mOnHoverListener;

        @UnsupportedAppUsage
        private OnGenericMotionListener mOnGenericMotionListener;

        @UnsupportedAppUsage
        private OnDragListener mOnDragListener;

        private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;

        OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;

        OnCapturedPointerListener mOnCapturedPointerListener;

        private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;

        WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback;

        /**
         * This lives here since it's only valid for interactive views.
         */
        private List<Rect> mSystemGestureExclusionRects;

        /**
         * Used to track {@link #mSystemGestureExclusionRects}
         */
        public RenderNode.PositionUpdateListener mPositionUpdateListener;

        /**
         * Allows the application to implement custom scroll capture support.
         */
        ScrollCaptureCallback mScrollCaptureCallback;
    }

 从中我们发现getListenerInfo()是获取一个ListenerInfo的内部静态类,其中我们发现OnClickListener跟OnLongClickListener均是其接口引用.再回到view中的setOnClickListener,我们发现它本质上就是将我们传递进去的OnClickListener实现类赋值给了ListenerInfo的mOnClickListener 对象,然后ctrl这个对象,看它在哪里调用了其onClick方法

然后我们发现在view的performClick中有调用到

然后再看performClick在哪里调用的

 最终指向的是View的onTouchEvent的ACTION_UP的时候调用的,其中实现是用PerformClick异步执行,若不成功再调用performClick(),performClick()则执行OnClickListener中的onClick方法

然后用断点的方式去分析实际执行过程

我们看到它先执行了DetorView的dispatchEvent

然后执行了activity的dispatchTouchEvent

然后执行了View的dispatchTouchEvent

然后再这个方法里头执行onTouchEvent方法


因为onClick事件在手势上分成3个动作按下(MotionEvent.ACTION_DOWN),抬起(MotionEvent.ACTION_UP),所以它会先执行MotionEvent.ACTION_DOWN,此时VIew的dispatchTouchEvent返回true的,于是又回到了DetorView的dispatchTouchEvent中

可以发现在ACTION_UP方法的时候执行了点击操作,然后同样还是这一系列操作执行完后View的dispatchTouchEvent还是返回true,然后又回到了梦开始的地方,detorView的dispatchTouchEvent

然后同时执行了点击的方法操作

然后就很明了啦,后面就执行了onClick的执行方法了

到这里整个流程就走完了
其中布局是这样的:


总结: 

 onClickListener点击事件流程就是:DetorView:dispatchTouchEvent->Activity:dispatchTouchEvent->View:disPatchTouchEvent->View:onTouchEvent->View:OnTouchEvent-ACTION_DOWN->.然后把前面的流程走遍->View:OnTouchEvent-ACTION_UP->performClick->DetorView:dispatchTouchEvent

问题:

1.onTouchEvent,OnClickListener,dispatchTouchEvent,onTouchListener四者执行的顺序?

答:dispatchTouchEvent->onTouchListener->onTouchEvent->OnClickListener

 
 
 
 

 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/z936689039/article/details/121570844