Javafx:TreeView 的域对象

Posted

技术标签:

【中文标题】Javafx:TreeView 的域对象【英文标题】:Javafx : Domain objects to TreeView 【发布时间】:2018-08-28 22:20:50 【问题描述】:

我的应用中有三个域对象,如下所示:

public class Workflow 
  private String name;
  private List<Sheet> sheets;


public class Sheet 
  private String name;
  private List<Task> tasks;


public class Task 
  private String name;

这三个都依赖于工作流 -> 工作表 -> 任务。我的目标是构建 TreeView 使其如下所示:

-Workflow
|
 - workflow name
 -Sheets
 |
  - sheet name
  - Tasks
  |
   - task name

到目前为止,我已经构建了一个示例,它构建的内容比我预期的要少,但它根本不是通用的和“自动化的”。

public class TreeViewSample extends Application 

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


@Override
public void start(Stage primaryStage) 
    primaryStage.setTitle("Tree View Sample");        

    Workflow w = setup();

    TreeItem<String> rootItem = new TreeItem<String> ("Workflow");
    rootItem.setExpanded(true);

        TreeItem<String> item = new TreeItem<String> (w.getName());
        rootItem.getChildren().add(item);
(...)


    TreeView<String> tree = new TreeView<String> (rootItem);        
    StackPane root = new StackPane();
    root.getChildren().add(tree);
    primaryStage.setScene(new Scene(root, 300, 250));
    primaryStage.show();


private Workflow setup()

    Workflow wflow = new Workflow();
    wflow.setName("wflow name");
    wflow.setSheets(Arrays.asList(new Sheet("sheet name", Arrays.asList(new Task("task name")))));

    return wflow;

有人可以建议我如何递归地访问我的域对象并像我的示例中那样构建 TreeView 吗?

【问题讨论】:

【参考方案1】:

您必须为所有模型(工作流、工作表、任务)创建一个共同的Model,因为它们都有一个字符串属性,创建一个非常简单。假设我们有以下模型:

public class Model 

    private String name;

    public Model(String name) 
        this.name = name;
    

    public String getName() 
        return name;
    

    @Override
    public String toString() 
        return getName();
    


class Workflow 
    private String name;
    private List<Sheet> sheets = new ArrayList<>();

    public Workflow(String name) 
        this.name = name;
    

    public String getName() 
        return name;
    

    public List<Sheet> getSheets() 
        return sheets;
    


class Sheet 
    private String name;
    private List<Task> tasks = new ArrayList<>();

    public Sheet(String name) 
        this.name = name;
    

    public String getName() 
        return name;
    

    public List<Task> getTasks() 
        return tasks;
    


class Task 
    private String name;

    public Task(String name) 
        this.name = name;
    

    public String getName() 
        return name;
    

我把你所有的模型放在一起,以便更好地看到它们。

我看到你没有在你的应用程序中使用任何 .fxml 文件,我的文件是 .fxml 我建议你至少将 Main 类与 Controller 类分开,例如:

public class Main extends Application


    @Override
    public void start(Stage primaryStage) throws Exception 
        FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml"));
        AnchorPane pane = loader.load();
        primaryStage.setScene(new Scene(pane,800,600));
        primaryStage.show();
    

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

然后是Controller类:

