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

Android拖拽布局触摸事件冲突:长按拖拽与删除按钮点击失效排查

问题描述

我使用DragLinearLayout类实现了拖拽功能,现在希望实现长按布局时启用拖拽,并在拖拽模式下为每个布局添加删除按钮。

当前实现思路:长按后将每个子视图嵌套到FrameLayout中,为FrameLayout添加关闭按钮后再放入DragLinearLayout,但遇到两个问题:

  1. 添加FrameLayout后无法拖拽;
  2. 拦截FrameLayout触摸事件后可拖拽,但关闭按钮无法点击,重写其onTouch事件也无效。
现有代码
private void doLongPressProcess() {

    for (int i = 0; i < dragLayout.getChildCount(); i++) {

        View child = dragLayout.getChildAt(i);
        child.setId(View.generateViewId());
        child.setTag("" + i);
        FrameLayout childFrame = getFrameLayout(i);

        dragLayout.removeViewAt(i);
        childFrame.addView(child);
        dragLayout.addView(childFrame, i);

        if (i != 0 && i != 2) {
//To enable drag and drop only for 0 and 2nd position
            dragLayout.setViewDraggable(childFrame, childFrame);
            ImageView closeButton = getImageView(child, dragLayout);
            (childFrame).addView(closeButton);
        } else {
            dragLayout.disableViewDraggable(child, child);
        }
    }

}

@NonNull
private FrameLayout getFrameLayout(int i) {

    FrameLayout childFrame = new FrameLayout(context) {

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return true;
        }
    };
    childFrame.setTag("Frame" + i);
    childFrame.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT,
            WRAP_CONTENT));
    GradientDrawable border = new GradientDrawable();
    border.setStroke(1, Color.BLACK); //black border with full opacity
    childFrame.setBackground(border);
    return childFrame;
}


@NonNull
private ImageView getImageView(View child, dragLayout dragLayout) {
    ImageView closeButton = new androidx.appcompat.widget.AppCompatImageView(context) {

        @Override
        public void setOnTouchListener(OnTouchListener l) {
            super.setOnTouchListener(l);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // Notify parent that child might handle the click
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_UP:
                    // Handle the click event
                    performClick();
                    // Notify parent that child has handled the click
                    break;
                case MotionEvent.ACTION_CANCEL:
                    // Cancel the click handling
                    break;
            }
            return super.onTouchEvent(event);
        }
    };
    closeButton.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
    closeButton.setImageResource(R.drawable.ic_close);
    closeButton.setTag(child.getTag());
    closeButton.setY(0);
    closeButton.setX(0);
    closeButton.setOnClickListener(v -> {
        int size = dragLayout.getChildCount();
        for (int i = 0; i < size; i++) {

            if (dragLayout.getChildAt(i).getTag().equals(v.getTag())) {
                dragLayout.removeViewInLayout(dragLayout.getChildAt(i));
                break;
            }
        }
    });
    return closeButton;
}
解决方案

1. 修复FrameLayout的触摸拦截逻辑

不要直接拦截所有触摸事件,而是判断触摸点是否在关闭按钮范围内,仅在非按钮区域拦截事件给拖拽逻辑:

@NonNull
private FrameLayout getFrameLayout(int i) {

    FrameLayout childFrame = new FrameLayout(context) {

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            // 查找关闭按钮
            View closeBtn = findViewWithTag("close_btn_" + i);
            if (closeBtn != null) {
                int[] location = new int[2];
                closeBtn.getLocationOnScreen(location);
                int btnLeft = location[0];
                int btnTop = location[1];
                int btnRight = btnLeft + closeBtn.getWidth();
                int btnBottom = btnTop + closeBtn.getHeight();
                // 判断触摸点是否在按钮内
                boolean isTouchOnBtn = ev.getRawX() >= btnLeft && ev.getRawX() <= btnRight
                        && ev.getRawY() >= btnTop && ev.getRawY() <= btnBottom;
                if (isTouchOnBtn) {
                    // 触摸按钮时不拦截,让按钮处理事件
                    return false;
                }
            }
            // 非按钮区域,拦截事件给DragLinearLayout处理拖拽
            return super.onInterceptTouchEvent(ev);
        }
    };
    childFrame.setTag("Frame" + i);
    childFrame.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
    GradientDrawable border = new GradientDrawable();
    border.setStroke(1, Color.BLACK);
    childFrame.setBackground(border);
    return childFrame;
}

2. 修正关闭按钮的布局参数与点击逻辑

父容器是FrameLayout,需用FrameLayout.LayoutParams定位,同时简化触摸处理:

@NonNull
private ImageView getImageView(View child, DragLayout dragLayout, int position) {
    ImageView closeButton = new androidx.appcompat.widget.AppCompatImageView(context);
    // 给按钮设置唯一tag用于查找
    closeButton.setTag("close_btn_" + position);
    // 使用FrameLayout布局参数定位按钮
    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
    params.gravity = Gravity.TOP | Gravity.START;
    closeButton.setLayoutParams(params);
    closeButton.setImageResource(R.drawable.ic_close);
    closeButton.setTag(child.getTag());
    closeButton.setOnClickListener(v -> {
        // 通过tag直接查找并移除对应布局
        View targetFrame = dragLayout.findViewWithTag("Frame" + v.getTag());
        if (targetFrame != null) {
            dragLayout.removeView(targetFrame);
        }
    });
    return closeButton;
}

3. 修正长按处理中的拖拽逻辑错误

原代码注释与判断逻辑相反,需调整拖拽启用的条件:

// 原错误逻辑:i != 0 && i != 2
// 修改为:仅0和2位置启用拖拽
if (i == 0 || i == 2) {
    dragLayout.setViewDraggable(childFrame, childFrame);
    ImageView closeButton = getImageView(child, dragLayout, i);
    childFrame.addView(closeButton);
} else {
    dragLayout.disableViewDraggable(childFrame, childFrame);
}

内容的提问来源于stack exchange,提问作者Vijayadhas Chandrasekaran

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:50:14