阅读之前建议先看从源码分析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,onTouch四者执行的顺序?
答:dispatchTouchEvent->onTouch->onTouchEvent->OnClickListener,因为在onTouche在disPatchTouchEvent会提前判断的,这块在之前文章也提到过,如果onTouchLisener.onTouch返回true的话,那么View的onTouchEvent就不会走了,所以如果你写了2个方法,一个是setOnClickListener和一个setOnTouchEvent,并且setOnTouchEvent返回true的话,那么根据刚刚的流程,此时是会直接执行View的onTouch方法的,而不会执行setOnClick.onClick方法的
|