View事件分发(三)-ViewGroup事件分发(源码分析)
2020-10-30 本文已影响0人
如愿以偿丶
1.ViewGroup的事件分发三个重要方法
1.1.dispatchTouchEvent
1.2.onInterceptTouchEvent
1.3.onTouchEvent
2.通过上述三个方法阅读源码
1.dispatchTouchEvent源码
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
//当手指按下ACTION_DOWN
if (actionMasked == MotionEvent.ACTION_DOWN) {
...
//1.主要是将清除mFirstTouchTarget置为空 mFirstTouchTarget = null
cancelAndClearTouchTargets(ev);
}
//检查是否拦截(默认false)
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//请求父容器不拦截,默认false
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//2.调用onInterceptTouchEvent();
intercepted = onInterceptTouchEvent(ev);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
// 检查是否为取消 false
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// 声明newTouchTarget
TouchTarget newTouchTarget = null;
// canceled和intercepted都为false,进入此方法
if (!canceled && !intercepted) {
//手指按下,或多点按下..
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;
//倒序的一个循环,应该是处理RelativeLayout,从上倒下获取每一个View
for (int i = childrenCount - 1; i >= 0; i--) {
...
newTouchTarget = getTouchTarget(child);
...
//3.重要方法,该方法会调用子View的dispatchTouchEvent或者父类的dispatchTouchEvent
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
...
//添加touchTarget,并给mFirstTouchTarget也赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
break;
}
}
}
}
}
return handled;
}
1.1 cancelAndClearTouchTargets(ev);源码
/**
* Cancels and clears all touch targets.
*/
private void cancelAndClearTouchTargets(MotionEvent event) {
if (mFirstTouchTarget != null) {
...
//清除TouchTargets
clearTouchTargets();
}
}
/**
* Clears all touch targets.
*/
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
//将mFirstTouchTarget置为空
mFirstTouchTarget = null;
}
}
1.2.onInterceptTouchEvent源码
//默认返回false
public boolean onInterceptTouchEvent(MotionEvent ev) {
...
return false;
}
1.3.dispatchTransformedTouchEvent源码
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
if (child == null) {
//如果没有子View,调用父类的dispatchTouchEvent(event);,就回到了上一节讲的View的事件分发
handled = super.dispatchTouchEvent(event);
} else {
//否则调用子View的dispatchTouchEvent(event);,后续就是View的事件分发。此时就走了一遍了。
handled = child.dispatchTouchEvent(event);
}
return handled;
}
...
transformedEvent.recycle();
return handled;
}
由源码可知:
1.ViewGroup的dispatchTouchEvent,经过一系列判断,当手指按下,首先调用onInterceptTouchEvent,onInterceptTouchEvent默认返回false不拦截,在通过for循环遍历child,如果child为空,调用View的dispatchTouchEvent,否则,调用子View的dispatchTouchEvent。之后就回到了View的事件分发。
通过代码测试
1.自定义ViewGroup
//自定义ViewGroup
public class TouchViewGroup extends LinearLayout {
public TouchViewGroup(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
//事件分发
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("TAG","ViewGroup dispatchTouchEvent----"+ev.getAction());
return super.dispatchTouchEvent(ev);
}
//事件拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("TAG","ViewGroup onInterceptTouchEvent----"+ev.getAction());
return super.onInterceptTouchEvent(ev);
}
//事件触摸
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG","ViewGroup onTouchEvent----"+event.getAction());
return super.onTouchEvent(event);
}
2.自定义View
public class TouchView extends View {
public TouchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("TAG","View dispatchTouchEvent----"+event.getAction());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG","View onTouchEvent----"+event.getAction());
return super.onTouchEvent(event);
}
}
3.布局文件
<com.yc.drawdashboard.touch.TouchViewGroup 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"
tools:context=".twelve.ScalableActivity">
<com.yc.drawdashboard.touch.TouchView
android:id="@+id/touchview"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@color/colorPrimary"/>
</com.yc.drawdashboard.touch.TouchViewGroup>
4.主页面代码,添加onTouchListener,onClickListener
public class ScalableActivity extends AppCompatActivity {
private TouchView mTouchView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scalable);
mTouchView = findViewById(R.id.touchview);
mTouchView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG","View onTouch----"+event.getAction());
return false;
}
});
mTouchView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG","View onClick");
Toast.makeText(ScalableActivity.this,"onClick",Toast.LENGTH_SHORT).show();
}
});
}
}
通过手指点击屏幕打印执行顺序
第一种情况:
当ViewGroup和View的重写方法都返回默认值时。
执行顺序:
ViewGroup dispatchTouchEvent按下
ViewGroup onInterceptTouchEvent按下
View dispatchTouchEvent按下
View onTouch按下
View onTouchEvent按下
ViewGroup dispatchTouchEvent移动
ViewGroup onInterceptTouchEvent移动
View dispatchTouchEvent移动
View onTouch移动
View onTouchEvent移动
ViewGroup dispatchTouchEvent抬起
ViewGroup onInterceptTouchEvent抬起
View dispatchTouchEvent抬起
View onTouch抬起
View onTouchEvent抬起
onClick
动画效果:
第二种情况:
当View的onTouchEvent返回true时,其他都不变
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG","View onTouchEvent----"+event.getAction());
return true;
}
执行顺序:
ViewGroup dispatchTouchEvent按下
ViewGroup onInterceptTouchEvent按下
View dispatchTouchEvent按下
View onTouch按下
View onTouchEvent按下
ViewGroup dispatchTouchEvent移动
ViewGroup onInterceptTouchEvent移动
View dispatchTouchEvent移动
View onTouch移动
View onTouchEvent移动
ViewGroup dispatchTouchEvent抬起
ViewGroup onInterceptTouchEvent抬起
View dispatchTouchEvent抬起
View onTouch抬起
View onTouchEvent抬起
此时不调用onClick,因为View的onTouchEvent没有调用super.onTouchEvent(event)
动画效果:
第三种情况:
将View的onClickListener去掉,其他都不变
执行顺序:
ViewGroup dispatchTouchEvent按下
ViewGroup onInterceptTouchEvent按下
View dispatchTouchEvent按下
View onTouch按下
View onTouchEvent按下
动画效果:
第四种情况:
将ViewGroup的onInterceptTouchEvent返回true,其他都不变
//事件拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("TAG","ViewGroup onInterceptTouchEvent----"+ev.getAction());
return true;
}
执行顺序:
ViewGroup dispatchTouchEvent按下
ViewGroup onInterceptTouchEvent按下
ViewGroup onTouchEvent按下
动画效果: