Я новичок в JavaFX. Мне нужна помощь в том, как сделать узел TreeView перетаскиваемым

#java #treeview

#java #просмотр дерева

Вопрос:

Я знаю, что этот вопрос задавался несколько раз, но я не смог получить помощь ни из одной статьи.

Мой основной.FXML — это

 <?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>
  

Мой Controller.java это

 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());
    }
}
  

Мой Main.java это

 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);
    }
}
  

Я видел статью, в которой рассказывается, как добавить функцию перетаскивания в TreeItem через TreeCell, добавив свойство Cell. Но процессы были довольно сложными, и я, будучи непрофессионалом в JavaFX, не смог их понять.

Итак, будет весьма полезно, если кто-нибудь сможет мне помочь с этим.

Заранее спасибо.

Ответ №1:

Добавьте в свой код контроллера, отвечающий за настройку пользовательской фабрики ячеек, которая будет прикреплять обработчики к событиям перетаскивания / 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;
});
  

Основные обработчики взяты со страницы javadoc DragEvent:

 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();
}
  

Комментарии:

1. Мне нужна еще одна помощь по этой теме. Я хочу, чтобы в консоли, на которой выполняется перетаскивание, было напечатано имя узла, а также имя узла, на который удален исходный узел. Короче говоря, мне нужны имена исходного и целевого узлов. Заранее спасибо

2. используйте: (TreeCell<Строка>) DragEvent. getSourceGesture() , чтобы получить доступ к вашей TreeCell. Для целевого узла это то же самое, только имя метода — getTargetGesture

3. Я пробовал так, как вы сказали, но я столкнулся с двумя проблемами :

4. Проблема № 1 : я использовал System.out.println((TreeCell<String>) DragEvent.getSourceGesture()). Здесь вывод был таким: «TextFieldTreeCell@fff603[styleClass=cell indexed-cell tree-cell text-field-tree-cell]’NodeB'», Проблема № 2: я также использовал System.out.println(((TreeCell<String>)DragEvent.getGestureTarget()).getText()); Здесь в выводе: только onDragDetected активен, ни DragOver, ни DragDropped не активны.

5. 1. TreeCell имеет метод GetItem, который возвращает значение типа в данном случае строку «NodeB», 2. Перетаскивание / удаление может быть неактивным, если вы ничего не помещаете в Dragboard или не вводите null в обработчик обнаружения перетаскивания.

Ответ №2:

С помощью @kozmatteo я смог приобрести функцию перетаскивания TreeView в JavaFX. Код контроллера выглядит следующим образом :

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()
                amp;amp; !targetNode.equalsIgnoreCase(source.getItem()))
        {
            System.out.println("Dropped on: "   targetNode);
            success = true;
        }
        event.setDropCompleted(success);
        event.consume();
    }
}
  

Демонстрация приведенного выше кода:

введите описание изображения здесь


Но одна из проблем, которая возникает, если onDragOver немного изменить код. Я хочу напечатать имя узла, на который перетаскивается перетаскиваемый узел.

onDragOver(); изменен метод:


 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();
}
  

Демонстрация после добавления кода выше:

введите описание изображения здесь

Наконец:

Я хочу знать имя узла, на который перетаскивается исходный код. Если кто-нибудь может помочь мне с этим, то это будет очень полезно.