Seit der Version [1.4]1.4 kann man bei Swing-Komponenten Drag-and-Drop sehr einfach mit Hilfe der Methode setDragEnabled(boolean value) aktivieren.
Dabei bezieht sich die Aktivierung nur auf den Lieferanten - die Komponenten, die nicht aktiviert wurden, können jederzeit die verschobenen Daten aufnehmen. Welche Komponenten Drag-and-Drop unterstützen ist in folgender Tabelle aufgeführt:
Bei Swing-Komponenten, die von Natur aus kein Drag-and-Drop unterstützen, wie beispielsweise JLabel, kann man dies seit der Version [1.4]1.4 durch folgenden Code nachprogrammieren:
JLabel label = new JLabel();
label.setTransferHandler(new TransferHandler("text"));
label.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
JComponent c = (JComponent)e.getSource();
TransferHandler th = c.getTransferHandler();
th.exportAsDrag(c, e, TransferHandler.COPY);
}
});
Das Entscheidende ist dabei der String des Konstruktors der Klasse TransferHandler. Hierbei handelt es sich um eine JavaBeans-Property, so dass hier das zu transferierende Objekt mittels der get-Methode »getText« geholt bzw. über die Setter-Methode »setText« gesetzt wird.
Tabelle 8.4: Einfache Unterstützung von Drag-and-Drop der Swing-Standardkomponenten seit der Version 1.4 Komponente Unterstützt Drag Unterstützt Drop
JColorChooser X X
JEditorPane X X
JFileChooser X
JFormattedTextField X X
JLabel
JList X
JPasswordField X
JTable X
JTextArea X X
JTextField X X
JTextPane X X
JTree X
java.awt.dnd.DropTarget
Möchte man mehr Kontrolle über den Drag-and-Drop-Vorgang erhalten bzw. steht nur eine ältere Version von Java zur Verfügung, kann man über die Packages java.awt.datatransfer und java.awt.dnd die nötigen Klassen einbinden.
Wie beim Copy-and-Paste werden die Daten mit Hilfe des Transferable-Interfaces ausgetauscht.
Wenn man nur auf das »Fallenlassen« reagieren möchte, benötigt man ein DropTarget. Dieses initialisiert man mit der Komponente, die das Ziel darstellt und einem DropTargetListener. Dieses Interface definiert die folgenden Methoden:
* public void dragEnter(DropTargetDragEvent e)
Wird aufgerufen, wenn von außen ein hinübergezogenes Objekt die Komponente erreicht.
* public void dragExit(DropTargetEvent e)
Wird aufgerufen, wenn der gedrückte Mauszeiger die Komponente verlässt.
* public void dragOver(DropTargetDragEvent e)
Wenn die gedrückte Maus über die Komponente gezogen wird, wird diese Methode mehrmals aufgerufen, um über die aktuelle Position des Mauszeigers innerhalb der Komponente zu informieren.
* public void drop(DropTargetDropEvent e)
Wird die Maustaste losgelassen (Drop), wird diese Methode aufgerufen.
* public void dropActionChanged(DropTargetDragEvent e)
Wenn die gewünschte Aktion (Kopieren, Verschieben oder Verknüpfen) geändert wurde (meist über die Tastatur wie z.B. die Shift-Taste auf Windows), wird diese Methode aufgerufen.
Besonders wichtig ist hierbei die Methode drop. In dieser Methode muss entschieden werden, was mit dem übertragenen Objekt gemacht werden soll. Entweder es wird übernommen oder zurückgewiesen.
Der Ablauf ist dabei folgender:
* Abfrage der DataFlavors.
* Wenn eine passende Form gefunden wurde, wird DropTargetDropEvent.acceptDrop(int dropAction) aufgerufen. Typischerweise, wenn der Benutzer die Aktion selbst bestimmen darf, sieht der Aufruf folgendermaßen aus:
e.acceptDrop(e.getDropAction());
* Über das Transferable-Interface wird das entsprechende Objekt weiterverarbeitet.
* Zum Abschluss wird als Bestätigung die Methode DropTargetDragEvent.dropComplete(boolean success) mit dem passenden Wert aufgerufen. Die Methode wird verlassen.
* Falls bei Punkt 2. kein passendes Format gefunden wurde, wird die Methode DropTargetDragEvent.rejectDrop() aufgerufen.
Hat man eine Komponente, die aus vielen unterschiedlichen Bereichen (wie ein Baum oder eine Tabelle) besteht, möchte man vielleicht, dass nur an bestimmten Bereichen ein Fallenlassen erlaubt ist. Zu diesem Zweck kann man in den Methoden dragEnter und dragOver entweder die Methode DropTargetDragEvent.acceptDrop(int dropAction) oder DropTargetDragEvent.rejectDrop() aufrufen, je nachdem, ob an der Stelle ein Fallenlassen erlaubt ist oder nicht. Dies wird meist an der Position der Maus (DropTargetDragEvent.getLocation()) innerhalb der Komponente bestimmt.
Wenn man dem Benutzer nicht die Wahl lassen möchte, ob er das Objekt verschieben, kopieren oder verknüpfen darf, dann muss lediglich die entsprechende dropComplete()-Methode mit einem der folgenden Werte gefüllt werden: DnDConstants.ACTION_COPY, DnDConstants.ACTION_COPY_OR_MOVE, DnDConstants.ACTION_LINK, DnDConstants.ACTION_MOVE. Wer keine Vorgaben machen möchte, kann die aktuelle Aktion mittels DropTargetDragEvent.getDropAction() beibehalten.
label = new JLabel();
DropTargetListener dropTargetListener =
new DropTargetListener() {
// Die Maus betritt die Komponente mit
// einem Objekt
public void dragEnter(DropTargetDragEvent e) {}
// Die Komponente wird verlassen
public void dragExit(DropTargetEvent e) {}
// Die Maus bewegt sich über die Komponente
public void dragOver(DropTargetDragEvent e) {}
public void drop(DropTargetDropEvent e) {
try {
Transferable tr = e.getTransferable();
DataFlavor[] flavors = tr.getTransferDataFlavors();
for (int i = 0; i < flavors.length; i++)
if (flavors.isFlavorJavaFileListType()) {
// Zunächst annehmen
e.acceptDrop (e.getDropAction());
List files = (List) tr.getTransferData(flavors);
// Wir setzen in das Label den Namen der ersten
// Datei
label.setText(files.get(0).toString());
e.dropComplete(true);
return;
}
} catch (Throwable t) { t.printStackTrace(); }
// Ein Problem ist aufgetreten
e.rejectDrop();
}
// Jemand hat die Art des Drops (Move, Copy, Link)
// geändert
public void dropActionChanged(
DropTargetDragEvent e) {}
};
DropTarget dropTarget = new DropTarget(
label, dropTargetListener);
java.awt.dnd.DragSource
Um eine Komponente als Quelle eines Drag-and-Drop-Vorganges zu verwenden, muss ein Exemplar der Klasse DragSource initialisiert werden.
Mit createDefaultDragGestureRecognizer(Component c, int actions, DragGestureListener dgl) kann die entsprechende Quell-Komponente, die beabsichtigte Aktion und der Listener für den Start eines Drag-Events registriert werden.
Das Interface DragGestureListener besitzt nur die Methode
public void dragGestureRecognized(DragGestureEvent dge)
Diese Methode wird aufgerufen, wenn der Benutzer bei gedrückter Maustaste den Mauszeiger bewegt. Ob dadurch ein Drag-and-Drop initiiert ist, wird in der Methode entschieden. Falls ja, wird die Methode startDrag() in dem DragGestureEvent-Exemplar aufgerufen. Hierbei gibt es verschiedene Signaturen. Pflicht ist die Angabe eines Cursors (aufgrund des Bugs #4407521 bei den Java Versionen 1.3.1 und 1.4 sollte der Wert hier null sein) und des Transferable, welches die Daten enthält. Zusätzlich kann eine Grafik angegeben werden, die beim Drag-and-Drop angezeigt wird , und ein DragSourceListener. Es ist zu beachten, dass Windows keine eigenen Grafiken unterstützt.
DragGestureListener dragGestureListener =
new DragGestureListener() {
public void dragGestureRecognized(
DragGestureEvent e) {
// Der Text des Label soll
// JVM-intern
// übertragen werden
StringSelection selection =
new StringSelection(
label.getText());
e.startDrag(null, selection, selection);
}
};
DragSource dragSource = new DragSource();
DragGestureRecognizer dgr =
dragSource.createDefaultDragGestureRecognizer(
label,
DnDConstants.ACTION_COPY,
dragGestureListener);
java.awt.dnd.DragSourceListener
Über das Interface DragSourceListener kann die Quelle eines Drag-and-Drop-Vorganges über den Verlauf informiert werden. Dies ist besonders dann interessant, wenn nach einem erfolgreichen Drag-and-Drop-Vorgang das verschobene Objekt gelöscht werden muss.
Hierzu wird die Methode dragDropEnd(DragSourceDropEvent e) benutzt, die aufgerufen wird, wenn ein Drag-and-Drop beendet wurde. Wurden beispielsweise Dateien verschoben, so werden diese durch folgenden Code gelöscht:
public void dragDropEnd (DragSourceDropEvent e) {
if (e.getDropSuccess() &&
e.getDropAction() == DnDConstants.ACTION_MOVE) {
Transferable tr = e.getDragSourceContext().
getTransferable();
DataFlavor[] flavors = tr.getTransferDataFlavors();
for (int i = 0; i < flavors.length; i++)
if (flavors.isFlavorJavaFileListType())
try {
List files = (List) tr.getTransferData(flavors);
for (int j = 0; j < files.size (); j++)
((File) files.get(j)).delete();
} catch (Throwable t) { t.printStackTrace (); }
}
}
java.awt.dnd.Autoscroll
Wenn die Zielkomponente in einem JScrollPane liegt und dann der Mauszeiger an den Rand des sichtbaren Bereiches stößt, möchte man meist, dass der dabei bisher verdeckte Bereich sichtbar gemacht wird. Zu diesem Zweck muss die entsprechende Komponente das Interface Autoscroll implementieren. Eine zusätzliche Registrierung der Komponente ist hierbei nicht nötig.
Seit der Version [1.4]1.4 kann man bei Swing-Komponenten dafür die Methode setAutoscrolls(boolean flag) benutzen.
Vor der Version 1.4 wird dafür das Interface Autoscroll benötigt. Dieses definiert zwei Methoden:
* public void autoscroll(Point cursorLocation)
Wird aufgerufen, wenn der Mauszeiger während eines Drag-Vorgangs in den Scrollbereich kommt, den die Methode getAutoscrollInsets() definiert.
* public Insets getAutoscrollInsets()
Definiert die Grenzen des Bereiches innerhalb der Komponente, bei der kein Scrolling vonnöten ist. Ist der Mauszeiger außerhalb dieser Grenzen, wird die Methode autoscroll() aufgerufen.