Most read:
Popular archives:
JavaWorld's new look is here!
We've upgraded the site with a fresh look-and-feel, improved topical navigation, better search, new features, and expanded
community platform. Learn more about the changes to JavaWorld.
| Oracle Compatibility Developer's Guide |
| The Explosion in DBMS Choice |
This article will show you how to create D&D-enabled versions of many more Swing components. We'll also learn how to transfer various data types beyond plain text.
In the first article in this series, we discussed how to transfer text. It is, however, possible to drag and drop other data
types, such as images and GUI components. In order to transfer other kinds of data, we must create at least one DataFlavor class and a Transferable class.
Previously, we created a StringTransferable class that provided text data in several DataFlavors. StringTransferable encapsulates a java.lang.String object, which it makes available in these flavors. When the user drops the data onto a droppable component, the component
requests the data in its own preferred DataFlavor. Depending on the DataFlavor requested, the String is either simply returned or transformed into a byte stream.
An important point to remember is that a java.lang.String is serializable. If the DropTarget resides in the same JVM, it will receive a reference to a live object. If, however, the DropTarget is in a different JVM, it will receive a serialized copy of the String. The D&D system automatically serializes the String in the same way that RMI parameters are marshalled. We can easily transfer any data encapsulated in a serializable object
by defining our own Transferable class.
In this example, we'll create a Transferable class for our own Rockhopper class. Rockhopper will be serializable, as will its members; if any are not, we can mark them transient and transfer their data manually via
the writeObject() method. This Transferable creates two DataFlavors for our class. In both DataFlavors our Rockhopper class is the representation type. The Transferable object simply keeps a reference to the Rockhopper object, which is returned in the getTransferData() method. Remember that, if the DropTarget is in another JVM, the D&D system will automatically serialize the Rockhopper object.
Now, let's look at the code for this example:
public class Rockhopper extends Bird implements Serializable {
private String name; // String is serializable
}
public class RockhopperTransferable
implements Transferable,
ClipboardOwner {
public static DataFlavor rockhopperFlavor=null;
public static DataFlavor localRockhopperFlavor=null;
static {
try {
rockhopperFlavor = new
DataFlavor(com.rockhoppertech.Rockhopper.class,
"Non local Rockhopper");
localRockhopperFlavor = new
DataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
"; class=com.rockhoppertech.Rockhopper",
"Local Rockhopper");
} catch(Exception e) {
System.err.println(e);
}
}
private Rockhopper penguin;
public RockhopperTransferable(Rockhopper rocky) {
this.penguin = rocky;
}
public Object getTransferData(...) {
// check to see if the requested flavor matches
... etc ...
return this.penguin; // easy!
}
etc.
You may want to create a library of Transferable classes for commonly used data types. Popular custom Transferable choices include java.awt.Color, java.util.Date, java.awt.Font, and java.io.File.
Creating an Image Transferable class is a bit tricky. The problem is that java.awt.Image isn't serializable, so it can't be used as the representation type for a DataFlavor class. Luckily, javax.swing.ImageIcon is serializable. The ImageIcon maintains a reference to an Image, but it's a transient member. To actually transfer the Image data, the ImageIcon class defines the readObject() and writeObject() methods for manual transfer.
The capability to transfer image data to the native system is more complicated. For native transfers, we need to provide the
data as a stream of bytes via an InputStream subclass. For our StringTransferable class, we employed a ByteArrayInputStream object that used the String's bytes in the proper charset.
Similarly, we can create a JPEGTransferable class that provides the image data in JPEG format. The DataFlavor used by the Transferable can specify image/jpeg and its MIME type. The getTransferData() method can provide the data either via the com.sun.image.codec.jpeg classes or the Java Advanced Imaging API. The latter also provides other formats, such as GIF.
Our JPEGTransferable class will work as long as we have a native program that accepts data with an image/jpeg MIME type. On Windows systems, however,
these programs are rare. Most Windows programs accept only Win32 clipboard formats such as DIB (device independent bitmap)
or ENHMETAFILE (enhanced metafile). Therefore, for Windows environments you'll need to create InputStream classes that provide the data in one of these formats. Moreover, you'll need to use a special FlavorMap instance.
The FlavorMap class provides mapping between MIME types used in Java and native clipboard types such as DIB or HDROP. The D&D system creates
the default FlavorMap from $(JDK)/jre/lib/flavormap.properties. For our custom native InputStreams to be used, we need to add an entry to flavormap.properties. Alternatively, we could create a FlavorMap instance for our listeners.
Here's a custom entry in flavormap.properties:
DIB=image/x-win-bmp;class=com.rockhoppertech.dnd .datatransfer.DIBInputStream
If you need a single D&D-enabled component, you can create a subclass that defines DragGestureListener, DragSourceListener, and DropTargetListener as inner classes. This was the technique used in the first article. If you need a number of D&D-enabled components, you will
write very similar code for each component's listeners.
Rather than using inner classes for the listeners, in this article we will use reusable adapters. This makes more sense for a D&D-enabled component library; it should be kept in mind, however, that it is also a bit more difficult to create adapters that will adapt to all possible uses.
The main classes and interfaces in our D&D library include:
DragComponentDragSourceAdapterDragGestureAdapterDropComponentDropTargetAdapterA D&D-enabled component would create associations with an instance of each of these classes, as shown in Figure 1.
SwingWorker source code hereJInternalFrame's DropTargets