Kristallkugel
Greifenfeder
Einhorn Zauberstab
Alraune
Pegasus im Sonnenuntergang

JComponent Drag n Drop

Einführung

Drag n Drop ist für den Benutzer eine einfache Möglichkeit Daten durch Verschieben schnell zu ordnen.
Da Drag n Drop sehr intuitiv ist und auf der Selbstständigkeit des Benutzers aufbaut, kann es keinen einfachen Ersatz für dieses mächtige Handling geben. Aber genau dieser Punkt ist auch die grösste Schwäche des Drag n Drops: Der Benutzer weiss oftmals nicht, wann oder wo er Daten mit Drag n Drop verschieben kann.

Das Implementieren einer Drag n Drop Komponente mit Java Swing ist im Vergleich zum ihrem Nutzen oftmals sehr aufwändig.

Aufwand  Schwierigkeitsgrad 4 Schwierigkeitsgrad 4 Schwierigkeitsgrad 4 Schwierigkeitsgrad 4 Schwierigkeitsgrad 4

Vorbereitung

Die meisten JComponents können mit Drag n Drop umgehen.

Diese drei Methoden müssen mit den richtigen Parametern auf der Komponente aufgerufen werden:
setDragEnabled(boolean); Schaltet mit true das Drag n Drop für die Komponente ein
setDropMode(DropMode); Der Dropmode bestimmt, wie das Element eingefügt wird
setTransferHandler(TransferHandler); Der TransferHandler wird benötigt um zu bestimmen was während des Drag und Drop Vorganges passiert

Dropmode

Das sind die vier verschiedenen DropMode:

DropMode.INSERT Fügt das Element an der gewählten Position ein
DropMode.USE_SELECTION Ersetzt das Element in der Liste
DropMode.ON
Ersetzt das Element in der Liste
DropMode.ON_OR_INSERT Ersetzt das Element in der Liste oder fügt es ein (Ctrl-Key relevant)

Da man in den meisten Drag n Drop Fällen keine vorhandenen Daten ersetzt werden, benutzt man oftmals nur den DropMode.INSERT.

TransferHandler

Der TransferHandler ist der Schlüssel zum Drag n Drop. Er behandelt das Transferieren von Datentypen zu anderen Komponenten. Auch andere, nicht Java-Programme können mit den gedraggten Inhalten (vor allem vom Typ String) umegehen.

Implementieren der Logik (TransferHandler)

Um einen TransferHandler zu implementieren erstellt man eine Subklasse von TransferHandler.

Folgender Methoden müssen mindestens überschrieben und implementiert werden:

public canImport(TransferSupport)
public getSourceAction(JComponent)
public importData(TransferSupport)
protected createTransferable(JComponet)

canImport(TransferSupport)

Während des Drag-Vorganges wird diese Methode wiederholt aufgerufen und bestimmt mit einem boolean als Rückgabewert ob ein Drop möglich ist. Dabei wird auch der Cursor angepasst.

An dieser Stelle kann getestet werden ob das gedragte Element den Anfroderungen/Data-Flavor (in diesem Beispiel ein String-Typ) entspricht:


  @Override
  public boolean canImport(TransferHandler.TransferSupport info) {
    if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
      return false;
    }
    return true;
  }

getSourceAction(JComponent)

Als Rückgabewert gibt man in dieser Methode an, welche Operation auf der Quelle (JComponent), erlaubt ist.
Entsprechend der Operation, wird auch der Cursor angepasst.

Es gibt folgende Operations-Möglichkeiten:
TransferHandler.NONE
Kein Transfer der Daten
TransferHandler.COPY Kopiert die Daten
TransferHandler.MOVE Verschiebt die Daten
TransferHandler.COPY_OR_MOVE Verschiebt die Daten, oder Kopiert sie wenn die CTRL-Taste gedrückt ist
TransferHandler.LINK Verlinkte Daten

Hier ein Code-Beispiel:

  @Override
  public int getSourceActions(JComponent c) {
    return TransferHandler.COPY_OR_MOVE;
  }

importData(TransferSupport)

Wenn ein Drop erfolgreich war wird diese Methode aufgerufen.

Bei Kopiervorgängen muss wahrscheinlich nicht viel in dieser Methode stehen. Wenn ein Element jedoch auf der alten Komponente entfernt wird, muss das transferierte Element aus dieser Komponente gelöscht werden. Auch eine Neu-Selektierung der verbliebenen Elemente wäre in diesem Fall angebracht.

  1. In diesem Beispiel wird die neue Position des Elementes in eine JList berechent.
  2. Über ein Transferable-Objekt kann das transferierte Element bezogen werden.
  3. Dieses Element wird der nun innerhalb der ursprünglichen JList verschoben (move-Methode wurde selber implementiert) und danach neu selektiert.


  @Override
  public boolean importData(TransferSupport transferInfo) {
    if (!transferInfo.isDrop()) {
      System.out.println("Element wurde nicht transferiert!");
      return false;
    }

    /* 1. */
    JList.DropLocation dropLocation = (JList.DropLocation)transferInfo.getDropLocation();
    int index = dropLocation.getIndex();

    /* 2. */
    Transferable transferable = transferInfo.getTransferable();
    String data;
    try {
      data = (String)transferable.getTransferData(DataFlavor.stringFlavor);
    }
    catch (Exception e) { return false; }

    /* 3. */
    JList list = (JList)transferInfo.getComponent();
    ListModel listModel = (ListModel)list.getModel();                             
    int rangedIndex = listModel.move(data, index);
     
    list.getSelectionModel().setSelectionInterval(0, rangedIndex);

    return true;
  }

createTransferable(JComponet)

In dieser Methode wird das transferierte Element herausgesucht, in ein Transferable-Objekt gekappselt und zurückgegeben.

Dieses sehr simple Beispiel zeigt, wie man ein selektiertes Element (entspricht dem transferierten Element) aus einer JList bekommt. Dieses String-Element wird danach in einer StringSelection gekappselt und als Rückgabewert verwendet:


  @Override
  protected
Transferable createTransferable(JComponent dragList) {  
    JList list = (JList)dragList;
    String selectedValue = (String)list.getSelectedValue();
    return new StringSelection(selectedValue);
  }

Zusammenfassung

Es gibt kein allgemein gültiges Rezept wie Drag n Drop auf einer Komponente aktiviert werden soll. Es ist sehr wichtig zu verstehen, dass Drag n Drop sehr vielseitig eingesetzt werden kann, zum Beispiel mit Multiselect, dem Kopieren und Verschieben etc. Deshalb ist die Implementierung des TransferHandlers immer eigen.

Literatur