Simple Drag and Drop Tutorial

I wanted to play around drag and drop API in JavaFX. Here is a sample application implemented using DnD API. This example comprises 2 Listviews i.e. Players and Team. Players are dragged from source list view and dropped inside target list view.

I have come across a couple of issues while working with DnD API:

1) I was not able to drag and drop multiple cells initially using a custom data format because dropping multiple cells was resulting in a serialization related exception as listed in the following ticket:

https://forums.oracle.com/thread/2596807

One of the developers posted a solution that worked i.e. Change the type from ObservableList to ArrayList while storing the content. The exceptions were thrown because ObservableLists cannot be serialized.

2) I was not sure how to store multiple rows/cells inside clipboard content. After all, there is an easy way to do so by creating an instance of DataFormat and assign a name as follows:


// Create a custom data format which acts as a key and give it a name

private DataFormat listDataFormat = new DataFormat("listOfPlayers");

// Store the selected items as the value for the key when multiple rows are dragged

ClipboardContent content = new ClipboardContent();

content.put(listDataFormat, new ArrayList<Player>(playersListView.getSelectionModel().getSelectedItems()));

// Retrieve the value by key when the drag is dropped on target.

List<Player> players = (List<Player>) dragEvent.getDragboard().getContent(listDataFormat);

Notes:

– A list cell factory is used to render the player name in list cells.

– A BlendMode is used to differentiate when an active drag is over the target. Using a blend mode is not really ideal though.

– Move Transfer mode is used and players are removed from source list view after successful drop on to the target.


import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.effect.BlendMode;
import javafx.scene.input.*;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.Callback;

public class JavaFXDnDApplication extends Application
{

private static final ListView<Player> playersListView = new ListView<Player>();

private static final ObservableList<Player> playersList = FXCollections.observableArrayList();

private static final ListView<Player> teamListView = new ListView<Player>();

private static final GridPane rootPane = new GridPane();

public static void main(String[] args)
 {
 launch(args);
 }

@Override
 public void start(Stage primaryStage)
 {
 primaryStage.setTitle("Drag and Drop Application");

initializeComponents();

initializeListeners();

buildGUI();

populateData();

primaryStage.setScene(new Scene(rootPane, 400, 325));
 primaryStage.show();
 }

 private void initializeListeners()
 {
 playersListView.setOnDragDetected(new EventHandler<MouseEvent>()
 {
 @Override
 public void handle(MouseEvent event)
 {
 System.out.println("setOnDragDetected");

Dragboard dragBoard = playersListView.startDragAndDrop(TransferMode.MOVE);

ClipboardContent content = new ClipboardContent();

content.putString(playersListView.getSelectionModel().getSelectedItem().getName());

dragBoard.setContent(content);
 }
 });

playersListView.setOnDragDone(new EventHandler<DragEvent>()
 {
 @Override
 public void handle(DragEvent dragEvent)
 {
 System.out.println("setOnDragDone");

// This is not the ideal place to remove the player because the drag might not have been exited on the target.
// String player = dragEvent.getDragboard().getString();
// playersList.remove(new Player(player));
 }
 });

teamListView.setOnDragEntered(new EventHandler<DragEvent>()
 {
 @Override
 public void handle(DragEvent dragEvent)
 {
 System.out.println("setOnDragEntered");

teamListView.setBlendMode(BlendMode.DIFFERENCE);
 }
 });

teamListView.setOnDragExited(new EventHandler<DragEvent>()
 {
 @Override
 public void handle(DragEvent dragEvent)
 {
 System.out.println("setOnDragExited");

teamListView.setBlendMode(null);
 }
 });

teamListView.setOnDragOver(new EventHandler<DragEvent>()
 {
 @Override
 public void handle(DragEvent dragEvent)
 {
 System.out.println("setOnDragOver");

dragEvent.acceptTransferModes(TransferMode.MOVE);
 }
 });

teamListView.setOnDragDropped(new EventHandler<DragEvent>()
 {
 @Override
 public void handle(DragEvent dragEvent)
 {
 System.out.println("setOnDragDropped");

String player = dragEvent.getDragboard().getString();

teamListView.getItems().addAll(new Player(player));

playersList.remove(new Player(player));

dragEvent.setDropCompleted(true);
 }
 });
 }

 private void buildGUI()
 {
 rootPane.setPadding(new Insets(10));
 rootPane.setPrefHeight(30);
 rootPane.setPrefWidth(100);
 rootPane.setVgap(10);
 rootPane.setHgap(20);

Label playersLabel = new Label("Players");
 Label teamLabel = new Label("Team");

rootPane.add(playersLabel, 0, 0);
 rootPane.add(playersListView, 0, 1);
 rootPane.add(teamLabel, 1, 0);
 rootPane.add(teamListView, 1, 1);
 }

 private void populateData()
 {
 playersList.addAll(
 new Player("Adam"), new Player("Alex"), new Player("Alfred"), new Player("Albert"),
 new Player("Brenda"), new Player("Connie"), new Player("Derek"), new Player("Donny"),
 new Player("Lynne"), new Player("Myrtle"), new Player("Rose"), new Player("Rudolph"),
 new Player("Tony"), new Player("Trudy"), new Player("Williams"), new Player("Zach")
 );

playersListView.setItems(playersList);
 }

private void initializeComponents()
 {
 initializeListView(playersListView);

initializeListView(teamListView);
 }

 private void initializeListView(ListView<Player> listView)
 {
 listView.setPrefSize(250, 290);
 listView.setEditable(false);
 listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
 listView.setCellFactory(new StringListCellFactory());
 }

 class StringListCellFactory implements Callback<ListView<Player>, ListCell<Player>>
 {
 @Override
 public ListCell<Player> call(ListView<Player> playerListView)
 {
 return new StringListCell();
 }

class StringListCell extends ListCell<Player>
 {
 @Override
 protected void updateItem(Player player, boolean b)
 {
 super.updateItem(player, b);

if (player != null)
 {
 setText(player.getName());
 }
 }
 }
 }
}
public class Player
{
 private String name;

public Player(String name)
 {
 this.name = name;
 }

public String getName()
 {
 return name;
 }

public void setName(String name)
 {
 this.name = name;
 }

@Override
 public boolean equals(Object o)
 {
 if (this == o) return true;
 if (o == null || getClass() != o.getClass()) return false;

Player player = (Player) o;

if (name != null ? !name.equals(player.name) : player.name != null) return false;

return true;
 }

@Override
 public int hashCode()
 {
 return name != null ? name.hashCode() : 0;
 }
}

In case of dragging and dropping multiple cells, use the following implementation:

playersListView.setOnDragDetected(new EventHandler<MouseEvent>()
 {
 @Override
 public void handle(MouseEvent event)
 {
 System.out.println("setOnDragDetected");

Dragboard dragBoard = playersListView.startDragAndDrop(TransferMode.MOVE);

ClipboardContent content = new ClipboardContent();

content.put(listDataFormat, new ArrayList<Player>(playersListView.getSelectionModel().getSelectedItems()));

dragBoard.setContent(content);
 }
 });

teamListView.setOnDragDropped(new EventHandler<DragEvent>()
 {
 @Override
 public void handle(DragEvent dragEvent)
 {
 System.out.println("setOnDragDropped");

List<Player> players = (List<Player>) dragEvent.getDragboard().getContent(listDataFormat);

teamListView.getItems().addAll(players);

playersList.removeAll(players);

dragEvent.setDropCompleted(true);
 }
 });

Untitled

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s