Event

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:

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.

A JavaFX Press-Drag-Release Gesture Example
A JavaFX Press-Drag-Release Gesture Example

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:

A JavaFX Drag-and-Drop Gesture Example using the DragBoard
A JavaFX Drag-and-Drop Gesture Example using the DragBoard

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:

A JavaFX Drag and Drop Example with Custom Data Types
A JavaFX Drag and Drop Example with Custom Data Types

4. Download Java Source Code

This was an example of javafx.scene.control.ListView

Download
You can download the full source code of this example here: JavaFxDragDropExample.zip

Andreas Pomarolli

Andreas has graduated from Computer Science and Bioinformatics at the University of Linz. During his studies he has been involved with a large number of research projects ranging from software engineering to data engineering and at least web engineering. His scientific focus includes the areas of software engineering, data engineering, web engineering and project management. He currently works as a software engineer in the IT sector where he is mainly involved with projects based on Java, Databases and Web Technologies.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
mohmd
mohmd
5 years ago

Thank you for the example and explanation!
Would you please provide another example with drag & drop on shapes?

Jeon Wonbai
Jeon Wonbai
4 years ago

Please, I want to get how make Transparent Background on overlay Pane

Back to top button