Java - 如何使用其组件拖放JPanel [英] Java - How to drag and drop JPanel with its components

查看:122
本文介绍了Java - 如何使用其组件拖放JPanel的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有关于拖放的问题:
我可以删除标签,文本或图标。但是我想拖放一个包含所有组件(Label,Textbox,..等)的JPanel。

I have a question about dragging and droping: I can drop labels, text or icon. But I want to drag and drop a JPanel with all its components (Label, Textbox,..etc).

我该怎么做?

推荐答案

此解决方案适用。一些开始的气球。

This solution works. Some cavets to start with.

我没有使用TransferHandler API。我不喜欢它,它是限制性的,但这是一个个人的事情(它做得如何,这样做很好),所以这可能不符合你的期望。

I didn't use the TransferHandler API. I don't like it, it's to restrictive, but that's a personal thing (what it does, it does well), so this might not meet your expectations.

我正在使用BorderLayout进行测试。如果您想使用其他布局,您将不得不尝试并绘制出来。 DnD子系统确实提供有关鼠标点的信息(移动和移动时)

I was testing with BorderLayout. If you want to use other layouts, you're going to have to try and figure that out. The DnD subsystem does provide information about the mouse point (when moving and dropping)

所以我们需要什么:

A DataFlavor。我选择这样做,因为它允许更多的限制

A DataFlavor. I chose to do this because it allows a greater deal of restriction

public class PanelDataFlavor extends DataFlavor {

    // This saves me having to make lots of copies of the same thing
    public static final PanelDataFlavor SHARED_INSTANCE = new PanelDataFlavor();

    public PanelDataFlavor() {

        super(JPanel.class, null);

    }

}

可转移。某些包装数据(我们的JPanel)包装了一堆DataFlavors(在我们的例子中只是PanelDataFlavor)

A Transferable. Some kind of wrapper that wraps the data (our JPanel) up with a bunch of DataFlavors (in our case, just the PanelDataFlavor)

public class PanelTransferable implements Transferable {

    private DataFlavor[] flavors = new DataFlavor[]{PanelDataFlavor.SHARED_INSTANCE};
    private JPanel panel;

    public PanelTransferable(JPanel panel) {
        this.panel = panel;
    }

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

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {

        // Okay, for this example, this is over kill, but makes it easier
        // to add new flavor support by subclassing
        boolean supported = false;

        for (DataFlavor mine : getTransferDataFlavors()) {

            if (mine.equals(flavor)) {

                supported = true;
                break;

            }

        }

        return supported;

    }

    public JPanel getPanel() {

        return panel;

    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {

        Object data = null;
        if (isDataFlavorSupported(flavor)) {

            data = getPanel();

        } else {

            throw new UnsupportedFlavorException(flavor);

        }

        return data;

    }

}

DragGestureListener

A "DragGestureListener"

为此,我创建了一个简单的DragGestureHandler,它将一个JPanel作为要拖动的内容。这允许手势处理程序变得自我管理。

For this, I created a simple DragGestureHandler that takes a "JPanel" as the content to be dragged. This allows the gesture handler to become self managed.

public class DragGestureHandler implements DragGestureListener, DragSourceListener {

    private Container parent;
    private JPanel child;

    public DragGestureHandler(JPanel child) {

        this.child = child;

    }

    public JPanel getPanel() {
        return child;
    }

    public void setParent(Container parent) {
        this.parent = parent;
    }

    public Container getParent() {
        return parent;
    }

    @Override
    public void dragGestureRecognized(DragGestureEvent dge) {

        // When the drag begins, we need to grab a reference to the
        // parent container so we can return it if the drop
        // is rejected
        Container parent = getPanel().getParent();

        setParent(parent);

        // Remove the panel from the parent.  If we don't do this, it
        // can cause serialization issues.  We could over come this
        // by allowing the drop target to remove the component, but that's
        // an argument for another day
        parent.remove(getPanel());

        // Update the display
        parent.invalidate();
        parent.repaint();

        // Create our transferable wrapper
        Transferable transferable = new PanelTransferable(getPanel());

        // Start the "drag" process...
        DragSource ds = dge.getDragSource();
        ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), transferable, this);

    }

    @Override
    public void dragEnter(DragSourceDragEvent dsde) {
    }

    @Override
    public void dragOver(DragSourceDragEvent dsde) {
    }

    @Override
    public void dropActionChanged(DragSourceDragEvent dsde) {
    }

    @Override
    public void dragExit(DragSourceEvent dse) {
    }

    @Override
    public void dragDropEnd(DragSourceDropEvent dsde) {

        // If the drop was not sucessful, we need to
        // return the component back to it's previous
        // parent
        if (!dsde.getDropSuccess()) {

            getParent().add(getPanel());

            getParent().invalidate();
            getParent().repaint();

        }
    }
}

