小计事件分发
点击事件传递方向 activity->viewgroup(父类是View)->…->view.
1.activity 主要关心最后分发的结果,首先就向下分发,下面有消费者就结束,没有就调用activity的onTouchEvent(ev)
/**
* 源码分析:Activity.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
// 仅贴出核心代码
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
// 若getWindow().superDispatchTouchEvent(ev)的返回true
// 则Activity.dispatchTouchEvent()就返回true,则方法结束。即 :该点击事件停止往下传递 & 事件传递过程结束
// 否则:继续往下调用Activity.onTouchEvent
}
return onTouchEvent(ev);
}
————————————————
版权声明:本文为CSDN博主「Carson带你学Android」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/carson_ho/article/details/54136311
2.ViewGroup
Activity的getWindow().superDispatchTouchEvent(ev)向下分发给ViewGroup.DispatchTouchEvent(ev)
重点是:
viewgroup的前置onInterceptTouchEvent(ev)方法,决定了是否在这里先拦截,
若拦截,调用super.dispatchTouchEvent(ev); 即父类的view的dispatchTouchEvent(ev)方法,看3
不拦截 遍历子view的.dispatchTouchEvent(ev),向下分发
若都不消费,就也调用super.dispatchTouchEvent(ev);
/**
* 源码分析:ViewGroup.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
// 仅贴出关键代码
...
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// 分析2
// 1. 通过for循环,遍历当前ViewGroup下的所有子View
for (int i = count - 1; i >= 0; i--) {
if (child.dispatchTouchEvent(ev)) {
//实现了点击事件从ViewGroup到子View的传递(具体请看下面章节介绍的View事件分发机制)
//消费了就返回true
return true;
}
}
}
...
return super.dispatchTouchEvent(ev);
// 若无任何View接收事件(如点击空白处)/ViewGroup本身拦截了事件(复写了onInterceptTouchEvent()返回true)
// 会调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
// 因此会执行ViewGroup的onTouch() -> onTouchEvent() -> performClick() -> onClick(),即自己处理该事件,事件不会往下传递
// 具体请参考View事件分发机制中的View.dispatchTouchEvent()
...
}
/**
ViewGroup.onInterceptTouchEvent()
* a. 返回false:不拦截(默认)
* b. 返回true:拦截,即事件停止往下传递(需手动复写onInterceptTouchEvent()其返回true)
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 默认不拦截
return false;
}
————————————————
版权声明:本文为CSDN博主「Carson带你学Android」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/carson_ho/article/details/54136311
3. viewd的dispatchTouchEvent(ev)
重点在于 ontouch()和控件本来是否可以点击的属性,设置了 bool OnTouchListener(),并且根据返回值 ture 就消费了,整个事件结束,返回false,会接着判断是否能流向onclick(), 判断依据:控件本来是否可以点击的属性
/**
* 源码分析:View.dispatchTouchEvent()
*/
public boolean dispatchTouchEvent(MotionEvent event) {
if ( (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener != null &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
// 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()
// 1. (mViewFlags & ENABLED_MASK) == ENABLED
// 2. mOnTouchListener != null
// 3. mOnTouchListener.onTouch(this, event)
// 下面对这3个条件逐个分析
/**
* 条件1:(mViewFlags & ENABLED_MASK) == ENABLED
* 说明:
* 1. 该条件是判断当前点击的控件是否enable
* 2. 由于很多View默认enable,故该条件恒定为true(除非手动设置为false)
*/
/**
* 条件2:mOnTouchListener != null
* 说明:
* 1. mOnTouchListener变量在View.setOnTouchListener()里赋值
* 2. 即只要给控件注册了Touch事件,mOnTouchListener就一定被赋值(即不为空)
*/
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
}
/**
* 条件3:mOnTouchListener.onTouch(this, event)
* 说明:
* 1. 即回调控件注册Touch事件时的onTouch();
* 2. 需手动复写设置,具体如下(以按钮Button为例)
*/
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
// 若在onTouch()返回true,就会让上述三个条件全部成立,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束
// 若在onTouch()返回false,就会使得上述三个条件不全部成立,从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)
// onTouchEvent()源码分析 -> 分析1
}
});
/**
* 分析1:onTouchEvent()
*/
public boolean onTouchEvent(MotionEvent event) {
... // 仅展示关键代码
// 若该控件可点击,则进入switch判断中
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
// 根据当前事件类型进行判断处理
switch (event.getAction()) {
// a. 事件类型=抬起View(主要分析)
case MotionEvent.ACTION_UP:
performClick();
// ->>分析2
break;
// b. 事件类型=按下View
case MotionEvent.ACTION_DOWN:
break;
// c. 事件类型=结束事件
case MotionEvent.ACTION_CANCEL:
break;
// d. 事件类型=滑动View
case MotionEvent.ACTION_MOVE:
}
break;
}
// 若该控件可点击,就一定返回true
return true;
}
// 若该控件不可点击,就一定返回false
return false;
}
/**
* 分析2:performClick()
*/
public boolean performClick() {
if (mOnClickListener != null) {
// 只要通过setOnClickListener()为控件View注册1个点击事件
// 那么就会给mOnClickListener变量赋值(即不为空)
// 则会往下回调onClick() & performClick()返回true
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
————————————————
版权声明:本文为CSDN博主「Carson带你学Android」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/carson_ho/article/details/54136311