¿Cómo puedo usar Arrastrar y soltar en Swing para obtener la ruta del archivo?
-
03-07-2019 - |
Pregunta
Tengo un JTextField en mi aplicación swing que contiene la ruta del archivo de un archivo seleccionado para ser utilizado. Actualmente tengo un JFileChooser que se utiliza para completar este valor. Sin embargo, me gustaría agregar la capacidad para que un usuario arrastre y suelte un archivo en este JTextField y haga que coloque la ruta del archivo en ese JTextField en lugar de tener que usar siempre el JFileChooser.
¿Cómo se puede hacer esto?
Solución
Primero debes ver compatibilidad con Swing DragDrop . Después de eso, hay algunos pequeños trucos para diferentes sistemas operativos. Una vez que tenga las cosas en marcha, estará manejando la devolución de llamada drop (). En esta devolución de llamada, querrá verificar el DataFlavor del Transferible.
Para Windows solo puede consultar DataFlavor.isFlavorJavaFileListType () y luego obtenga sus datos como este
List<File> dropppedFiles = (List<File>)transferable.getTransferData(DataFlavor.javaFileListFlavor)
Para Linux (y probablemente Solaris), el DataFlavor es un poco más complicado. Tendrá que hacer su propio DataFlavor y el tipo transferible será diferente
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
....
}
}
Otros consejos
Hay un programa de ejemplo que contiene una clase que se puede utilizar para facilitar la función de arrastrar y soltar archivos y carpetas:
http://www.iharder.net/current/java/filedrop/
Probé esto con Windows 7 y Ubuntu 10.10, y parece funcionar bien en ambos entornos.
Para usarlo, agrega algo como esto a su código:
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
En caso de que no quieras pasar demasiado tiempo investigando este tema relativamente complejo, y estás en Windows (o usando Java 7 o posterior), aquí tienes un ejemplo rápido sobre cómo aceptar archivos eliminados con un JTextArea como un objetivo de caída:
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();
}
}
});
Sé que esta es una pregunta antigua, pero las respuestas actuales están un poco desactualizadas:
- desde JDK 1.6, la clase 'TransferHandler' debe usarse con nuevos métodos (sobrescritos)
- el soporte para Linux se volvió mucho mejor, sin necesidad de manejo personalizado
Esto funciona en Linux (KDE5) y 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;
}
}
Úsalo en cualquier componente con
myComponent.setTransferHandler(new FileDropHandler());
Esto funciona para mí. Lo estoy usando así (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
}
}