Consider a tableview inside a JavaFX application. The tableview contains data about people:
id firstname lastname
1 John Doe
Each of the rows of the tableview can be used to instantiate a Person object:
new Person(Integer id, String firstname, String lastname)
.
Here is what I want to do:
I want to select one of the Persons inside the tableview and drag it with my mouse into a textarea. Inside the textarea I want to print the String:
1 John Doe
.
I know that I have to implement three functions to achieve this: onDragDetected(), onDragOver() and onDragDropped()
.
I was thinking to start with:
public void onDragDetected(DragEvent dragevent) {
Person person = tvPersonData.getSelectionModel().getSelectedItem();
}
This works. But I have to somehow get my Person into the DragBoard object (dragevent.getDragboard()
). I think that I do. And then maybe my other functions can use whatever is inside my dragboard.
Can anyone help me out here?
In order to use a custom object in a DragAndDrop
gesture, you need to define a custom DataFormat
for the object:
DataFormat personDataFormat = new DataFormat("com.package.Person");
You obviously need to replace the com.package.Person
will the full path to the class you are creating this for.
Note also that in order for a class to be used as a DataFormat
, that class must be Serializable
. This is accomplished by implementing the Serializable
interface in your Person
object:
class Person implements Serializable {}
Another thing to remember is that JavaFX properties are not Serializable
so if you use StringProperty
in your Person
object, this will not work; you'll need to create a wrapper class that holds the data you need.
Once you have your DataFormat
and Person
implementing Serializable
, you can copy your Person
object to the Clipboard just like any other content; you just need to specify the DataFormat
for it:
ClipboardContent content = new ClipboardContent();
content.put(personDataFormat, tableView.getSelectionModel().getSelectedItem());
Dropping onto the TextArea
is going to be similar. You'll define the DataFormat
and then add the code to process as desired:
Person droppedPerson = (Person) db.getContent(personDataFormat);
textArea.appendText(droppedPerson.getName() + "\n");
Full example application below:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.Serializable;
import java.util.Arrays;
public class DragObject extends Application {
private final TableView<Person> tableView = new TableView<>();
private final TextArea textArea = new TextArea();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// **********************************************************************************************
// Create a basic layout
// **********************************************************************************************
VBox root = new VBox(5);
root.setAlignment(Pos.TOP_CENTER);
root.setPadding(new Insets(10));
// **********************************************************************************************
// Configure a regular TableView
// **********************************************************************************************
configureTableView();
tableView.setItems(sampleData());
tableView.setPrefHeight(200);
root.getChildren().add(tableView);
root.getChildren().add(textArea);
// **********************************************************************************************
// This method will configure our drag-and-drop functionality
// **********************************************************************************************
initDragAndDrop();
// **********************************************************************************************
// Set the Scene for the stage
// **********************************************************************************************
primaryStage.setScene(new Scene(root));
// **********************************************************************************************
// Configure the Stage
// **********************************************************************************************
primaryStage.setTitle("Test Application");
primaryStage.show();
}
private void configureTableView() {
// **********************************************************************************************
// Just a standard sample TableView
// **********************************************************************************************
TableColumn<Person, String> colName = new TableColumn<>("Name");
TableColumn<Person, String> colEmail = new TableColumn<>("Email");
colName.setCellValueFactory(new PropertyValueFactory<>("name"));
colEmail.setCellValueFactory(new PropertyValueFactory<>("email"));
tableView.getColumns().addAll(colName, colEmail);
}
private void initDragAndDrop() {
DataFormat personDataFormat = new DataFormat("Person");
// **********************************************************************************************
// Add drag and drop to the TableView
// **********************************************************************************************
tableView.setOnDragDetected(event -> {
// **********************************************************************************************
// Drag was detected, allow any type of transfer
// **********************************************************************************************
Dragboard db = tableView.startDragAndDrop(TransferMode.ANY);
// **********************************************************************************************
// Add the selected Person to the clipboard, using our custom DataFormat
// **********************************************************************************************
ClipboardContent content = new ClipboardContent();
content.put(personDataFormat, tableView.getSelectionModel().getSelectedItem());
db.setContent(content);
});
// **********************************************************************************************
// Add drag and drop to the TextArea
// **********************************************************************************************
textArea.setOnDragOver(event -> {
// **********************************************************************************************
// Person was dragged over the TextArea; accept it only if dragged from a node other than itself.
// In this case, this condition isn't necessary, but still good practice
// **********************************************************************************************
if (event.getGestureSource() != textArea
&& event.getDragboard().hasContent(personDataFormat)) {
// **********************************************************************************************
// For this example, we'll allow ANY transfer type again
// **********************************************************************************************
event.acceptTransferModes(TransferMode.ANY);
}
// **********************************************************************************************
// Consume the event. We'll handle placing content into the TextArea next
// **********************************************************************************************
event.consume();
});
// **********************************************************************************************
// Add handler to the TextArea to accept a Person object that's been dropped and add the person's
// name to the TextArea
// **********************************************************************************************
textArea.setOnDragDropped(event -> {
// **********************************************************************************************
// First, ensure the object being dropped was a Person object
// **********************************************************************************************
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasContent(personDataFormat)) {
// **********************************************************************************************
// Get a reference to the Person being dropped. Note that you need to
// cast the object to a Person so Java knows what object type you're
// working with.
// **********************************************************************************************
Person droppedPerson = (Person) db.getContent(personDataFormat);
textArea.appendText(droppedPerson.getName() + "\n");
success = true;
}
// **********************************************************************************************
// Complete the event
// **********************************************************************************************
event.setDropCompleted(success);
event.consume();
});
}
private ObservableList<Person> sampleData() {
// **********************************************************************************************
// Just some sample Persons for our TableView
// **********************************************************************************************
return FXCollections.observableList(Arrays.asList(
new Person("John Williams", "jwilliams@et.com"),
new Person("Howard Shore", "hshore@shire.com"),
new Person("Danny Elfman", "delfman@scissorpalace.org")));
}
}
RESULT: