JavaFX Drag and Drop Example
This is a JavaFX Drag and Drop Example. A drag-and-drop
gesture is a specific type of a press-drag-release
gesture.
A press-drag-release
gesture is a user action of pressing a mouse button, dragging the mouse with the pressed button, and releasing the button. The gesture can be initiated on a Scene or a Node.
Several nodes and scenes may participate in a single press-drag-release
gesture. The gesture is capable of generating different types of events and delivering those events to different nodes. The type of generated events and nodes receiving the events depends on the purpose of the gesture.
The following table shows an overview of the whole tutorial:
Table Of Contents
The following examples uses Java SE 7 and JavaFX 2.2.
1. A Press-Drag-Release Gesture
There exists different types of a Press-Drag-Release Gesture. At first we will discuss the Simple Press-Drag-Release Gesture and the Full Press-Drag-Release Gesture.
1.1 The Code
FxDragDropExample1.java
import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.input.MouseDragEvent; import javafx.scene.input.MouseEvent; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class FxDragDropExample1 extends Application { // Create the TextFields TextField sourceFld = new TextField("This is the Source Text"); TextField targetFld = new TextField("Drag and drop the source text here"); // Create the LoggingArea TextArea loggingArea = new TextArea(""); public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage stage) { // Set the Size of the TextFields sourceFld.setPrefSize(200, 20); targetFld.setPrefSize(200, 20); // Create the Labels Label sourceLbl = new Label("Source Node:"); Label targetLbl = new Label("Target Node:"); // Create the GridPane GridPane pane = new GridPane(); pane.setHgap(5); pane.setVgap(20); // Add the Labels and Fields to the Pane pane.addRow(0, sourceLbl, sourceFld); pane.addRow(1, targetLbl, targetFld); // Add mouse event handlers for the source sourceFld.setOnMousePressed(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { sourceFld.setMouseTransparent(true); writelog("Event on Source: mouse pressed"); event.setDragDetect(true); } }); sourceFld.setOnMouseReleased(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { sourceFld.setMouseTransparent(false); writelog("Event on Source: mouse released"); } }); sourceFld.setOnMouseDragged(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { writelog("Event on Source: mouse dragged"); event.setDragDetect(false); } }); sourceFld.setOnDragDetected(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { sourceFld.startFullDrag(); writelog("Event on Source: drag detected"); } }); // Add mouse event handlers for the target targetFld.setOnMouseDragEntered(new EventHandler <MouseDragEvent>() { public void handle(MouseDragEvent event) { writelog("Event on Target: mouse dragged"); } }); targetFld.setOnMouseDragOver(new EventHandler <MouseDragEvent>() { public void handle(MouseDragEvent event) { writelog("Event on Target: mouse drag over"); } }); targetFld.setOnMouseDragReleased(new EventHandler <MouseDragEvent>() { public void handle(MouseDragEvent event) { targetFld.setText(sourceFld.getSelectedText()); writelog("Event on Target: mouse drag released"); } }); targetFld.setOnMouseDragExited(new EventHandler <MouseDragEvent>() { public void handle(MouseDragEvent event) { writelog("Event on Target: mouse drag exited"); } }); // Create the VBox VBox root = new VBox(); // Add the Pane and The LoggingArea to the VBox root.getChildren().addAll(pane,loggingArea); // Set the Style of the VBox root.setStyle("-fx-padding: 10;" + "-fx-border-style: solid inside;" + "-fx-border-width: 2;" + "-fx-border-insets: 5;" + "-fx-border-radius: 5;" + "-fx-border-color: blue;"); // Create the Scene Scene scene = new Scene(root,300,200); // Add the Scene to the Stage stage.setScene(scene); // Set the Title stage.setTitle("A Press Drag Release Example"); // Display the Stage stage.show(); } // Helper Method for Logging private void writelog(String text) { this.loggingArea.appendText(text + "\n"); } }
1.2 A Simple Press-Drag-Release Gesture
The simple press-drag-release
gesture is the default drag gesture. It is used when the drag gesture involves only one Node
(the Node
on which the gesture was initiated). During the drag gesture, all MouseDragEvent types (mouse-drag entered, mouse-drag over, mouse-drag exited, mouse-, and mouse-drag released) are delivered only to the gesture source Node
. In this case, when the mouse button is pressed, the topmost Node
is picked and all subsequent mouse events are delivered to that node until the mouse button is released.
When the mouse is dragged onto another Node
, the Node
on which the gesture was started is still under the cursor and, therefore, no other nodes receive the events until the mouse button is released.
The above example demonstrates a case of the simple press-drag-release
gesture. It adds two different nodes of the class TextField to a Scene
. One is called the source node and the other the target node. Event handlers are added to both nodes. The target node adds MouseDragEvent
handlers to detect any mouse-drag event on it.
Note that the drag-detected
event is generated once after the mouse is dragged. The MouseEvent
object has a dragDetect
flag, which can be set in the mouse-pressed
and mouse-dragged
events. If it is set to true, the subsequent event that is generated is the drag-detected
event. The default is to generate it after the mouse-dragged
event. If you want to generate it after the mouse-pressed
event, not the mouse-dragged
event, you need to modify the event handlers:
sourceFld.setOnMousePressed(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { sourceFld.setMouseTransparent(true); writelog("Event on Source: mouse pressed"); event.setDragDetect(true); } }); sourceFld.setOnMouseDragged(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { writelog("Event on Source: mouse dragged"); event.setDragDetect(false); } });
1.3 A Full Press-Drag-Release Gesture
When the source node of a drag gesture receives the drag-detected
event, you can start a full press-dragrelease
gesture by calling the startFullDrag()
method on the source node. The startFullDrag()
method exists in both Node
and Scene
classes, allowing you to start a full press-drag-release
gesture for a Node
and a scene
.
You need to do one more set up to see the full press-drag-release
gesture in action. The source node of the drag gesture will still receive all mouse-drag
events as it is under the cursor when a drag is happening. You need to set the mouseTransparent
property of the gesture source to false so the Node
below it will be picked and mouse-drag
events will be delivered to that node. Set this property to true in the mouse-pressed
event and set it back to false in the mouse-released
event.
sourceFld.setOnMousePressed(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { sourceFld.setMouseTransparent(true); writelog("Event on Source: mouse pressed"); event.setDragDetect(true); } }); sourceFld.setOnMouseReleased(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { sourceFld.setMouseTransparent(false); writelog("Event on Source: mouse released"); } });
1.4 The GUI
Run the program, press the mouse button on the source Node
, drag it onto the target Node
, and, finally, release the mouse button. The output that follows shows that the target Node
receives mouse-drag events
as the mouse is dragged inside its bounds. This is the case of a full press-drag-release
gesture where the node over which the mouse drag takes place receives the mouse-drag
events.
2. A Drag-and-Drop Gesture
2.1 The Code
FxDragDropExample2.java
import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.input.ClipboardContent; import javafx.scene.input.DragEvent; import javafx.scene.input.Dragboard; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class FxDragDropExample2 extends Application { // Create the TextFields TextField sourceFld = new TextField("This is the source text"); TextField targetFld = new TextField("Drag and drop the source text here"); // Create the LoggingArea TextArea loggingArea = new TextArea(""); public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage stage) { // Create the Labels Label headerLbl = new Label("Drag and drop the source text field onto the target text field."); Label sourceLbl = new Label("Gesture Source:"); Label targetLbl = new Label("Gesture Target:"); // Set the Prompt on the TextFields sourceFld.setPromptText("Enter text to drag"); targetFld.setPromptText("Drag the source text here"); // Create the GridPane GridPane pane = new GridPane(); pane.setHgap(5); pane.setVgap(20); // Add the Labels and Fields to the Pane pane.add(headerLbl, 0, 0, 2, 1); pane.addRow(1, sourceLbl, sourceFld); pane.addRow(2, targetLbl, targetFld); // Add mouse event handlers for the source sourceFld.setOnDragDetected(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { writelog("Event on Source: drag detected"); dragDetected(event); } }); sourceFld.setOnDragDone(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Source: drag done"); dragDone(event); } }); // Add mouse event handlers for the target targetFld.setOnDragOver(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Target: drag over"); dragOver(event); } }); targetFld.setOnDragDropped(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Target: drag dropped"); dragDropped(event); } }); // Create the VBox VBox root = new VBox(); // Add the Pane and The LoggingArea to the VBox root.getChildren().addAll(pane,loggingArea); // Set the Style of the VBox root.setStyle("-fx-padding: 10;" + "-fx-border-style: solid inside;" + "-fx-border-width: 2;" + "-fx-border-insets: 5;" + "-fx-border-radius: 5;" + "-fx-border-color: blue;"); // Create the Scene Scene scene = new Scene(root); // Add the Scene to the Stage stage.setScene(scene); // Set the Title stage.setTitle("A Drag and Drop Example"); // Display the Stage stage.show(); } private void dragDetected(MouseEvent event) { // User can drag only when there is text in the source field String sourceText = sourceFld.getText(); if (sourceText == null || sourceText.trim().equals("")) { event.consume(); return; } // Initiate a drag-and-drop gesture Dragboard dragboard = sourceFld.startDragAndDrop(TransferMode.COPY_OR_MOVE); // Add the source text to the Dragboard ClipboardContent content = new ClipboardContent(); content.putString(sourceText); dragboard.setContent(content); event.consume(); } private void dragOver(DragEvent event) { // If drag board has a string, let the event know that // the target accepts copy and move transfer modes Dragboard dragboard = event.getDragboard(); if (dragboard.hasString()) { event.acceptTransferModes(TransferMode.COPY_OR_MOVE); } event.consume(); } private void dragDropped(DragEvent event) { // Transfer the data to the target Dragboard dragboard = event.getDragboard(); if (dragboard.hasString()) { targetFld.setText(dragboard.getString()); // Data transfer is successful event.setDropCompleted(true); } else { // Data transfer is not successful event.setDropCompleted(false); } event.consume(); } private void dragDone(DragEvent event) { // Check how data was transfered to the target. If it was moved, clear the text in the source. TransferMode modeUsed = event.getTransferMode(); if (modeUsed == TransferMode.MOVE) { sourceFld.setText(""); } event.consume(); } // Helper Method for Logging private void writelog(String text) { this.loggingArea.appendText(text + "\n"); } }
2.2 Introduction
The third type of drag gesture is called a drag-and-drop
gesture, which is a user action combining the mouse movement with a pressed mouse button. It is used to transfer data from the gesture source to a gesture target.
A drag-and-drop gesture allows transferring data from:
- One node to another node
- A node to a scene
- One scene to another scene
- A scene to a node
The source and target can be in the same Java
or JavaFX
application or two different Java
or JavaFX
applications.
Several steps are involved in performing a drag-and-drop
gesture:
- A mouse button is pressed on a node.
- The mouse is dragged with the button pressed.
- The node receives a drag-detected event.
- The gesture target uses the data from the dragboard.
- etc.
2.3 Understanding the Data Transfer Modes
In a drag-and-drop
gesture, the data can be transferred in three modes:
- Copy
- Move
- Link
The copy mode indicates that the data will be copied from the gesture source to the gesture target. You may drag a TextField
and drop it onto another TextField
. The latter gets a copy of the text contained in the former.
The move mode indicates that the data will be moved from the gesture source to the gesture target. You may drag a TextField
and drop it onto another TextField
. The text in the former is then moved to the latter.
The link mode indicates that the gesture target will create a link (or reference) to the data being transferred. The actual meaning of “link” depends on the application.
2.4 Understanding the Dragboard
In a drag-and-drop
data transfer, the gesture source and the gesture target do not know each other. In fact, they may belong to two different applications. Two JavaFX
applications, or one JavaFX
and one native for example. In a dragand-drop
gesture, an intermediary is also used to facilitate the data transfer.
A Dragboard acts as an intermediary between the gesture source and gesture target. A Dragboard
is the storage device that holds the data being transferred. The gesture source places the data into a Dragboard
. The Dragboard
is made available to the gesture target, so it can inspect the type of content that is available for transfer. When the gesture target is ready to transfer the data, it gets the data from the Dragboard
.
An instance of the Dragboard
class represents a dragboard. The class is inherited from the Clipboard class. An instance of the Clipboard
class represents an operating system clipboard. Typically, an operating system uses a clipboard to store data during cut, copy, and paste operations.
2.5 The steps in a drag-and-drop gesture in detail
In the following sections I will discuss the steps in a drag-and-drop
gesture in detail, and you will build an example application. The application will have two TextFields displayed in a scene. One TextField
is called the source Node
and the other the target Node
. The user can drag and drop the source node over to the target node. Upon completion of the gesture, the text from source node is transferred (copied or moved) to the target node.
2.5.1 Initiating the Drag-and-Drop Gesture
The first step in a drag-and-drop
gesture is to convert a simple press-drag-release
gesture into a drag-and-drop
gesture. This is accomplished in the mouse-drag
detected event handler for the gesture source. Calling the startDragAndDrop()
method on the gesture source initiates a drag-and-drop
gesture. The method is available in the Node
and Scene
classes, so a node and a scene can be the gesture source of a drag-and-drop
gesture.
// Initiate a drag-and-drop gesture Dragboard dragboard = sourceFld.startDragAndDrop(TransferMode.COPY_OR_MOVE);
The method accepts the list of supported transfer modes by the gesture source and returns a Dragboard
. The gesture source needs to populate the Dragboard
with the data it intends to transfer. The following snippet of code initiates a drag-and-drop
gesture, copies the source TextField
text to the Dragboard
, and consumes the Event
. The drag-and-drop
gesture is initiated only when the TextField
contains text.
sourceFld.setOnDragDetected(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { writelog("Event on Source: drag detected"); dragDetected(event); } }); private void dragDetected(MouseEvent event) { // User can drag only when there is text in the source field String sourceText = sourceFld.getText(); if (sourceText == null || sourceText.trim().equals("")) { event.consume(); return; } // Initiate a drag-and-drop gesture Dragboard dragboard = sourceFld.startDragAndDrop(TransferMode.COPY_OR_MOVE); // Add the source text to the Dragboard ClipboardContent content = new ClipboardContent(); content.putString(sourceText); dragboard.setContent(content); event.consume(); }
2.5.2 Detecting a Drag Gesture
Once the drag-and-drop
gesture has been initiated, you can drag the gesture source over to any other Node
. The gesture source has already put the data in the Dragboard
declaring the transfer modes that it supports. It is now time for the potential gesture targets to declare whether they accept the data transfer offered by the gesture source. Note that there could be multiple potential gesture targets. One of them will become the actual gesture target when the gesture source is dropped on it. The potential gesture target receives several types of drag events:
- It receives a drag-entered event when the gesture source enters its bounds.
- It receives a drag-over event when the gesture source is dragged around within its bounds.
- It receives a drag-exited event when the gesture source exits its bounds.
- It receives a drag-dropped event when the gesture source is dropped over it by releasing the mouse button.
In a drag-over
event handler, the potential gesture target needs to declare that it intends to participate in the drag-and-drop
gesture by calling the acceptTransferModes(TransferMode... modes)
method of the DragEvent. Typically, the potential target checks the content of the Dragboard
before declaring whether it accepts the transfer modes. The following snippet of code accomplishes this. The target TextField
checks the Dragboard
for plain text. It contains plain text, so the target declares that it accepts COPY
and MOVE
transfer modes.
targetFld.setOnDragOver(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Target: drag over"); dragOver(event); } }); private void dragOver(DragEvent event) { // If drag board has a string, let the event know that // the target accepts copy and move transfer modes Dragboard dragboard = event.getDragboard(); if (dragboard.hasString()) { event.acceptTransferModes(TransferMode.COPY_OR_MOVE); } event.consume(); }
2.5.3 Dropping the Source onto the Target
If the potential gesture target accepts the transfer mode supported by the gesture source, the gesture source can be dropped on the target. The dropping is accomplished by releasing the mouse button while the gesture source is still over the target. When the gesture source is dropped onto a target, the target becomes the actual gesture target. The actual gesture target receives the drag-dropped
event. You need to add a drag-drop event handler for the gesture target in which it performs two tasks:
- It accesses the data in the dragboard.
- It calls the setDropCompleted(boolean isTransferDone) method of the DragEvent object.
Passing true to the method indicates that the data transfer was successful. Passing false indicates that the data transfer was unsuccessful. The Dragboard
cannot be accessed after calling this method. The following snippet of code performs the data transfer and sets the appropriate completion flag:
targetFld.setOnDragDropped(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Target: drag dropped"); dragDropped(event); } }); private void dragDropped(DragEvent event) { // Transfer the data to the target Dragboard dragboard = event.getDragboard(); if (dragboard.hasString()) { targetFld.setText(dragboard.getString()); // Data transfer is successful event.setDropCompleted(true); } else { // Data transfer is not successful event.setDropCompleted(false); } event.consume(); }
2.5.4 Completing the Drag-and-Drop Gesture
After the gesture source has been dropped, it receives a drag-done
event. The DragEvent
object contains a getTransferMode()
method. When it is called from the drag-done
event handler, it returns the transfer mode used for the data transfer. Depending on the transfer mode, you can clear or keep the content of the gesture source. For example, if the transfer mode is MOVE
, it is better to clear the source content to give the user a real feel of the data move.
If the getTransferMode()
method returns null
or TransferMode.ONE
, it indicates that no data transfer happened. The following snippet of code handles the drag-done
event for the source TextField
. The source text is cleared if the data transfer mode was MOVE
.
sourceFld.setOnDragDone(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Source: drag done"); dragDone(event); } }); private void dragDone(DragEvent event) { // Check how data was transfered to the target. If it was moved, clear the text in the source. TransferMode modeUsed = event.getTransferMode(); if (modeUsed == TransferMode.MOVE) { sourceFld.setText(""); } event.consume(); }
2.6 The GUI
The following image shows a simple Drag and Drop example:
3. Transferring Custom Data Types
You can transfer data in any format using the drag-and-drop
gesture provided the data is Serializable. In this section, I will demonstrate how to transfer custom data. You will transfer an ArrayList. The class is very simple. It contains one private field with its getter and setter methods.
3.1 The Fruit Class
Fruit.java
import java.io.Serializable; public class Fruit implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String name = ""; public Fruit(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return name; } }
3.2 The Code
FxDragDropExample3.java
import java.util.ArrayList; import java.util.List; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.SelectionMode; import javafx.scene.control.TextArea; import javafx.scene.input.ClipboardContent; import javafx.scene.input.DataFormat; import javafx.scene.input.DragEvent; import javafx.scene.input.Dragboard; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class FxDragDropExample3 extends Application { // Create the ListViews ListView<Fruit> sourceView = new ListView<>(); ListView<Fruit> targetView = new ListView<>(); // Create the LoggingArea TextArea loggingArea = new TextArea(""); // Set the Custom Data Format static final DataFormat FRUIT_LIST = new DataFormat("FruitList"); public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage stage) { // Create the Labels Label sourceListLbl = new Label("Source List: "); Label targetListLbl = new Label("Target List: "); Label messageLbl = new Label("Select one or more fruits from a list, drag and drop them to another list"); // Set the Size of the Views and the LoggingArea sourceView.setPrefSize(200, 200); targetView.setPrefSize(200, 200); loggingArea.setMaxSize(410, 200); // Add the fruits to the Source List sourceView.getItems().addAll(this.getFruitList()); // Allow multiple-selection in lists sourceView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); targetView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); // Create the GridPane GridPane pane = new GridPane(); pane.setHgap(10); pane.setVgap(10); // Add the Labels and Views to the Pane pane.add(messageLbl, 0, 0, 3, 1); pane.addRow(1, sourceListLbl, targetListLbl); pane.addRow(2, sourceView, targetView); // Add mouse event handlers for the source sourceView.setOnDragDetected(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { writelog("Event on Source: drag detected"); dragDetected(event, sourceView); } }); sourceView.setOnDragOver(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Source: drag over"); dragOver(event, sourceView); } }); sourceView.setOnDragDropped(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Source: drag dropped"); dragDropped(event, sourceView); } }); sourceView.setOnDragDone(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Source: drag done"); dragDone(event, sourceView); } }); // Add mouse event handlers for the target targetView.setOnDragDetected(new EventHandler <MouseEvent>() { public void handle(MouseEvent event) { writelog("Event on Target: drag detected"); dragDetected(event, targetView); } }); targetView.setOnDragOver(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Target: drag over"); dragOver(event, targetView); } }); targetView.setOnDragDropped(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Target: drag dropped"); dragDropped(event, targetView); } }); targetView.setOnDragDone(new EventHandler <DragEvent>() { public void handle(DragEvent event) { writelog("Event on Target: drag done"); dragDone(event, targetView); } }); // Create the VBox VBox root = new VBox(); // Add the Pane and The LoggingArea to the VBox root.getChildren().addAll(pane,loggingArea); // Set the Style of the VBox root.setStyle("-fx-padding: 10;" + "-fx-border-style: solid inside;" + "-fx-border-width: 2;" + "-fx-border-insets: 5;" + "-fx-border-radius: 5;" + "-fx-border-color: blue;"); // Create the Scene Scene scene = new Scene(root); // Add the Scene to the Stage stage.setScene(scene); // Set the Title stage.setTitle("A Drag and Drop Example for Custom Data Types"); // Display the Stage stage.show(); } // Create the Fruit List private ObservableList<Fruit> getFruitList() { ObservableList<Fruit> list = FXCollections.<Fruit>observableArrayList(); Fruit apple = new Fruit("Apple"); Fruit orange = new Fruit("Orange"); Fruit papaya = new Fruit("Papaya"); Fruit mango = new Fruit("Mango"); Fruit grape = new Fruit("Grape"); Fruit guava = new Fruit("Guava"); list.addAll(apple, orange, papaya, mango, grape, guava); return list; } private void dragDetected(MouseEvent event, ListView<Fruit> listView) { // Make sure at least one item is selected int selectedCount = listView.getSelectionModel().getSelectedIndices().size(); if (selectedCount == 0) { event.consume(); return; } // Initiate a drag-and-drop gesture Dragboard dragboard = listView.startDragAndDrop(TransferMode.COPY_OR_MOVE); // Put the the selected items to the dragboard ArrayList<Fruit> selectedItems = this.getSelectedFruits(listView); ClipboardContent content = new ClipboardContent(); content.put(FRUIT_LIST, selectedItems); dragboard.setContent(content); event.consume(); } private void dragOver(DragEvent event, ListView<Fruit> listView) { // If drag board has an ITEM_LIST and it is not being dragged // over itself, we accept the MOVE transfer mode Dragboard dragboard = event.getDragboard(); if (event.getGestureSource() != listView && dragboard.hasContent(FRUIT_LIST)) { event.acceptTransferModes(TransferMode.COPY_OR_MOVE); } event.consume(); } @SuppressWarnings("unchecked") private void dragDropped(DragEvent event, ListView<Fruit> listView) { boolean dragCompleted = false; // Transfer the data to the target Dragboard dragboard = event.getDragboard(); if(dragboard.hasContent(FRUIT_LIST)) { ArrayList<Fruit> list = (ArrayList<Fruit>)dragboard.getContent(FRUIT_LIST); listView.getItems().addAll(list); // Data transfer is successful dragCompleted = true; } // Data transfer is not successful event.setDropCompleted(dragCompleted); event.consume(); } private void dragDone(DragEvent event, ListView<Fruit> listView) { // Check how data was transfered to the target // If it was moved, clear the selected items TransferMode tm = event.getTransferMode(); if (tm == TransferMode.MOVE) { removeSelectedFruits(listView); } event.consume(); } private ArrayList<Fruit> getSelectedFruits(ListView<Fruit> listView) { // Return the list of selected Fruit in an ArratyList, so it is // serializable and can be stored in a Dragboard. ArrayList<Fruit> list = new ArrayList<>(listView.getSelectionModel().getSelectedItems()); return list; } private void removeSelectedFruits(ListView<Fruit> listView) { // Get all selected Fruits in a separate list to avoid the shared list issue List<Fruit> selectedList = new ArrayList<>(); for(Fruit fruit : listView.getSelectionModel().getSelectedItems()) { selectedList.add(fruit); } // Clear the selection listView.getSelectionModel().clearSelection(); // Remove items from the selected list listView.getItems().removeAll(selectedList); } // Helper Method for Logging private void writelog(String text) { this.loggingArea.appendText(text + "\n"); } }
Most of the program is similar to what you have seen before. The difference is in how you store and retrieve the ArrayList<Fruit>
in the Dragboard
.
You define a new data format for this data transfer because the data do not fit into any of the categories available as the constants in the DataFormat class. You have to define the data as constants, as in the following code:
// Set the Custom Data Format static final DataFormat FRUIT_LIST = new DataFormat("FruitList");
Now you have given a unique mime type FruitList
for the data format. In the drag-detected
event, you need to store the list of selected items onto the Dragboard
. The following snippet of code in the dragDetected()
method stores the job. Notice that you have used the new data format while storing the data on the Dragboard
.
// Put the the selected items to the dragboard ArrayList<Fruit> selectedItems = this.getSelectedFruits(listView); ClipboardContent content = new ClipboardContent(); content.put(FRUIT_LIST, selectedItems); dragboard.setContent(content);
In the drag-over
event, if the ListView is not being dragged over itself and the Dragboard
contains data in the FRUIT_LIST
data format, the ListView
declares that it accepts a COPY
or MOVE
transfer. The following snippet of code in the dragOver()
method does the job:
// If drag board has an FRUIT_LIST and it is not being dragged // over itself, we accept the MOVE transfer mode Dragboard dragboard = event.getDragboard(); if (event.getGestureSource() != listView && dragboard.hasContent(FRUIT_LIST)) { event.acceptTransferModes(TransferMode.COPY_OR_MOVE); }
Finally, you need to read the data from the dragboard when the source is dropped on the target. You need to use the getContent()
method of the Dragboard
specifying the FRUIT_LIST
as the data format. The returned result needs to be cast to the ArrayList<Fruit>
. The following snippet of code in the dragDropped()
method does the job:
// Transfer the data to the target Dragboard dragboard = event.getDragboard(); if(dragboard.hasContent(FRUIT_LIST)) { ArrayList<Fruit> list = (ArrayList<Fruit>)dragboard.getContent(FRUIT_LIST); listView.getItems().addAll(list); // Data transfer is successful dragCompleted = true; } // Data transfer is not successful event.setDropCompleted(dragCompleted);
Finally, in the drag-done
event handler, which is implemented in the dragDone()
method, you remove the selected items from the source ListView
if MOVE
was used as the transfer mode. Notice that you have used an ArrayList
, as both the ArrayList
and Fruit
classes are serializable.
3.3 The GUI
The following image shows an example of a Drag and Drop Gesture using Custom Data Types:
4. Download Java Source Code
This was an example of javafx.scene.control.ListView
You can download the full source code of this example here: JavaFxDragDropExample.zip
Thank you for the example and explanation!
Would you please provide another example with drag & drop on shapes?
Please, I want to get how make Transparent Background on overlay Pane