关闭

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

标签: Android设计模式责任链模式Android源码分析Android事件分发机制事件分发
80人阅读 评论(0) 收藏 举报
分类:
一、责任链模式简述
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系,
将这些对象形成一条链,并沿着这条链传递该请求,直到有对象处理它为止
 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 floatoffsetY = 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) {
returnmDecor.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
 
 
 
 
 






0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:34875次
    • 积分:1382
    • 等级:
    • 排名:千里之外
    • 原创:105篇
    • 转载:8篇
    • 译文:0篇
    • 评论:2条