You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按下操作起始于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 21:20:28