android基础事件流程分析
建立嵌套的布局xml
<com.g.grefresh.ViewGroup1
android:id="@+id/parent1"
android:orientation="vertical"
android:layout_width="300dp"
android:background="@android:color/holo_orange_dark"
android:layout_height="300dp">
<com.g.grefresh.ViewGroup2
android:id="@+id/parent2"
android:orientation="vertical"
android:background="@color/colorPrimaryDark"
android:layout_width="200dp"
android:layout_height="200dp">
<com.g.grefresh.View1
android:id="@+id/view1"
android:background="@android:color/white"
android:layout_width="50dp"
android:layout_height="50dp">
</com.g.grefresh.View1>
</com.g.grefresh.ViewGroup2>
</com.g.grefresh.ViewGroup1>
布局分析:
最*的父布局 ViewGroup1。
ViewGroup1 的子布局ViewGroup2.
ViewGroup2拥有子View View1.
他们的事件代码如下:
ViewGroup1
override fun onTouchEvent(event: MotionEvent?): Boolean {
var result = super.onTouchEvent(event)
Log.e("gacmy","ViewGroup1 onTouchEvent:${result}")
return result
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
var result = super.dispatchTouchEvent(ev)
Log.e("gacmy","ViewGroup1 dispatchTouchEvent:${result}")
return result
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
var result = super.onInterceptTouchEvent(ev)
Log.e("gacmy","ViewGroup1 onInterceptTouchEvent:${result}")
return result
}
ViewGroup2
override fun onTouchEvent(event: MotionEvent?): Boolean {
var result = super.onTouchEvent(event)
Log.e("gacmy","ViewGroup2 onTouchEvent:${result}")
return result
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
var result = super.dispatchTouchEvent(ev)
Log.e("gacmy","ViewGroup2 dispatchTouchEvent:${result}")
return result
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
var result = super.onInterceptTouchEvent(ev)
Log.e("gacmy","ViewGroup2 onInterceptTouchEvent:${result}")
return result
}
View1
override fun onTouchEvent(event: MotionEvent?): Boolean {
var result = super.onTouchEvent(event)
Log.e("gacmy","View1 onTouchEvent:${result}")
return result
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
var result = super.dispatchTouchEvent(ev)
Log.e("gacmy","View1 dispatchTouchEvent:${result}")
return result
}
触发单击事件的日志打印情况:
log1:E/gacmy: ViewGroup1 onInterceptTouchEvent:false
log2:E/gacmy: ViewGroup2 onInterceptTouchEvent:false
log3:E/gacmy: View1 onTouchEvent:false
log4:E/gacmy: View1 dispatchTouchEvent:false
log5:E/gacmy: ViewGroup2 onTouchEvent:false
log6:E/gacmy: ViewGroup2 dispatchTouchEvent:false
log7:E/gacmy: ViewGroup1 onTouchEvent:false
log8:E/gacmy: ViewGroup1 dispatchTouchEvent:false
下面分析日志为什么这样打印
- 首先我们假设ViewGroup1 是最*的事件来源
- 我们需要从ViewGruop1的dispatchTouchEvent 作为起始入口点分析.
ViewGroup1 dispatchTouchEvent执行逻辑很简单
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
var result = super.dispatchTouchEvent(ev)
Log.e("gacmy","ViewGroup1 dispatchTouchEvent:${result}")
return result
}
首先执行ViewGroup1的父类 ViewGroup.java 的dispatchTouchEvent
这里需要注意的是 result = super.dispatchTouchEvent
需要等待父类的调用完成,才能打印返回。
打一个tag1 这里需要等待返回打印日志。
ViewGroup1的父类 ViewGroup dispatchTouchEvent执行逻辑分析
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
//第一次在ViewGroup1里产生的ACTION_DOWN事件
//if语句一定成立
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//设置 FLAG_DISALLOW_INTERCEPT 标志位 disallowIntercept 为true
//这里没有设置所以是false
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//ViewGroup1 没有设置 FLAG_DISALLOW_INTERCEPT 标记位所以会走到这里
//调用ViewGroup1 的onInterceptTouchEvent方法
//紧接着会调用ViewGroup1父类ViewGroup的onInterceptTouchEvent方法
//ViewGroup onInterceptTouchEvent 默认返回false
//ViewGroup1 获得父类的返回结果并且打印返回false
//log1 在这里打印 intercepted 返回false
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
...
//没有发送取消事件 并且intercepted = false 执行到这里
if (!canceled && !intercepted) {
//遍历ViewGroup1的子布局 它的里面只有ViewGroup2
for (int i = childrenCount - 1; i >= 0; i--) {
...
//这里调用ViewGroup dispatchTransformedTouchEvent 方法
//传入ViewGroup1 子布局 ViewGroup2 的对象
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
....
}
...
}
}
}
//ViewGroup1 父类ViewGroup调用的方法
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
...
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
//child 是ViewGroup2 对象不为空
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
//这里开始调用ViewGroup2 的dispatchTouchEvent
handled = child.dispatchTouchEvent(transformedEvent);
}
...
}
开始进入ViewGroup2 执行逻辑。
ViewGroup2 dispatchTouchEvent执行逻辑很简单
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
var result = super.dispatchTouchEvent(ev)
Log.e("gacmy","ViewGroup2 dispatchTouchEvent:${result}")
return result
}
首先执行ViewGroup2的父类 ViewGroup.java 的dispatchTouchEvent
这里需要注意的是 result = super.dispatchTouchEvent
需要等待父类的调用完成,才能打印返回。
打一个tag2 这里需要等待返回打印日志。
下面进入ViewGroup2的父类ViewGroup的执行逻辑
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
//第一次在ViewGroup2里产生的ACTION_DOWN事件
//if语句一定成立
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//设置 FLAG_DISALLOW_INTERCEPT 标志位 disallowIntercept 为true
//这里没有设置所以是false
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//ViewGroup2 没有设置 FLAG_DISALLOW_INTERCEPT 标记位所以会走到这里
//调用ViewGroup2 的onInterceptTouchEvent方法
//紧接着会调用ViewGroup2父类ViewGroup的onInterceptTouchEvent方法
//ViewGroup onInterceptTouchEvent 默认返回false
//ViewGroup2 获得父类的返回结果并且打印返回false
//log2 在这里打印 intercepted 返回false
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
...
//没有发送取消事件 并且intercepted = false 执行到这里
if (!canceled && !intercepted) {
//遍历ViewGroup2的子布局 它的里面只有View1
for (int i = childrenCount - 1; i >= 0; i--) {
...
//这里调用ViewGroup dispatchTransformedTouchEvent 方法
//传入ViewGroup2 子布局 View1 的对象
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
....
}
...
}
}
}
//ViewGroup2 父类ViewGroup调用的方法
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
...
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
//child 是View1 对象不为空
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
//这里开始调用View1 的dispatchTouchEvent
handled = child.dispatchTouchEvent(transformedEvent);
}
...
}
下面开始调用View1 dispatchTouchEvent方法
//View1 首先调用自己父类View的 dispatchTouchEvent方法
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
var result = super.dispatchTouchEvent(ev)
//这里等待返回打印日志 打一个标记tag3
Log.e("gacmy","View1 dispatchTouchEvent:${result}")
return result
}
//View.java dispatchTouchEvent方法的调用
public boolean dispatchTouchEvent(MotionEvent event) {
...
if (onFilterTouchEventForSecurity(event)) {
//如果EABLED 而且scrollbar正在被滑动 不会触发onTouchEvent事件
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//onTouchListener 不等与null
//而且onTouch返回true
//就不会调用onTouchEvent事件
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//scrollbar 没有滑动 而且 没有设置onTouchListener就会调用
//onTouchEvent onTouchEvent返回true 结果返回true
//onTouchEvent 返回false结果返回false
//在这里View 调用 自己子类View1的onTouchEvent方法
//等待子类View1的结果返回
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result
}
//View1 onTouchEvent方法调用
override fun onTouchEvent(event: MotionEvent?): Boolean {
//首先调用父类的View.java 的onTouchEvent方法
var result = super.onTouchEvent(event)
//接着等待父类onTouchEvent方法的返回 View.java clickable 属性默认是false 这里返回false
//log3在这里调用
Log.e("gacmy","View1 onTouchEvent:${result}")
return result
}
- 这里View1 onTouchEvent返回false之后,就会返回到父类的方法,dispatchTouchEvent方法中.
- dispatchTouchEvent onTouchEvent返回false View1 父类 View.java dispatchTouchEvent也会返回结果false。
- 接下来返回到tag3,View1调用父类的dispatchTouchEvent 等待返回结果
- 打印日志 log4…
- view1 dispatchTouchEvent 是由ViewGroup2的dispatchTransformedTouchEvent调用
- ViewGroup2 获取到view1 dispatchTouchEvent返回结果false dispatchTransformedTouchEvent 也会跟着返回false
- ViewGroup2 dispatchTransformedTouchEvent方法是由ViewGroup2的父类dispatchTouchEvent调用
下面继续分析ViewGroup2 父类dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
.....
for (int i = childrenCount - 1; i >= 0; i--) {
//来自View1的返回值 返回false ViewGroup2 只有一个View1 循环结束了
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
....
}
}
.....
//ViewGroup2 子类View1 的dispatchTouchEvent返回false
//这里mFirstTouchTarget == null 成立
//mFirstTouchTarget 是事件序列的链表头,存储的是子View 和发送的事件类型,如果子View dispatchTouchEvent返回true
//接收了事件则会给这个链表添加新的头部
//返回false 则不会为这个事件添加到链表里面,这里因为ViewGroup2只有一个子View,而且返回的fasle所以 mFirstTouchTarget是null
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
//这里caceled flase childView 也是null
//会调用ViewGroup2 父类ViewGroup的dispatchEvent
//然后调用ViewGroup2 的onTouchEvent方法 //log5就在这系列调用中发生
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
}
上面的ViewGroup.java dispatchTouchEvent,是由ViewGroup2的dispatchTouchEvent调用的。所以返回到ViewGroup2 的dispatchTouchEvent 方法中 log6发生
ViewGroup2 的dispatchTouchEvent 是由ViewGroup1 的父类ViewGroup dispatchTouchEvent调用的。接下来分析 ViewGroup1 父类ViewGroup dispatchTouchEvent方法,类似于ViewGroup2的父类ViewGroup的dispatchTouchevent()方法的调用,这里mFirstTouchTarget == null 所以会调用,ViewGroup的 dispatchTouchEvent 方法,最终会调用ViewGroup1的onTouchEvent方法 log7发生
上面的返回后,就会返回到起点ViewGroup1的 dispatchTouchEvent中去
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
var result = super.dispatchTouchEvent(ev)
//这里返回fasle 调用日志方法
Log.e("gacmy","ViewGroup1 dispatchTouchEvent:${result}")
//最终回调到我们事件的起点位置了
return result
}
这里View1发生点击事件,默认重写,事件传递的方法,只进行了一次ACTION_DOWN 事件的分发,后续滑动的ACTION_MOVE事件他们都收不到了?
- 这是因为我们的真正的事件起点不是ViewGroup1,而是Activity的DecorView,他也是一个FrameLayout布局,只需要分析它的dispatchTouchEvent代码就可了,也就是ViewGroup的dispatchTouchEvent
下面ViewGroup默认是真正的根布局的父类
public boolean dispatchTouchEvent(MotionEvent ev) {
...
//根布局获得ViewGroup1的返回false之后
//根布局再产生MOVE事件的话
//actionMasked == MotionEvent.ACTION_DOWN 不成立
//mFirstTouchTarget != null 也不成立 子View dispatchTouchEvent 因为返回false之后
//mFirstTouchTarget 没有添加任何节点 导致这个条件也不成立
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
//上面条件不成立 所以intercepted = true
//后面不会再进行 循环调用子View的dispatchTouchEvent方法了
//事件不会再传递下去
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
}
本文地址:https://blog.csdn.net/gacmy/article/details/110291916