事件分发的核心方法
组件 | dispatchTouchEvent | onTouchEvent | onInterceptTouchEvent |
---|---|---|---|
Activity | 存在 | 存在 | 不存在 |
ViewGroup | 存在 | 存在 | 存在 |
View | 存在 | 存在 | 不存在 |
事件分发流程图
image.png源码分析
1、事件从点击屏幕时触发,由手指点击屏幕,硬件通知底层,再调用java层,最后调用Activity的 public boolean dispatchTouchEvent(MotionEvent ev) 方法
/**
* 当一个点击操作发生时,事件传递顺序 Acivity - > Window ->DecorView DecorView 一般
* 就是当前界面的底层容器(即setContentView 所设置的View的 父容器),一个点击操作要是没有被Activity
* 下的任何View处理,即顶层DecorView的dispatchTouchEvent()方法返回false的话
* 则Activity的onTouchEvent()方法会调用
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
// 如果是down,说明是一个新的事件
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction(); // 空方法 添加自己的业务
}
//注释1 调用Window的superDispatchTouchEvent方法,吧事件从Activity分离到DecorView
if (getWindow().superDispatchTouchEvent(ev)) {
return true; //如果返回true
}
return onTouchEvent(ev);
}
注释1中调用Window的superDispatchTouchEvent,也就是调用PhoneWindow的superDispatchTouchEvent,而PhoneWindow的superDispatchTouchEvent又是调用DecorView的superDispatchTouchEvent,DecorView本身就是ViewGroup的实现类,相当于调用了ViewGroup的dispatchTouchEvent
## PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
## DecorView
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
滑动冲突的解决方法
1、制定合适的滑动策略
2、按滑动策略分发事件
制定合适的滑动策略
如何判断水平滑动还是竖直滑动?1、可以根据水平方向或者竖直方向的距离差来判断。2、可以根据滑动路径和水平方向的夹角来判断,小于30°则为水平方向,大于60°为竖直方向,30到60之间不处理,或者根据具体业务需求
按滑动策略分发事件
- 外部拦截法
外部拦截法一般用在容器中,父容器拦截子容器的事件,子容器没有响应。
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false; // 必须false,否则后续的MOVE和UP不再传递给子View
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要当前点击事件) {
intercepted = true;
} else {
intercepted = false;
}
break;
case MotionEvent.ACTION_UP:
intercepted = false; // 必须false,会影响子View的onClick是否被触发
break;
}
return intercepted;
}
- 内部拦截法
内部拦截法一般在子容器中实现,比如在ScrollView中嵌套的ScrollView,在第二个ScrollView中实现是否拦截事件。
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 关闭父元素的拦截功能,当前容器拦截
requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要此类点击事件) {
// 激活父元素原本的拦截功能 ,父容器拦截
requestDisallowInterceptTouchEvent(false);
break;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(event);
}
判断滑动的方向
/**
* @param startX 按下x 这个值不能在 onTouchEvent当局部变量,因为局部变量每次按下x,y都是0
* @param startY 按下y
* @param endX 结束x
* @param endY 结束y
* @return top = 1 bottom = 2 left = 3 right = 4
*/
public int getSlideDirection(float startX, float startY, float endX, float endY) {
final int top = 1;
final int bottom = 2;
final int left = 3;
final int right = 4;
float x = Math.abs(endX - startX);
float y = Math.abs(endY - startY);
double z = Math.sqrt(x * x + y * y);
int jiaodu = Math.round((float) (Math.asin(y / z) / Math.PI * 180));//角度
if (endY < startY && jiaodu > 45) {//上
Log.d(TAG, "角度:" + jiaodu + ", 動作:上");
return top;
} else if (endY > startY && jiaodu > 45) {//下
Log.d(TAG, "角度:" + jiaodu + ", 動作:下");
return bottom;
} else if (endX < startX && jiaodu <= 45) {//左
Log.d(TAG, "角度:" + jiaodu + ", 動作:左");
return left;
} else if (endX > startX && jiaodu <= 45) {//右
Log.d(TAG, "角度:" + jiaodu + ", 動作:右");
return right;
}
return -1;
}
float X = 0;
float Y = 0;
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 按下
X = event.getX();
Y = event.getY();
case MotionEvent.ACTION_UP: // 抬起
float upX = event.getX();
float upY = event.getY();
getSlideDirection(X, Y, upX, upY);
}
return super.onTouchEvent(event);
}
网友评论