Wie kann ich Drag-and-Drop in Swing verwenden Dateipfad zu bekommen?
-
03-07-2019 - |
Frage
Ich habe eine JTextField in meinem Schwung-Anwendung, die den Dateipfad einer Datei verwendet werden soll, ausgewählt hält. Zur Zeit habe ich eine JFileChooser der verwendet wird, um diesen Wert zu füllen. Aber ich mag die Möglichkeit für einen Benutzer hinzufügen, um eine Datei auf diese JTextField Drag-and-Drop und hat es den Dateipfad der Datei in die JTextField platzieren, anstatt immer mit dem JFileChooser verwendet wird.
Wie kann dies geschehen?
Lösung
Als erstes sollten Sie Drag & Drop-Unterstützung Schwingen . Danach gibt es nur wenige kleine Tricks für verschiedene Betriebssysteme. Sobald Sie die Dinge bekam gehen Sie das Drop () Callback werden handhaben. In diesem Rückruf sollten Sie die DataFlavor der übertragbaren überprüfen.
Für Windows können Sie einfach überprüfen Sie die DataFlavor.isFlavorJavaFileListType () und dann Daten wie diese erhalten
List<File> dropppedFiles = (List<File>)transferable.getTransferData(DataFlavor.javaFileListFlavor)
Für Linux (und wahrscheinlich Solaris) die DataFlavor ist ein wenig komplizierter. Sie benötigen, um Ihre eigenen DataFlavor und der übertragbaren Typ unterschiedlich sein wird machen
nixFileDataFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
String data = (String)transferable.getTransferData(nixFileDataFlavor);
for(StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens();)
{
String token = st.nextToken().trim();
if(token.startsWith("#") || token.isEmpty())
{
// comment line, by RFC 2483
continue;
}
try
{
File file = new File(new URI(token))
// store this somewhere
}
catch(...)
{
// do something good
....
}
}
Andere Tipps
Es ist ein Beispielprogramm, die eine Klasse enthält, die zur Erleichterung des Drag verwendet werden können, und für Dateien und Ordner löschen:
http://www.iharder.net/current/java/filedrop/
Getestet habe ich diese mit Windows 7 und Ubuntu 10.10, und es scheint, in beiden Umgebungen gut zu funktionieren.
Um es nutzen zu können, um so etwas zu Ihrem Code hinzu:
JPanel myPanel = new JPanel();
new FileDrop( myPanel, new FileDrop.Listener()
{ public void filesDropped( java.io.File[] files )
{
// handle file drop
...
} // end filesDropped
}); // end FileDrop.Listener
Falls Sie wollen nicht zu viel Zeit damit verbringen, diesen relativ komplexes Thema forscht, und Sie sind auf Windows (oder mit Hilfe von Java 7 oder höher), hier ein kurzes Beispiel, wie abgelegten Dateien mit einem JTextArea zu akzeptieren, wie ein Drop-Ziel:
JTextArea myPanel = new JTextArea();
myPanel.setDropTarget(new DropTarget() {
public synchronized void drop(DropTargetDropEvent evt) {
try {
evt.acceptDrop(DnDConstants.ACTION_COPY);
List<File> droppedFiles = (List<File>)
evt.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
for (File file : droppedFiles) {
// process files
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
Ich weiß, das ist eine alte Frage, aber die aktuellen Antworten sind alle ein bisschen veraltet:
- seit JDK 1.6 die Klasse 'TransferHandler' mit neuen (überschrieben) Methoden verwendet werden soll
- Unterstützung für Linux wurde viel besser, keine Notwendigkeit für individuelle Handhabung
Dies funktioniert unter Linux (KDE5) und Windows 7:
final class FileDropHandler extends TransferHandler {
@Override
public boolean canImport(TransferHandler.TransferSupport support) {
for (DataFlavor flavor : support.getDataFlavors()) {
if (flavor.isFlavorJavaFileListType()) {
return true;
}
}
return false;
}
@Override
@SuppressWarnings("unchecked")
public boolean importData(TransferHandler.TransferSupport support) {
if (!this.canImport(support))
return false;
List<File> files;
try {
files = (List<File>) support.getTransferable()
.getTransferData(DataFlavor.javaFileListFlavor);
} catch (UnsupportedFlavorException | IOException ex) {
// should never happen (or JDK is buggy)
return false;
}
for (File file: files) {
// do something...
}
return true;
}
}
Verwenden Sie es auf jede Komponente mit
myComponent.setTransferHandler(new FileDropHandler());
Das funktioniert für mich. Ich benutze es so (scala):
def onDrop(files: List[java.io.File]): Unit = { ... }
val lblDrop = new Label {
peer.setTransferHandler(new FileDropHandler(onDrop))
border = EtchedBorder
}
class FileDropHandler(val onDrop: List[java.io.File] => Unit) extends javax.swing.TransferHandler {
import javax.swing.JComponent
import java.awt.datatransfer.{Transferable, DataFlavor}
import java.net.URI
import java.io.File
val stdFileListFlavor = DataFlavor.javaFileListFlavor
val nixFileListFlavor = new DataFlavor("text/uri-list;class=java.lang.String")
override def canImport(comp: JComponent, flavors: Array[DataFlavor]): Boolean =
flavors.exists(flavor =>
(flavor == stdFileListFlavor) ||
(flavor == nixFileListFlavor)
)
override def importData(comp: JComponent, t: Transferable): Boolean = {
val flavors = t.getTransferDataFlavors()
val files = if (flavors.exists(_ == stdFileListFlavor)) {
val data = t.getTransferData(stdFileListFlavor)
importStdFileList( data )
} else if (flavors.exists(_ == nixFileListFlavor)) {
val data = t.getTransferData(nixFileListFlavor)
importNixFileList( data )
} else List()
onDrop( files )
!files.isEmpty
}
private def importStdFileList(data: Any): List[File] = {
data.asInstanceOf[List[File]] //XXX NOT TESTED
}
private def importNixFileList(data: Any): List[File] = {
def clean(rawLine: String): Option[String] = {
val line = rawLine.trim
if (line.length == 0 || line == "#") None
else Some(line)
}
def asURI(line: String): Option[URI] = {
try { Some(new URI(line)) }
catch { case e:Exception => println(e); None }
}
def asFile(uri: URI): Option[File] = {
try { Some(new File(uri)) }
catch { case e:Exception => println(e); None }
}
data.asInstanceOf[java.lang.String].split("\n")
.toList flatMap clean flatMap asURI flatMap asFile
}
}