所以这是基础。现在我们需要把它们连在一起...

Okay, so that's basics. Now we need to wire it all together...

所以,在面板中我想拖动我添加:

So, in the panel I want to drag I added:

    private DragGestureRecognizer dgr;
    private DragGestureHandler dragGestureHandler;

    @Override
    public void addNotify() {

        super.addNotify();

        if (dgr == null) {

            dragGestureHandler = new DragGestureHandler(this);
            dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
                    this,
                    DnDConstants.ACTION_MOVE,
                    dragGestureHandler);

        }

    }

    @Override
    public void removeNotify() {

        if (dgr != null) {

            dgr.removeDragGestureListener(dragGestureHandler);
            dragGestureHandler = null;

        }

        dgr = null;

        super.removeNotify();

    }

以这种方式使用add / remove notify的原因是保持系统清洁。当我们不再需要它们时,它有助于防止事件被传递到我们的组件。它还提供自动注册。您可能希望使用您自己的setDraggable方法。

The reason for using the add/remove notify in this way is to keep the system clean. It helps prevent events from been delivered to our component when we no longer need them. It also provides automatic registration. You may wish to use your own "setDraggable" method.

这是拖动方面,现在是放弃方。

That's the drag side, now for the drop side.

首先,我们需要一个DropTargetListener:

First, we need a DropTargetListener:

public class DropHandler implements DropTargetListener {

    @Override
    public void dragEnter(DropTargetDragEvent dtde) {

        // Determine if can actual process the contents comming in.
        // You could try and inspect the transferable as well, but 
        // There is an issue on the MacOS under some circumstances
        // where it does not actually bundle the data until you accept the
        // drop.
        if (dtde.isDataFlavorSupported(PanelDataFlavor.SHARED_INSTANCE)) {

            dtde.acceptDrag(DnDConstants.ACTION_MOVE);

        } else {

            dtde.rejectDrag();

        }

    }

    @Override
    public void dragOver(DropTargetDragEvent dtde) {
    }

    @Override
    public void dropActionChanged(DropTargetDragEvent dtde) {
    }

    @Override
    public void dragExit(DropTargetEvent dte) {
    }

    @Override
    public void drop(DropTargetDropEvent dtde) {

        boolean success = false;

        // Basically, we want to unwrap the present...
        if (dtde.isDataFlavorSupported(PanelDataFlavor.SHARED_INSTANCE)) {

            Transferable transferable = dtde.getTransferable();
            try {

                Object data = transferable.getTransferData(PanelDataFlavor.SHARED_INSTANCE);
                if (data instanceof JPanel) {

                    JPanel panel = (JPanel) data;

                    DropTargetContext dtc = dtde.getDropTargetContext();
                    Component component = dtc.getComponent();

                    if (component instanceof JComponent) {

                        Container parent = panel.getParent();
                        if (parent != null) {

                            parent.remove(panel);

                        }

                        ((JComponent)component).add(panel);

                        success = true;
                        dtde.acceptDrop(DnDConstants.ACTION_MOVE);

                        invalidate();
                        repaint();

                    } else {

                        success = false;
                        dtde.rejectDrop();

                    }

                } else {

                    success = false;
                    dtde.rejectDrop();

                }

            } catch (Exception exp) {

                success = false;
                dtde.rejectDrop();
                exp.printStackTrace();

            }

        } else {

            success = false;
            dtde.rejectDrop();

        }

        dtde.dropComplete(success);

    }

}

最后,需要在有兴趣的方面注册放置目标...在能够支持您要添加的投放的容器中

Finally, we need to register the drop target with interested parties...in those containers capable of supporting the drop you want to add

DropTarget dropTarget;
DropHandler dropHandler;

.
.
.

dropHandler = new DropHandler();
dropTarget = new DropTarget(pnlOne, DnDConstants.ACTION_MOVE, dropHandler, true);

个人来说,我在addNotify中初始化并处理在removeNotify中

Personally, I initialise in the addNotify and dispose in the removeNotify

dropTarget.removeDropTargetListener(dropHandler);

只是一个关于addNotifty的快速注释,我已经连续多次被称为您可能需要仔细检查您是否还没有设置放置目标。

Just a quick note on addNotifty, I have had this been called a number of times in succession, so you may want to double check that you haven't already set up the drop targets.

就是这样。

您还可以找到以下感兴趣的一些

You may also find some of the following of interest

http://rabbit-hole.blogspot.com.au/2006/05/my-drag-image-is-better-than-yours.html

http://rabbit-hole.blogspot.com.au/2006/08/drop-target-navigation-or-you-drag.html

http://兔洞。 blogspot.com.au/2006/04/smooth-jlist-drop-target-animation.html

甚至会浪费不检查它们如果只是出于兴趣。

It would be waste not to check them, even if just out of interest.

这篇关于Java - 如何使用其组件拖放JPanel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