Android事件传递(一):Activity、View、ViewGroup及dispatchtouchEvent、onTouchEvent梳理
Android事件传递(二):事件动作 DOWN 在Activity、View、ViewGroup传递
下面源码基于Android11 API30
上一篇文章分析了DOWN事件以后布局中每个对象的状态:
UP动作也是和DOWN一样从Activty开始到ViewGroup的dispatchTouchEvent方法,我们就从这里开始看,我们就用例子中的布局控件进行分析,先看DecorView :
DecorView extends ... extends ViewGroup:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
......
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
✍ 当前动作为ACTION_UP但是在DecorView中
mFirstTouchTarget不为null,所以会进此判断
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;
}
✍到这里我们没有使用onInterceptTouchEvent拦截事件所以intercepted = false
} else {
intercepted = true;
}
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
✍注意这里哈,UP事件又执行到这里重新初始化这两个的值 null 和 false !
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
✍以为当前是ACTION_UP事件所以不会进入此判断
}
}
✍DecorView里mFirstTouchTarget != null
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
✍所以看这里
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
✍ 还记得上面重新初始化两个值
alreadyDispatchedToNewTouchTarget = false
newTouchTarget = null
而 target = mFirstTouchTarget = TouchTarget@1862
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
✍到了这里,target.child就是mFirstTouchTarget.child = MyLinearLayoutOut还记得DOWN事件每层布局保存一个mFirstTouchTarget就是为了层层传递UP事件
然后 dispatchTransformedTouchEvent
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
......
}
......
return handled;
}
注意:
执行到这里和DOWN事件一样开始调用其子布局的dispatchTouchEvent方法,我们这里调用的就是MyLinearLayoutOut的dispatchTouchEvent方法,而DecorView就在这里等着子布局结果返回
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
MyLinearLayoutOut的dispatchTouchEvent方法和上面DecorVeiw的dispatchTouchEvent方法分析一样。
最终会调用到MyLinearLayout的dispatchTouchEvent方法:
MyLiearLayout extends ... extends ViewGroup:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
......
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
✍ 当前动作为ACTION_UP;在MyLiearLayout 中
mFirstTouchTarget为null
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
....
} else {
✍ 所以到这里!直接拦截
intercepted = true;
}
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
✍intercepted = true,不会进此判断!
if (!canceled && !intercepted) {
}
✍MyLiearLayout 里mFirstTouchTarget = null
if (mFirstTouchTarget == null) {
✍就走这里,然后就会调用MyLiearLayout 自身处理UP事件,就不会往里层布局传递UP事件了!!!!
因为我们重写了onTouchEvent方法返回true,如果你是给它设置一个clickListener就会回调click事件
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
}
......
}
......
✍这里就return true
return handled;
}
执行到MyLinearLayout开始往外层布局返回
MyLinearLayoutOut:
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)为true) {
handled = true;
}
return handled;
↓
↓
↓
DecorView:
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)为true) {
handled = true;
}
return handled;
这样renturn就直到根布局,UP动作就结束了。
最后附上源码及日志:
<?xml version="1.0" encoding="utf-8"?>
<com.sz.android11api30.MyLinearLayoutOut xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<com.sz.android11api30.MyLinearLayout
android:id="@+id/my_linearlayout"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.sz.android11api30.MyButton
android:id="@+id/my_btn"
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MyButton" />
</com.sz.android11api30.MyLinearLayout>
</com.sz.android11api30.MyLinearLayoutOut>
public class MyLinearLayoutOut extends LinearLayout {
public MyLinearLayoutOut(Context context) {
super(context);
}
public MyLinearLayoutOut(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyLinearLayoutOut(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
int num = 0;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LogUtils.logPrint(" MyLinearLayoutOut dispatchTouchEvent " + ActionUtils.getActionName(ev));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtils.logPrint(" MyLinearLayoutOut onTouchEvent " +ActionUtils.getActionName(event));
return super.onTouchEvent(event);
}
}
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context) {
super(context);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LogUtils.logPrint(" MyLinearLayout dispatchTouchEvent "+ ActionUtils.getActionName(ev));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtils.logPrint(" MyLinearLayout onTouchEvent " + ActionUtils.getActionName(event));
return true;
}
}
public class MyButton extends Button {
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
LogUtils.logPrint(" MyButton dispatchTouchEvent " + ActionUtils.getActionName(event));
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtils.logPrint(" MyButton onTouchEvent " + ActionUtils.getActionName(event));
return super.onTouchEvent(event);
}
}
日志:
2021-03-23 14:35:13.626 I/lyw: MainActivity dispatchTouchEvent ACTION_DOWN
2021-03-23 14:35:29.219 I/lyw: MyLinearLayoutOut dispatchTouchEvent ACTION_DOWN
2021-03-23 14:35:30.086 I/lyw: MyLinearLayout dispatchTouchEvent ACTION_DOWN
2021-03-23 14:35:31.723 I/lyw: MyButton dispatchTouchEvent ACTION_DOWN
2021-03-23 14:35:32.472 I/lyw: MyButton onTouchEvent ACTION_DOWN
2021-03-23 14:35:34.882 I/lyw: MyLinearLayout onTouchEvent ACTION_DOWN
2021-03-23 14:36:02.098 I/lyw: MainActivity dispatchTouchEvent ACTION_UP
2021-03-23 14:37:52.562 I/lyw: MyLinearLayoutOut dispatchTouchEvent ACTION_UP
2021-03-23 14:38:17.434 I/lyw: MyLinearLayout dispatchTouchEvent ACTION_UP
日志可以看出dispatchTouchEvent从外一直到最里面MyButton;
然后onTouchEvent从里到外到MyLinearLayout拦截处理事件,其外部父布局就不再判断是否需要处理此事件。
然后UP事件从外到里传递到接收处理事件的MyLinearLayout交给他处理就结束。