移动架构13_责任链模式分析Android事件分发机制

Android移动架构汇总​​​​​​​
一、责任链模式简述
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系,
将这些对象形成一条链,并沿着这条链传递该请求,直到有对象处理它为止
 
  ss
 重点:上一个处理对象必须含有下一个处理对象的引用,形成一个单向链表
Android事件传递:OnTouchEvent---请求 ---》View  ViewGroup  Activity 都有机会处理他
 
ss
 
  二、以责任链模式对比事件分发
模式 事件分发
上一个对象持有下一个对象 Activity持有Window对象,Window包含View对象
请求传递 DispatchEvent:Activity传给Window->ViewGroup -(遍历)-View
处理请求 onTouchEvent
每个对象所处理请求不同的抽象 TouchEvent
 
 
 
三、Android事件分发机制
1、带着问题分析源码
1 )在ViewGroup中重写onInterceptTouchEvent方法返回true为什么会拦截事件,
并且该ViewGroup会消费了Event(调用onOnTouchEvent)。
2  )为什么事件不再向子控件继续传递?
3)当父控件没有拦截事件时,事件是如何传递到子控件的
4)点击事件中的x,y坐标值都是以父布局的相对坐标,这里又是如何一层一层转换的?
在ViewGroup的dispatchTransformedTouchEvent方法中:
if(child == null) {
handled = super.dispatchTouchEvent(event);
} else{
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
//做了偏移,View相对于ViewGroup的距离
event.offsetLocation(offsetX, offsetY);
 
handled = child.dispatchTouchEvent(event);
 
event.offsetLocation(-offsetX, -offsetY);
}
ss
 
5)为什么给自定义View设置OnTouchListener后不再调用 重写的onTouch方法
 
 2、事件分发机制分析
用户点击屏幕作为发送者 ,每个ViewGroup  和View作为处理者
1),用户点击屏幕作为发送者,Activity、Window、VieGroup作为接受者。他们之间任何对象都能处理事件请求
2)activity、Window、ViewGroup之间的依赖关系形成了调用链
3)事件可以依赖这条调用链传递请求
 
3、源码分析
1)Activity最先接受到事件,调用dispatchTouchEvent方法
public booleandispatchTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent. ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true ;
}
returnonTouchEvent(ev);
}
如果Window的 superDispatchTouchEvent()返回true,整个方法返回true;否则,会调用Activity的OnTouchEvent方法
 
2)Activity的分发会调用Window的superDispatchTouchEvent方法
Window仅有一个实现类,PhoneWindow;其实现了superDispatchTouchEvent方法:
public booleansuperDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
DecorView中:
public booleansuperDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
mDecor->DecorView ->FrameLayout -> ViewGroup
调用了ViewGroup的dispatchTouchEvent方法
 
3)ViewGroup的dispatchTouchEvent中
(1)如果ViewGroup的onInterceptTouchEvent返回true,则事件不会向下传递,会传递到ViewGroup的onTouchEvent中去
dispatchTouchEvent:
...
intercepted = onInterceptTouchEvent(ev);//true
....
if(!canceled && !intercepted) {//intercepted= true,不执行这段逻辑
....
finalView[] children = mChildren;
for( inti = childrenCount - 1; i >= 0; i--) {
//循环查找子View进行传递的逻辑
...
 
代码执行:
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget. ALL_POINTER_IDS);
(传入的第三个参数为null)
 
dispatchTransformedTouchEvent中:
if(child == null) {
handled = super.dispatchTouchEvent(event);
}
调用父类的dispatchTouchEvent,即View的dispatchTouchEvent:
if(li != null&& li. mOnTouchListener!= null
&& ( mViewFlags& ENABLED_MASK) == ENABLED
&& li. mOnTouchListener.onTouch( this, event)) {
result = true;
}
 
if(!result && onTouchEvent(event)) {
result = true;
}
 
如果设置了 mOnTouchListener,则不调用onTouchEvent方法;否则调用onTouchEvent方法
 
(2)如果ViewGroup的onInterceptTouchEvent返回false
就会走到上面没有执行的if中代码快,遍历ViewGroup的所有child
for( intj = 0; j < childrenCount; j++) {
判断事件是否在child的边界范围内,获取一个View类型的child
同样执行了dispatchTransformedTouchEvent方法,但这次传入的是遍历的child而不是null,进入dispatchTransformedTouchEvent方法,执行了代码:
handled = child.dispatchTouchEvent(event);
事件传递到了child中去,也就是调用了View的dispatchTouchEvent方法
 
 
4) View的dispatchTouchEvent方法
View的dispatchTouchEvent中:
if(!result && onTouchEvent(event)) {
result = true;
}
也可能调用View自己的onTouchEvent方法
 
如果View的onTouchEvent返回true,那么View的dispatchTouchEvent返回true;
返回到ViewGroup中调用View的dispatchTouchEvent地方,即dispatchTransformedTouchEvent方法,
dispatchTransformedTouchEvent(...){
....
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
returnhandled;
}
返回true;
 
而dispatchTransformedTouchEvent是在ViewGroup的dispatchTouchEvent中遍历子控件地方调用的
...
if(dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//进入到这段逻辑中
mLastTouchDownTime= ev.getDownTime();
if(preorderedList != null) {
// childIndex points into presorted list, find original index
for( intj = 0; j < childrenCount; j++) {
if(children[childIndex] == mChildren[j]) {
mLastTouchDownIndex= j;
break;
}
}
} else{
mLastTouchDownIndex= childIndex;
}
mLastTouchDownX= ev.getX();
mLastTouchDownY= ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break ;
}
 
如果找到了接受事件的控件,那么下一次就会直接去找它的onTouchEvent;
原因就是 addTouchTarget, mFirstTouchTarget 被赋值
privateTouchTarget addTouchTarget(@NonNull View child, intpointerIdBits) {
finalTouchTarget target = TouchTarget. obtain(child, pointerIdBits);
target. next= mFirstTouchTarget;//这也是一个责任链
mFirstTouchTarget= target;
returntarget;
}
而break使得其他子控件不会接触这个事件
 
 
5) 事件分发示意图
ss
 
 
注意:
通常一个ViewGroup事件不拦截,则传递给子ViewGroup,还不处理,继续向下传递,如果最终不传递;那么看父ViewGroup是否处理,不传递“向上”传递,直到传递给最上一层。但是值得注意的是,并没child.super处理事件的方法,也就是说并不是底层传递上来的。这是一个递归,有一点像二叉树的遍历,以一个ViewGroup为例,实际代码是看其child是否消费事件,如果没有被消费,然后 自己会去super.dispatchonTouchEvent(当前的父类,不是向上),来到自身的的onTouchEvent。总结: 不是向上,而是递归
ss
 
 
 
 
 
 
 
 
 
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值