java - 禁用或拦截窗外的掉落

标签 java swing drag-and-drop awt jtabbedpane

我已经实现了一组可拖动的选项卡,遵循这个例子的形式: How to implement draggable tab using Java Swing?

一切似乎都如我所愿,但是,当我拖出主面板时,桌面将成为有效的放置目标(结果放置被接受并标记为成功)。

有没有办法拦截这个掉落以对掉落到我们的根 Pane 之外使用react?它很容易检测到,但我不清楚如何在外界之前真正捕捉到液滴。

在 DragSourceListener 的 dragDropEnd 被调用时,拖放已经执行,并且似乎没有在 dragOver/Exit/Whatever 中结束拖拽的好方法。

哎呀,如果这样的事情能奏效就好了:

@Override
public void dragOver(DragSourceDragEvent dragEvent)
{
    DragEnabledTabTransferData data = getTabTransferData(dragEvent);
    DragSourceContext dragSourceContext = dragEvent.getDragSourceContext();
    if (data == null)
    {
        dragSourceContext.setCursor(DragSource.DefaultMoveNoDrop);
        return;
    }
    if (!data.getTabbedPane().getRootPane().getBounds().contains(dragEvent.getLocation()))
    {
        dragSourceContext.dragDropEnd(new DragSourceDropEvent(dragSourceContext, 999, true));
    }
}

相反,拖动会继续拖动。我知道,但是我的麻烦得到了一个 dragDropEnd。

有什么想法吗?听到唯一的解决方案是让一些隐藏的最大化全局 Pane 仅充当放置目标来捕获窗口外事件,我会感到非常难过。

这是一个工作示例。如果您将一个选项卡拖出,比如说,在 Linux 中的桌面上,它会尝试将传输数据转换为 Serializable 并且不高兴。如果您想直接跳到我在上面指出的内容,我正在玩的拖拽被评论为“这是我假设我们能够拦截东西的地方”。

/** "Simple" example of DnD tabbed panes. Sourced from Eugene Yokota:
 * http:stackoverflow.com/questions/60269/how-to-implement-draggable-tab-using-java-swing */
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import javax.swing.*;

public class DnDTabbedPane extends JTabbedPane {
    private static final String NAME = "TabTransferData";
    private final DataFlavor FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME);

    public DnDTabbedPane() {
        super();
        final DragSourceListener dsl = new DragSourceListener() {
            public void dragEnter(DragSourceDragEvent e) {
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
            }

            public void dragExit(DragSourceEvent e) {
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
            }

            /**
             * This is where I'd assume we'd be able to intercept stuff 
             * so drops don't happen where we don't want them to.
             */
            public void dragOver(DragSourceDragEvent e) {
                TabTransferData data = getTabTransferData(e);
                if (data == null) {
                    e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
                    return;
                }
                //This is where I ended up robokilling the drag via hackery
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
            }
            public void dragDropEnd(DragSourceDropEvent e) {}
            public void dropActionChanged(DragSourceDragEvent e) {}
        };

        final DragGestureListener dgl = new DragGestureListener() {
            public void dragGestureRecognized(DragGestureEvent e) {

                Point tabPt = e.getDragOrigin();
                int dragTabIndex = indexAtLocation(tabPt.x, tabPt.y);
                if (dragTabIndex < 0) {
                    return;
                }
                e.startDrag(DragSource.DefaultMoveDrop,new TabTransferable(DnDTabbedPane.this, dragTabIndex), dsl);
            }
        };

        new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, new CDropTargetListener(), true);
        new DragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
    }

    private TabTransferData getTabTransferData(DropTargetDropEvent a_event) {       
        try {
            return (TabTransferData) a_event.getTransferable().getTransferData(FLAVOR);             
        } catch (Exception e) {}

        return null;
    }

    private TabTransferData getTabTransferData(DropTargetDragEvent a_event) {
        try {
            return  (TabTransferData) a_event.getTransferable().getTransferData(FLAVOR);                
        } catch (Exception e) {}

        return null;
    }

    private TabTransferData getTabTransferData(DragSourceDragEvent a_event) {
        try {
            return (TabTransferData) a_event.getDragSourceContext().getTransferable().getTransferData(FLAVOR);              
        } catch (Exception e) {}

        return null;        
    }

    class TabTransferable implements Transferable {
        private TabTransferData m_data = null;
        private DataFlavor[] flavors = {FLAVOR};
        public TabTransferable(DnDTabbedPane a_tabbedPane, int a_tabIndex) {
            m_data = new TabTransferData(DnDTabbedPane.this, a_tabIndex);
        }

        public Object getTransferData(DataFlavor flavor) {
            return m_data;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return flavor.getHumanPresentableName().equals(NAME);
        }       
    }

    class TabTransferData {
        DnDTabbedPane m_tabbedPane = null;
        int m_tabIndex = -1;

        public TabTransferData(DnDTabbedPane a_tabbedPane, int a_tabIndex) {
            m_tabbedPane = a_tabbedPane;
            m_tabIndex = a_tabIndex;
        }
    }

    class CDropTargetListener implements DropTargetListener {
        public void dragEnter(DropTargetDragEvent e) {
            if (isDragAcceptable(e)) {
                e.acceptDrag(e.getDropAction());
            } else {
                e.rejectDrag();
            }
        }

        public void drop(DropTargetDropEvent a_event) {
            if (isDropAcceptable(a_event)) {
                convertTab(getTabTransferData(a_event),
                getTargetTabIndex(a_event.getLocation()));
                a_event.dropComplete(true);
            } else {
                a_event.dropComplete(false);
            }
        }

        private boolean isTransferableGood(Transferable t, DataFlavor flavor)
        {
            return t == null || t.isDataFlavorSupported(flavor);
        }

        private boolean isDataGood(TabTransferData data)
        {
            if (DnDTabbedPane.this == data.m_tabbedPane && data.m_tabIndex >= 0) {
                return true;
            }
            return false;
        }

        public boolean isDragAcceptable(DropTargetDragEvent e) {
            Transferable t = e.getTransferable();
            if (!isTransferableGood(t, e.getCurrentDataFlavors()[0])) {
                return false;
            }
            return isDataGood(getTabTransferData(e));
        }

        public boolean isDropAcceptable(DropTargetDropEvent e) {
            Transferable t = e.getTransferable();
            if (!isTransferableGood(t, e.getCurrentDataFlavors()[0])) {
                return false;
            }
            return isDataGood(getTabTransferData(e));
        }

        public void dragExit(DropTargetEvent e) {}
        public void dropActionChanged(DropTargetDragEvent e) {}
        public void dragOver(final DropTargetDragEvent e) {}
    }

    private int getTargetTabIndex(Point a_point) {
        for (int i = 0; i < getTabCount(); i++) {
            Rectangle r = getBoundsAt(i);
            r.setRect(r.x - r.width / 2, r.y, r.width, r.height);
            if (r.contains(a_point)) {
                return i;
            }  
        }
        return -1;
    }

    private void convertTab(TabTransferData a_data, int a_targetIndex) {
        DnDTabbedPane source = a_data.m_tabbedPane;
        int sourceIndex = a_data.m_tabIndex;
        if (sourceIndex < 0) {
            return;
        }  

        Component cmp = source.getComponentAt(sourceIndex);
        String str = source.getTitleAt(sourceIndex);

        if (a_targetIndex < 0 || sourceIndex == a_targetIndex) {
            return;
        } 
        source.remove(sourceIndex);
        if (a_targetIndex == getTabCount()) {
            addTab(str, cmp);
        } else if (sourceIndex > a_targetIndex) {
            insertTab(str, null, cmp, null, a_targetIndex);
        } else {
            insertTab(str, null, cmp, null, a_targetIndex - 1);
        }
    }

    public static void main(String[] args)
    {
        JFrame window = new JFrame();
        DnDTabbedPane tabbedPane = new DnDTabbedPane();
        for(int i=0; i< 5; i++)
        {
            tabbedPane.addTab("I'm tab "+i, new JLabel("I'm tab "+i));
        }
        window.add(tabbedPane);
        window.setSize(400, 200);
        window.setVisible(true);
    }
}

