简体   繁体   中英

Javafx : Domain objects to TreeView

I have three domain objects in my app as follows :

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

All three are dependent as Workflow -> Sheet -> Task. My goal is to build TreeView so that it look like below :

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

So far, I have build a sample that builds more less what I expect, but it's not generic and 'automated' at all.

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

Can someone advise on how I can recursively go to my domain objects and build the TreeView as in my example ?

You have to create a common Model to all of you models(Workflow, Sheet,Task), since all have a String property, it is quite simple to create one. Let's suppose we have the following 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;
    }
}

I kept there together all of your models, to see them better.

I see you don't use any .fxml file at your app, mine is with .fxml I recommend that you separate at least to separate the Main class from the Controller class, like:

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

Then the Controller class:

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

Just in case if you need here is the .fxml file:

<?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="stackoverflow.tree.Controller">
    <TreeView fx:id="treeView"/>
</AnchorPane>

If you don't know the depth of the TreeView you can create all of the branches or leaves using recursion. In this case it is much simpler to use two foreachs instead of creating a recursive method which builds the tree structure.

Lacking a common supertype providing a list of child items you need to use 1 different method/1 nested loop per object containing a list of sub objects, ie

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

Or

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

unless you want to use reflection.


To avoid this you could implement a interface with your types:

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

which would allow you to simplify the creating of the TreeItem s to

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM