背景:在一个事件分发复杂的view中,插入一个可点击的控件,且不能影响到本身的事件分发。
尝试 :
1.对view本身设置点击事件;由于view整体是交由root view去处理分发,存在滑动、边界处理、调出其他界面等复杂操作。设置点击事件后,root view在该区域无法处理事件,fail。
2.覆写view的onTouchEvent;当返回true时,消费了事件,rootView无法处理,且无法将事件交还,不符合需求;当返回false时,因为整体事件分发复杂,会由其他控件消耗,后续事件无法获取,fail。
3.由于所有事件的分发顺序判断在rootView中,在rootView在处理时将事件传一份给当前view;当前view不参与事件分发的流程,只通过事件判断自己是否应该响应点击事件,pass。
实现:在onTouchEvent中插桩,执行该方法;插桩使用了单例的Controller,回调给对应的view
// gap,用于判断当前是否可以被认为是一个点击事件
private static final float TOUCH_GAP = 50f;
// 起始xy
private float mStartX, mStartY;
// 标识位,是否可以执行click事件
private boolean mShouldPerformClick;
public void handleTouchEvent(MotionEvent event) {
if (event == null) return;
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// down事件,初始化点击位置;如果点击事件在点击区间内,重置flag
mStartX = event.getRawX();
mStartY = event.getRawY();
if (isInArea(mStartX, mStartY)) {
mShouldPerformClick = true;
}
break;
case MotionEvent.ACTION_MOVE:
// 判断当前距离是否大于gap
float currentX = event.getRawX();
float currentY = event.getRawY();
if (mShouldPerformClick && (Math.abs(currentX - mStartX) > TOUCH_GAP
|| Math.abs(currentY - mStartY) > TOUCH_GAP)) {
mShouldPerformClick = false;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// 根据上面的判断,执行点击事件
if (mShouldPerformClick) {
yourOwnClickEvent();
}
mShouldPerformClick = false;
break;
default:
break;
}
}
// 判断是否down的位置在view上(此处通过rect去标识,自定义了点击区间)
private boolean isInArea(float startX, float startY) {
if (mRect.right <= 0) {
mContent.getGlobalVisibleRect(mRect);
}
return startX >= mRect.left && startX <= mRect.right
&& startY >= mRect.top && startY <= mRect.bottom;
}