Android拖拽布局触摸事件冲突:长按拖拽与删除按钮点击失效排查
问题描述
我使用DragLinearLayout类实现了拖拽功能,现在希望实现长按布局时启用拖拽,并在拖拽模式下为每个布局添加删除按钮。
当前实现思路:长按后将每个子视图嵌套到FrameLayout中,为FrameLayout添加关闭按钮后再放入DragLinearLayout,但遇到两个问题:
- 添加FrameLayout后无法拖拽;
- 拦截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
相关产品推荐
相关产品推荐

