1

So, in How can I use Drag-and-Drop in Swing to get file path?, several different solutions are suggested: one pointing to the TransferHandler tutorial (which doesn't actually explain how to drag and drop files, only explaining dragging JComponents, and then giving a demo with multiple moving parts and no explanation for files), one using DropTarget and DropTargetDropEvent, and one using an external library called FileDrop. The fourth answer (thankfully) actually explained what TransferHandler is and how to implement it for files, but the question I have is:

Which of these methods is best to use?

I don't know about using the external library if there is native support, but the big thing I don't understand is DropTarget.

I've only previously seen DropTarget used in programs written in Java 7 and earlier, so I assumed that it was made obsolete by TransferHandler, but the solution in the linked question says that it should be used for Java 7 or later. I'm very confused.

Thank you for your time, and I look forward to a response!

ABadHaiku
  • 65
  • 1
  • 6

1 Answers1

1

imho there is no generally best method for drag&drop. For simple file drag&drop (e.g. files from the file explorer) I would use the DropTargetListener. TransferHandle is likely to be used for drag&drop between components or applications.

You need a DropTarget to define the type of drop operation (e.g. copy and/or move) and the DropTargetListener.

An example that you might know (somewhat messy): Drag&Drop Example on stackoverflow

As an example I wrote a panel class that implements drag&drop via DropTargetListener. A derivation of this class which overrides importFiles can directly access a List<File>.

import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class DropTargetPanel extends JPanel implements DropTargetListener {
    private static final String NL = "\\r?\\n";
    private static final List<DataFlavor> supportedFlavors = 
        new ArrayList<>( List.of(DataFlavor.javaFileListFlavor, DataFlavor.stringFlavor) );

    public DropTargetPanel() {
        super();
        new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this);
    }
    
    @Override
    public void dragEnter(DropTargetDragEvent dtde) {
        if(supportedFlavors.contains(dtde.getCurrentDataFlavors()[0])) {
            dtde.acceptDrag(DnDConstants.ACTION_COPY);
        } else {
            dtde.rejectDrag();
        }
    }

    @Override
    public void dragOver(DropTargetDragEvent dtde) {
        dragEnter(dtde);
    }

    @Override
    public void dropActionChanged(DropTargetDragEvent dtde) { /* */ }

    @Override
    public void dragExit(DropTargetEvent dte) { /* */ }

    @SuppressWarnings("unchecked")
    @Override
    public void drop(DropTargetDropEvent dtde) {
        // It looks like only the first flavor is guaranteed to work for me - so what
        DataFlavor df = dtde.getCurrentDataFlavors()[0];
        Transferable transferable = dtde.getTransferable();
        
        if(supportedFlavors.contains(df)) {
            dtde.acceptDrop(dtde.getDropAction());
            if(df.isFlavorJavaFileListType()) {
                try { importFiles((List<File>)transferable.getTransferData(df)); }
                catch (Exception e) { e.printStackTrace(); } 
            } else {
                try { importFiles(getFiles(((String)transferable.getTransferData(df)).split(NL))); } 
                catch (Exception e) { e.printStackTrace(); } 
            }                
            dtde.dropComplete(true);
        }
        else dtde.rejectDrop();
    }

    // Creates a List<File> from a String Array
    private List<File> getFiles(String[] files){
        List<File> result = new ArrayList<>();
        for(int i = 0; i < files.length; i++) result.add(new File(files[i]));
        return result;
    }

    // Override this to gain access for the list of files
    protected void importFiles(List<File> files) {
        //
    }
}