到目前为止,我能做的最好的事情就是在我们跳出 parent 时调用一些东西来达到这种效果。

    Component rootPane = SwingUtilities.getRoot(component);
    Rectangle bounds = rootPane.getBounds();
    if (!bounds.contains(location))
    {
        Robot robot = null;
        try
        {
            robot = new Robot();
        } catch (AWTException e)
        {
            return;
        }
        robot.keyPress(KeyEvent.VK_ESCAPE);
        robot.keyRelease(KeyEvent.VK_ESCAPE);
    }

这完全是黑客攻击,并没有解决我的问题。我想拦截最终的放置事件,看看它是否在框架之外并在其自己的 JFrame 中生成选项卡。

如果我使用的是 NetBeans、MyDoggy 或 Eclipse 框架,我想这一切都会为我神奇地处理。唉。

最佳答案

无法直接取消拖动。见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4502185

我更愿意通过更改光标向用户显示不允许在桌面上放置。

你的 DragSourceListener dsl 在 dragOver 方法中有一个 DragSourceDragEvent 告诉你 桌面上的目标操作为 NONE。

更改为:

public void dragOver(DragSourceDragEvent e) {

    TabTransferData data = getTabTransferData(e);

    if( data == null || e.getTargetActions() == DnDConstants.ACTION_NONE ) {
        e.getDragSourceContext().setCursor( DragSource.DefaultMoveNoDrop );
        return;
    }

    e.getDragSourceContext().setCursor( DragSource.DefaultMoveDrop);
}

如果你真的想取消,那么你必须使用你的 ESC 解决方案或类似的东西:

    try {
        new Robot().mouseRelease( InputEvent.BUTTON1_MASK ); // if Button1 was the only Button to start a Drag
    } catch( AWTException e1 ) {
    }

关于java - 禁用或拦截窗外的掉落,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14249566/

相关文章:

java - 如何将Java选项卡式面板添加到自定义布局中?

java - JTextArea 始终为空?

java - 面板未显示在 JFrame 中

java - 移动时抬起代理并增加 z 位置

java - 限制Java文件中的输入数量

java - 在 Spring 中从类路径和文件系统加载 freemarker 模板

java - 控制 Gumstix Overo GPIO

javascript - 使用 Hammer.js 拖动事件将 HTML5 元素拖放到 div 上

javascript - JQuery 检查它是否正在拖动,如果没有则被拖动的对象返回到原始位置

c# - 如何在同一个 ListView 中拖放项目?