按下操作起始于View外部时,如何检测该View是否被触摸?
解决ScrollView中跨View触摸释放的检测问题
当触摸起始于目标View外部时,目标View的OnTouchListener不会收到任何触摸事件——因为Android的触摸事件传递规则是:只有按下事件落在View的范围内,后续的滑动、抬起事件才会分发给该View。针对你在ScrollView中的场景,有以下几种可行方案:
方案1:给ScrollView添加全局触摸监听,在ACTION_UP时校验坐标
你之前认为全局坐标判断不可行,但只要正确获取目标View的屏幕坐标范围,就能准确判断抬起操作是否落在View3上,同时不会影响ScrollView的正常滑动:
scrollView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { // 获取View3的屏幕坐标 int[] viewLocation = new int[2]; view3.getLocationOnScreen(viewLocation); int viewLeft = viewLocation[0]; int viewTop = viewLocation[1]; int viewRight = viewLeft + view3.getWidth(); int viewBottom = viewTop + view3.getHeight(); // 获取触摸抬起时的屏幕坐标 float touchX = event.getRawX(); float touchY = event.getRawY(); // 判断触摸点是否在View3范围内 if (touchX >= viewLeft && touchX <= viewRight && touchY >= viewTop && touchY <= viewBottom) { // 执行View3上释放的逻辑 handleView3Release(); } } return false; // 返回false,让ScrollView正常处理滑动事件 } });
方案2:自定义ScrollView,重写onTouchEvent
如果想把触摸判断逻辑封装起来,可以自定义ScrollView,在内部处理抬起事件的坐标校验:
public class CustomScrollView extends ScrollView { private View mTargetView; private OnTargetReleaseListener mListener; public CustomScrollView(Context context) { super(context); } public CustomScrollView(Context context, AttributeSet attrs) { super(context, attrs); } // 设置要检测的目标View和监听回调 public void setTargetReleaseView(View targetView, OnTargetReleaseListener listener) { mTargetView = targetView; mListener = listener; } @Override public boolean onTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_UP && mTargetView != null && mListener != null) { int[] location = new int[2]; mTargetView.getLocationOnScreen(location); int left = location[0]; int top = location[1]; int right = left + mTargetView.getWidth(); int bottom = top + mTargetView.getHeight(); float touchX = ev.getRawX(); float touchY = ev.getRawY(); if (touchX >= left && touchX <= right && touchY >= top && touchY <= bottom) { mListener.onTargetReleased(); } } return super.onTouchEvent(ev); } // 自定义回调接口 public interface OnTargetReleaseListener { void onTargetReleased(); } }
使用时,在布局中替换为CustomScrollView,然后设置目标View和监听:
CustomScrollView customScrollView = findViewById(R.id.custom_scroll_view); customScrollView.setTargetReleaseView(view3, new CustomScrollView.OnTargetReleaseListener() { @Override public void onTargetReleased() { handleView3Release(); } });
方案3:结合GestureDetector处理触摸手势
如果需要同时处理其他触摸手势,可以用GestureDetector辅助,在抬起时做坐标判断:
GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { // 可以在这里处理其他手势,比如滑动、双击等 }); scrollView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { gestureDetector.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_UP) { checkTouchInView3(event); } return false; } }); // 提取坐标判断逻辑为单独方法 private void checkTouchInView3(MotionEvent event) { int[] location = new int[2]; view3.getLocationOnScreen(location); int left = location[0]; int top = location[1]; int right = left + view3.getWidth(); int bottom = top + view3.getHeight(); float x = event.getRawX(); float y = event.getRawY(); if (x >= left && x <= right && y >= top && y <= bottom) { handleView3Release(); } }
关键说明
Android触摸事件的传递是按下事件决定后续事件的接收者:只有当ACTION_DOWN落在某个View范围内,该View才会收到后续的ACTION_MOVE和ACTION_UP。所以从View B按下后,所有触摸事件都会传递给View B或其上层容器,View3永远不会收到事件,必须从ScrollView(父容器)层面去拦截判断。
内容的提问来源于stack exchange,提问作者aWiseMan
相关产品推荐
相关产品推荐

