写点什么

【View 系列】View 事件分发源码探索,android 高级开发实战

用户头像
Android架构
关注
发布于: 刚刚

}


} else {


//如果没有子 view 消费了事件 并且事件不是 down 事件 intercepted 为 true


intercepted = true;


}


...


//如果没有 cancel 而且 viewGroup 本身也没拦截事件 则进入这个代码块


if (!canceled && !intercepted) {


...


//如果事件是 down 事件 则进入代码块


if (actionMasked == MotionEvent.ACTION_DOWN


|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)


|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {


...


final int childrenCount = mChildrenCount;


if (newTouchTarget == null && childrenCount != 0) {


...


final View[] children = mChildren;


//倒序遍历 child 查看 child 是否消费了事件


for (int i = childrenCount - 1; i >= 0; i--) {


...


//child.canReceivePointerEvents() 标识 child 是否能接收触摸事件 判断条件是可见并且没有执行动画


//!isTransformedTouchPointInView(x, y, child, null) 用来判断触摸事件的 x,y 是否在 child 的矩形区域内


//两个判断有一个条件不满足就会遍历下一个 child


if (!child.canReceivePointerEvents()|| !isTransformedTouchPointInView(x, y, child, null)) {


continue;


}


...


//调用子 view 的 dispatchTouchEvent 在 view 的 dispatchTouchEvent 里面调用了 onTouchEv


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ent()


//如果子 view 消费了事件 则 dispatchTransformedTouchEvent()返回 true 进入代码块执行逻辑


if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {


...


//赋值 newTouchTarget


//addTouchTarget()方法会将 mFirstTouchTarget 赋值 方便下面的事件系列直接拿到 //mFirstTouchTarget.child 并调用其 dispatchTouchEvent 方法


newTouchTarget = addTouchTarget(child, idBitsToAssign);


//将其设置为 true 下面代码会用到


alreadyDispatchedToNewTouchTarget = true;


break;


}


}


...


}


}


if (mFirstTouchTarget == null) {


// mFirstTouchTarget == null 代表没有子 view 消费事件


// 如果没有子 view 消费事件那么以后则直接调用 dispatchTransformedTouchEvent 方法 结果实际上是直接调用自己的 onTouchEvent()


// 注意 第三个参数出的是 null 会直接调用到 super.dispatchTouchEvent()即 view 的 dispatchTouchEvent()


//view 的 dispatchTouchEvent 会调用 onTouchEvent


handled = dispatchTransformedTouchEvent(ev, canceled, null,


TouchTarget.ALL_POINTER_IDS);


} else {


//如果 mFirstTouchTarget 不为 null(代表有子 view 消费了事件) 则走进这个代码块


TouchTarget predecessor = null;


TouchTarget target = mFirstTouchTarget;


while (target != null) {


final TouchTarget next = target.next;


//down 事件的时候如果有子 view 消费事件了 alreadyDispatchedToNewTouchTarget 值会为 true 并且


//target 和 newTouchTarget 地址值是一样的


if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {


handled = true;


} else { //其他事件(除了 down)都会走这个代码块


//如果 viewGroup 拦截了事件 则会导致 cancelChild 为 true 此时传递子 view 的 event.action 是 ACTION_CANCEL


final boolean cancelChild = resetCancelNextUpFlag(target.child)


|| intercepted;


//继续调用子 view 的 dispatchTouchEvent


//注意第三个参数这时候传的是 target.child 并不是 null 内部会 target.child.dispatchTouchEvent()


if (dispatchTransformedTouchEvent(ev, cancelChild,


target.child, target.pointerIdBits)) {


handled = true;


}


//如果取消 child 事件


//那么会导致 mFirstTouchTarget = target.next


//通常单指事件 cancelChild 直接会导致 mFirstTouchTarget 为 null


if (cancelChild) {


if (predecessor == null) {


mFirstTouchTarget = next;


} else {


predecessor.next = next;


}


//回收 target


target.recycle();


target = next;


continue;


}


}


predecessor = target;


target = next;


}


}


return handled;


}


public boolean onInterceptTouchEvent(MotionEvent ev) {


...


return false;


}


private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,


View child, int desiredPointerIdBits) {


...


if (child == null) {


//调用 view 的 dispatchTouchEvent


handled = super.dispatchTouchEvent(transformedEvent);


else {


...


//调用子 view 的 dispatchTouchEvent


handled = child.dispatchTouchEvent(transformedEvent);


}


return handled;


}


//View.java


//View 的 dispatchTouchEvent


public boolean dispatchTouchEvent(MotionEvent event) {


...


//首先查看是否设置有 onTouchListener 如果有并且返回了 true 则不会执行 onTouchEvent


//下面的代码说明 onTouchListener 优先级高于 onTouchEvent


ListenerInfo li = mListenerInfo;


if (li != null && li.mOnTouchListener != null


&& (mViewFlags & ENABLED_MASK) == ENABLED


&& li.mOnTouchListener.onTouch(this, event)) {


result = true;


}


if (!result && onTouchEvent(event)) {


result = true;


}


...


return result;


}


public boolean onTouchEvent(MotionEvent event) {


...


//标识 view 是否可点击 设置 onClickListener 也会导致 clickable 为 true


final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE


|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)


|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;


if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {


switch(action) {


case MotionEvent.ACTION_UP:


...


//回调了 OnClickListener 的 onClick 方法


performClickInternal();


...


break;


}


//如果 view 可点击 直接就返回 true 了


return true;


}


//如果 view 不可点击 返回 false


return false;


}


复制代码


通过上面的源码分析,我们可以总结一下基础流程:


  • 首先 ACTION_DOWN 事件:

  • 刚开始我们会清除之前的所有 touch 状态,因为 ACTION_DOWN 是一系列事件的开头

  • 然后我们会询问自己的 onInterceptTouchEvent 方法 是否拦截事件 默认不拦截

  • 如果不拦截 倒序遍历 children,让每个子 view 调用自己的 dispatchTouchEvent 方法

  • 子 view 处理过程中分为:

  • 子 view 消费了事件 那么我们会赋值 mFirstTouchTarget 对象 并将 alreadyDispatchedToNewTouchTarget 置为 true 接着执行下面的代码块:


if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {handled = true; }


然后返回 handled;


  • 子 view 没消费事件或者是 viewGroup 自己拦截了 那么 mFirstTouchTarget 对象为空 直接走进去 dispatchTransformedTouchEvent()并在里面调用了自己的 onTouchEvent 方法然后返回 handled;

  • 后序事件(以 ACTION_MOVE 为例):

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
【View系列】View事件分发源码探索,android高级开发实战