Subhra Jyoti Lahiri
Subhra Jyoti Lahiri

Reputation: 320

I am new to JavaFX. I want help on how to make a TreeView node Draggable and Droppable

I know this question has been asked a multiple times, but I was unable to get help from any of the article.

My Main.FXML is

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
    <children>
        <TreeView fx:id="treeView" layoutX="51.0" layoutY="24.0" onContextMenuRequested="#mouseClick" onMouseClicked="#mouseClick" prefHeight="352.0" prefWidth="493.0" />
    </children>
</AnchorPane>

My Controller.java is

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseEvent;

import java.net.URL;
import java.util.ResourceBundle;

public class Controller implements Initializable
{
    @FXML TreeView<String> treeView;

    @Override
    public void initialize(URL location, ResourceBundle resources)
    {
        TreeItem<String> root = new TreeItem<>("root");

        TreeItem<String> nodeA = new TreeItem<>("nodeA");
        TreeItem<String> nodeB = new TreeItem<>("nodeB");
        TreeItem<String> nodeC = new TreeItem<>("nodeC");

        root.getChildren().add(nodeA);
        root.getChildren().add(nodeB);
        root.getChildren().add(nodeC);

        treeView.setRoot(root);

        root.setExpanded(true);
    }

    @FXML
    private void mouseClick(MouseEvent mouseEvent)
    {
        TreeItem<String> item = treeView.getSelectionModel().getSelectedItem();
        System.out.println(item.getValue());
    }
}

My Main.java is

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }


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

I have seen article that teaches how to add the Drag and Drop feature to TreeItem via TreeCell by adding Cell Property. But the processes were quiet complicated and I being a layman to JavaFX was unable to understand those.

So, it will be quite helpful if anyone can help me out with this.

Thanks in Advance.

Upvotes: 0

Views: 186

Answers (2)

Subhra Jyoti Lahiri
Subhra Jyoti Lahiri

Reputation: 320

With the help of @kozmatteo I was able to acquire the drag and drop feature of TreeView in JavaFX. The Controller code is as below :

Controller.java


import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.input.*;

import java.net.URL;
import java.util.ResourceBundle;

public class Controller implements Initializable
{
    @FXML TreeView<String> treeView;
    private TreeCell<String> source, treeCell;


    @Override
    public void initialize(URL location, ResourceBundle resources)
    {
        TreeItem<String> root = new TreeItem<>("root");

        TreeItem<String> nodeA = new TreeItem<>("nodeA");
        TreeItem<String> nodeB = new TreeItem<>("nodeB");
        TreeItem<String> nodeC = new TreeItem<>("nodeC");

        root.getChildren().add(nodeA);
        root.getChildren().add(nodeB);
        root.getChildren().add(nodeC);

        treeView.setRoot(root);

        root.setExpanded(true);

        treeView.setCellFactory(param -> {
            // creating cell from deafult factory
            treeCell = TextFieldTreeCell.forTreeView().call(param);
            // setting handlers
            treeCell.setOnDragDetected(this::onDragDetected);
            treeCell.setOnDragOver(this::onDragOver);
            treeCell.setOnDragDropped(this::onDragDropped);
            return treeCell;
        });
    }

    private void onDragDetected(MouseEvent event)
    {
        source = (TreeCell<String>) event.getSource();
        Dragboard db = source.startDragAndDrop(TransferMode.ANY);
        ClipboardContent content = new ClipboardContent();
        content.putString(source.getItem());
        db.setContent(content);
        System.out.println("Dragging: " + db.getString());
        event.consume();
    }

    private void onDragOver(DragEvent dragEvent)
    {
        Dragboard db = dragEvent.getDragboard();

        if (db.hasString())
        {
            dragEvent.acceptTransferModes(TransferMode.COPY);
        }

        dragEvent.consume();
    }

    private void onDragDropped(DragEvent event)
    {
        Dragboard db = event.getDragboard();
        String targetNode = ((TreeCell<String>)event.getGestureTarget()).getItem();

        boolean success = false;
        if (db.hasString()
                && !targetNode.equalsIgnoreCase(source.getItem()))
        {
            System.out.println("Dropped on: " + targetNode);
            success = true;
        }
        event.setDropCompleted(success);
        event.consume();
    }
}

Demonstration for the code above:

enter image description here


But One of the problem that occurs if the onDragOver code is changed a bit. I want to print the name of the node upon which the dragging node is dragged over.

onDragOver(); method changed:


private void onDragOver(DragEvent dragEvent)
    {
        Dragboard db = dragEvent.getDragboard();
        if (db.hasString())
        {
            dragEvent.acceptTransferModes(TransferMode.COPY);
            String targetNode = ((TreeCell<String>)event.getGestureTarget()).getItem(); // On adding this piece of code the DragOver event is not working.
        }

        dragEvent.consume();
}

Demonstration after adding the code above:

enter image description here

Finally:

I want to know the name of the node on which the source is being dragged over. If anyone can help me with this then it will be very much helpful.

Upvotes: 0

kozmatteo
kozmatteo

Reputation: 41

Add in your Controller code responsible for setting a custom cell factory, that will attach handlers to Drag/MouseEvents.

treeView.setCellFactory(param -> {
    // creating cell from deafult factory
    TreeCell<String> treeCell = TextFieldTreeCell.forTreeView().call(param);
    // setting handlers
    treeCell.setOnDragDetected(this::onDragDetected);
    treeCell.setOnDragOver(this::onDragOver);
    treeCell.setOnDragDropped(this::onDragDropped);
    return treeCell;
});

Basic handlers taken from DragEvent javadoc page:

private void onDragDetected(MouseEvent event) {
    TreeCell<String> source = (TreeCell<String>) event.getSource();
    Dragboard db = source.startDragAndDrop(TransferMode.ANY);
    ClipboardContent content = new ClipboardContent();
    content.putString(source.getItem());
    db.setContent(content);
    event.consume();
}

private void onDragOver(DragEvent dragEvent) {
    Dragboard db = dragEvent.getDragboard();
    if (db.hasString()) {
        dragEvent.acceptTransferModes(TransferMode.COPY);
    }
    dragEvent.consume();
}

private void onDragDropped(DragEvent event) {
    Dragboard db = event.getDragboard();
    boolean success = false;
    if (db.hasString()) {
        System.out.println("Dropped: " + db.getString());
        success = true;
    }
    event.setDropCompleted(success);
    event.consume();
}

Upvotes: 1

Related Questions