1

I am trying to make simple java app to drag and drop image into JLabel which is on JFrame with no luck. I will later use this functionality in my program which will receive dropped image, resize it and then adjust brightness. What is simplest way to make it and to save that image for further editing?

Here is my code which only receives dropped text:

import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.TransferHandler;

class DragNDrop extends JFrame {
        JLabel label;

        public DragNDrop() {

            setTitle("Simple Drag & Drop");
            setLayout(null);

            label = new JLabel();
            label.setBounds(0, 0, 200, 200);

            add(label);
            label.setTransferHandler(new TransferHandler("text"));

            setSize(200, 200);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setLocationRelativeTo(null);
            setVisible(true);
    }

    public static void main(String[] args) {
            new DragNDrop();
    }
}
Kara
  • 6,115
  • 16
  • 50
  • 57
Neone
  • 482
  • 1
  • 9
  • 21
  • 1
    First, `new TransferHandler("text")` creates a TransferHandler that makes use of the JLabel's `getText` and `setText` methods, so it can't possibly work with images. Second, the public TransferHandler constructor creates a TransferHandler that only works with other Java components; it will not accept dragged data from non-Java applications. You will have to write your own subclass of TransferHandler. – VGR Dec 15 '13 at 15:54
  • But TransferHandler works with text dropped from outside java applications. I don't know how to write that subclass.. – Neone Dec 15 '13 at 16:10
  • In what OS are you able to drop text onto your application? From what other application are you dragging the text? I cannot get it to work in Linux or in Windows 7. The TransferHandler default implementation's source code does not allow for any DataFlavors other than [DataFlavor.javaJVMLocalObjectMimeType](http://docs.oracle.com/javase/7/docs/api/java/awt/datatransfer/DataFlavor.html#javaJVMLocalObjectMimeType). – VGR Dec 15 '13 at 17:17
  • I am dropping text in Windows 8, selecting it from Netbeans and dropping on to app. – Neone Dec 15 '13 at 17:22

1 Answers1

3

When you create a TransferHandler using the constructor that takes a String, you create a TransferHandler that can only perform transfers within the same JVM.

new TransferHandler("text") creates a TransferHandler that transfers the 'text' property of JLabel (or any other component or bean). If you want to drag images, you need a property of type Image. JLabel has no properties of type Image, but it has a property of type Icon. If you use that, the drag source must supply data of type Icon, and you will still have the limitation of only accepting data from the same JVM.

If you want to accept any Image data from any application, you must write a custom TransferHandler. Images can take a number of forms, but some common forms are:

  1. A java.awt.Image, represented by DataFlavor.imageFlavor.
  2. A List of Files (List<File>), represented by DataFlavor.javaFileListFlavor. As the name suggests, this usually is the result of the user's dragging files.
  3. Binary data (usually in the form of an InputStream) whose MIME type (such as "image/png") indicates the bytes are an image.

Your custom TransferHandler, then, might look like this:

private static class ImageHandler
extends TransferHandler {
    private static final long serialVersionUID = 1;

    private boolean isReadableByImageIO(DataFlavor flavor) {
        Iterator<?> readers = ImageIO.getImageReadersByMIMEType(
            flavor.getMimeType());
        if (readers.hasNext()) {
            Class<?> cls = flavor.getRepresentationClass();
            return (InputStream.class.isAssignableFrom(cls) ||
                    URL.class.isAssignableFrom(cls) ||
                    File.class.isAssignableFrom(cls));
        }

        return false;
    }

    @Override
    public boolean canImport(TransferSupport support) {
        if (support.getUserDropAction() == LINK) {
            return false;
        }

        for (DataFlavor flavor : support.getDataFlavors()) {
            if (flavor.equals(DataFlavor.imageFlavor) ||
                flavor.equals(DataFlavor.javaFileListFlavor) ||
                isReadableByImageIO(flavor)) {

                return true;
            }
        }
        return false;
    }

    @Override
    public boolean importData(TransferSupport support) {
        if (!(support.getComponent() instanceof JLabel)) {
            return false;
        }
        if (!canImport(support)) {
            return false;
        }

        // There are three types of DataFlavor to check:
        // 1. A java.awt.Image object (DataFlavor.imageFlavor)
        // 2. A List<File> object (DataFlavor.javaFileListFlavor)
        // 3. Binary data with an image/* MIME type.

        if (support.isDataFlavorSupported(DataFlavor.imageFlavor)) {
            try {
                Image image = (Image)
                    support.getTransferable().getTransferData(
                        DataFlavor.imageFlavor);

                JLabel label = (JLabel) support.getComponent();
                label.setIcon(new ImageIcon(image));
                return true;
            } catch (UnsupportedFlavorException | IOException e) {
                e.printStackTrace();
            }
        }

        if (support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
            try {
                Iterable<?> list = (Iterable<?>)
                    support.getTransferable().getTransferData(
                        DataFlavor.javaFileListFlavor);
                Iterator<?> files = list.iterator();
                if (files.hasNext()) {
                    File file = (File) files.next();
                    Image image = ImageIO.read(file);

                    JLabel label = (JLabel) support.getComponent();
                    label.setIcon(new ImageIcon(image));
                    return true;
                }
            } catch (UnsupportedFlavorException | IOException e) {
                e.printStackTrace();
            }
        }

        for (DataFlavor flavor : support.getDataFlavors()) {
            if (isReadableByImageIO(flavor)) {
                try {
                    Image image;

                    Object data =
                        support.getTransferable().getTransferData(flavor);
                    if (data instanceof URL) {
                        image = ImageIO.read((URL) data);
                    } else if (data instanceof File) {
                        image = ImageIO.read((File) data);
                    } else {
                        image = ImageIO.read((InputStream) data);
                    }

                    JLabel label = (JLabel) support.getComponent();
                    label.setIcon(new ImageIcon(image));
                    return true;
                } catch (UnsupportedFlavorException | IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return false;
    }
}

Some additional advice: In practice, when writing a full-featured application, you should be using LayoutManagers. You could remove the setLayout(null) and label.setBounds lines from your code, and you would get exactly the same result, because a JFrame's default contentPane uses a BorderLayout, and the single-argument add method places your JLabel in the center of that BorderLayout, which means it fills the entire frame.

VGR
  • 40,506
  • 4
  • 48
  • 63