public class Controller implements Initializable 

    @FXML
    private TreeView<Model> treeView;

    @Override
    public void initialize(URL location, ResourceBundle resources) 
        Workflow workflow = createWorkflow(); // This just sets up the models that you are using.

        // You have to create a root in your case the "Workflow"
        TreeItem<Model> root = new TreeItem<>(new Model(workflow.getName()));
        // The foreach sheet you create a branch
        workflow.getSheets().forEach(sheet -> 
            TreeItem<Model> sheetBranch = new TreeItem<>(new Model(sheet.getName()));
            // Then you have to add each branch to the root
            root.getChildren().add(sheetBranch);
            // Then foreach sheet you create a task item
            sheet.getTasks().forEach(task -> 
                TreeItem<Model> taskItem = new TreeItem<>(new Model(task.getName()));
                // Then you have to add each task to its sheet parent
                sheetBranch.getChildren().add(taskItem);
            );
        );
        // Finally, you set the root for the TreeView. Of course this can be done right after instantiating the root.
        treeView.setRoot(root);
    

    // ------------------- Setup the model -----------------------

    private Workflow createWorkflow() 
        Workflow workflow = new Workflow("Workflow");
        workflow.getSheets().addAll(createSheets());
        return workflow;
    

    private List<Sheet> createSheets() 
        List<Sheet> sheets = new ArrayList<>();
        IntStream.range(1, 10).forEach(value -> sheets.add(createSheet()));
        return sheets;
    

    private Sheet createSheet() 
        Sheet sheet = new Sheet("Sheet" + new Random().nextInt(100)); // Random added to have different names
        sheet.getTasks().addAll(createTasks());
        return sheet;
    

    private List<Task> createTasks() 
        List<Task> tasks = new ArrayList<>();
        IntStream.range(1, 5).forEach(value -> tasks.add(createTask()));
        return tasks;
    

    private Task createTask() 
        return new Task("Task" + new Random().nextInt(100)); // Random added to have different names
    

如果您需要这里是.fxml 文件,以防万一:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="***.tree.Controller">
    <TreeView fx:id="treeView"/>
</AnchorPane>

如果您不知道TreeView 的深度,您可以使用递归创建所有分支或叶子。在这种情况下,使用两个 foreach 比创建构建树结构的递归方法要简单得多。

【讨论】:

【参考方案2】:

缺少提供子项目列表的通用超类型,您需要使用 1 个不同的方法/1 个嵌套循环每个包含子对象列表的对象,即

private TreeItem<String> createWorkFlow(Workflow workflow) 
    TreeItem<String> item = new TreeItem<>(workflow.getName());
    for (Sheet sheet : workflow.getSheets()) 
        item.getChildren().add(createSheet(sheet));
    
    return item;


private TreeItem<String> createSheet(Sheet sheet) 
    TreeItem<String> item = new TreeItem<>(sheet.getName());
    for (Task task : sheet.getTasks()) 
        item.getChildren().add(new TreeItem<>(task.getName());
    
    return item;

或者

private TreeItem<String> createWorkFlow(Workflow workflow) 
    TreeItem<String> workflowItem = new TreeItem<>(workflow.getName());
    for (Sheet sheet : workflow.getSheets()) 
        TreeItem<String> sheetItem = new TreeItem<>(sheet.getName());
        for (Task task : sheet.getTasks()) 
            sheetItem.getChildren().add(new TreeItem<>(task.getName()));
        
        workflowItem.getChildren().add(sheetItem);
    
    return item;

除非你想使用反射。


为避免这种情况,您可以使用您的类型实现接口:

public interface Item<T extends Item<?>> 
    String getName();
    default List<T> getChildren() 
        return null; // default for terminal object
    

这将允许您将TreeItems 的创建简化为

public static <T extends Item<?>> TreeItem<String> createItem(Item<T> item) 
    TreeItem<String> treeItem = new TreeItem<>(item.getName());
    List<T> children = item.getChildren();
    if (children != null) 
        for (Item<?> ci : children) 
            treeItem.getChildren().add(createItem(ci));
        
    
    return treeItem;

【讨论】:

我已按照您的指示进行操作,效果也很好,谢谢! 是的,如果你更喜欢递归方式,你应该使用这个答案;)

以上是关于Javafx:TreeView 的域对象的主要内容,如果未能解决你的问题,请参考以下文章

javaFX树结构treeview使用

带有 gtkmm 的 Glade 构建的 TreeView

javafx:如何隐藏TreeView中的“下拉箭头”?

WPF TreeView内部拖动项

JavaFX仅扩展包含特定Child的TreeView父级

如何使某些JavaFX TreeView节点不可选择?