Java Swing drag and drop FAQ: How do I get Java/Swing image drag and drop working on Mac OS X?
I've been working on several new Swing applications for Mac OS X recently, and most of these applications include features like image processing, copy and paste clipboard interaction, and in today's example, dealing with drag and drop events on the Mac.
I couldn't get the "typical" Java/Swing drag and drop techniques to work on the Mac ... frankly, I don't know why, and since I solved the problem with a slightly different approach, I don't really care. But I would like to share with you what worked for me.
Getting drag and drop to work with images on Mac OS X
I was able to get drag and drop working on my Mac system using the following code. First, I created a subclass of a JFrame I named my subclass MetaFrame
, but you can name yours anything you'd like.
Second, I defined a class variable named dropTarget
that is an instance of a DropTarget:
private DropTarget dropTarget;
Next, I created a JPanel, made the background white, instantiated my dropTarget
, using the JPanel
as an argument to the constructor, and then placed the panel in the content pane of my JFrame
:
JPanel panel = new JPanel(); panel.setBackground(Color.WHITE); dropTarget = new DropTarget(panel,new DropTargetImplementation(this, panel)); getContentPane().add(panel, BorderLayout.CENTER);
Next, I created a class named DropTargetImplementation
that extends the DropTargetAdapter class, and put this code inside my MetaFrame
class. As you can see, this class handles a lot of the heavy lifting:
private class DropTargetImplementation extends DropTargetAdapter { MetaFrame metaFrame; JPanel panel; public DropTargetImplementation(MetaFrame metaFrame, JPanel panel) { this.metaFrame = metaFrame; this.panel = panel; } public void drop(DropTargetDropEvent e) { System.err.println("The DropPanel received the DropEvent"); // Called when the user finishes or cancels the drag operation. Transferable transferable = e.getTransferable(); try { if (transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) { e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); Image image = (Image) transferable.getTransferData(DataFlavor.imageFlavor); e.getDropTargetContext().dropComplete(true); metaFrame.addImage(image); } else { System.err.println("DROP::That wasn't an image!"); e.rejectDrop(); } } catch (IOException ioe) { ioe.printStackTrace(); e.rejectDrop(); } catch (UnsupportedFlavorException ufe) { ufe.printStackTrace(); e.rejectDrop(); } } public void dragEnter(DropTargetDragEvent e) { // called when the user is dragging and enters our target panel.setBackground(Color.GREEN); } public void dragExit(DropTargetEvent e) { // called when the user is dragging and leaves our target panel.setBackground(Color.WHITE); } public void dragOver(DropTargetDragEvent e) { // called when the user is dragging and moves over our target panel.setBackground(Color.GREEN); } } // end of DropTargetImpl class
As you can see from that code, the drop
method does most of the hard work. I won't describe it too much here; if you're going to work with image processing a lot you're going to want to learn about the classes used in that method by reading the Javadoc and working with more code samples.
My favorite part about that code -- besides the part where the drag and drop of images actually works -- is turning the background of the panel green when the user is about to drop an image onto the panel/frame. I like applications that actively give this sort of feedback to the user.
What didn't work for me
As mentioned, I didn't get the "traditional" approach to work on my Mac OS X 10.5.7 system, using Java 1.5.0_16. By that I mean that I tried defining an extended JPanel
, like this:
public class DropPanel extends JPanel implements DropTargetListener
and then using that DropPanel
in my MetaFrame
, but it just wouldn't work. In this class I was also trying to highlight my JPanel
with a green color when I hovered over the panel/frame with an image that I wanted to drop, but with this code the panel would never turn green. For some reason it wasn't getting the drop event.