ridi
ridi

Reputation: 61

TreeTableView: Displaying different data types


I have a Factory class which contains a list of employees. I want to use a TreeTableView to display the Factory data. It is pretty forward to display the name and the size of a Factory, but i don't know how to display the employees names!

public class Factory {
       private String name;
       private double size;
       private List<Employee> employees;

       public Factory(name, size){this.name=name; this.size=size} 

       // Getters & setters

    }

I want to have the following output:

enter image description here

With the possibilty to fold the factory.

Upvotes: 0

Views: 1866

Answers (1)

James_D
James_D

Reputation: 209319

In a TreeView or TreeTableView all nodes in the tree have to be of the same type. This makes the kind of design you want (which is very natural) something of a pain. Basically, you have to make the type of the TreeView or TreeTableView the most specific superclass of all the types of rows you want in the tree: i.e. in this case the type of the TreeTableView needs to be a superclass of both Employee and Factory. Then the cell value factories on the columns would have to type test the row objects to determine what value to return.

It would be unusual to have an object model in which these were related by inheritance other than both being subclasses of Object, so you probably need a TreeTableView<Object> here.

So roughly speaking (if you are using plain old JavaBean style, instead of the recommended JavaFX properties), you would define something like

TreeTableView<Object> treeTable = new TreeTableView<>();
treeTable.setShowRoot(false);

TreeTableColumn<Object, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(cellData -> {
    TreeItem<Object> rowItem = cellData.getValue();
    if (rowItem != null && (rowItem.getValue() instanceof Factory)) {
        Factory f = (Factory) rowItem.getValue() ;
        return new SimpleStringProperty(f.getName());
    } else {
        return new SimpleStringProperty("");
    }
});

TreeTableColumn<Object, Number> sizeColumn = new TreeTableColumn<>("Size");
sizeColumn.setCellValueFactory(cellData -> {
    TreeItem<Object> rowItem = cellData.getValue();
    if (rowItem != null && (rowItem.getValue() instanceof Factory)) {
        Factory f = (Factory) rowItem.getValue() ;
        return new SimpleObjectProperty<Number>(Double.valueOf(f.getSize()));
    } else {
        return new SimpleObjectProperty<Number>(null);
    }
});

TreeTableColumn<Object, String> employeeColumn = new TreeTableColumn<>("Employee");
employeeColumn.setCellValueFactory(cellData -> {
    TreeItem<Object> rowItem = cellData.getValue();
    if (rowItem != null && (rowItem.getValue() instanceof Employee)) {
        Employee emp = (Employee) rowItem.getValue() ;
        return new SimpleStringProperty(emp.getName());
    } else {
        return new SimpleStringProperty("");
    }
});

treeTable.getColumns().addAll(nameColumn, sizeColumn, employeeColumn);

and of course you populate it with

// fully initialized list of factories, with employee lists initialized:
List<Factory> factories = ... ;

TreeItem<Object> root = new TreeItem<>(null);
for (Factory factory : factories) {
    TreeItem<Object> factoryItem = new TreeItem<>(factory);
    root.getChildren().add(factoryItem);
    for (Employee emp : factory.getEmployees()) {
        TreeItem<Object> employeeItem = new TreeItem<>(emp);
        factoryItem.getChildren().add(employeeItem);
    }
}
treeTable.setRoot(root);

Here's a simple SSCCE using this:

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;

public class TreeTableExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TreeTableView<Object> treeTable = new TreeTableView<>();
        treeTable.setShowRoot(false);

        TreeTableColumn<Object, String> nameColumn = new TreeTableColumn<>("Name");
        nameColumn.setCellValueFactory(cellData -> {
            TreeItem<Object> rowItem = cellData.getValue();
            if (rowItem != null && (rowItem.getValue() instanceof Factory)) {
                Factory f = (Factory) rowItem.getValue() ;
                return new SimpleStringProperty(f.getName());
            } else {
                return new SimpleStringProperty("");
            }
        });

        TreeTableColumn<Object, Number> sizeColumn = new TreeTableColumn<>("Size");
        sizeColumn.setCellValueFactory(cellData -> {
            TreeItem<Object> rowItem = cellData.getValue();
            if (rowItem != null && (rowItem.getValue() instanceof Factory)) {
                Factory f = (Factory) rowItem.getValue() ;
                return new SimpleObjectProperty<Number>(Double.valueOf(f.getSize()));
            } else {
                return new SimpleObjectProperty<Number>(null);
            }
        });

        TreeTableColumn<Object, String> employeeColumn = new TreeTableColumn<>("Employee");
        employeeColumn.setCellValueFactory(cellData -> {
            TreeItem<Object> rowItem = cellData.getValue();
            if (rowItem != null && (rowItem.getValue() instanceof Employee)) {
                Employee emp = (Employee) rowItem.getValue() ;
                return new SimpleStringProperty(emp.getName());
            } else {
                return new SimpleStringProperty("");
            }
        });

        treeTable.getColumns().addAll(nameColumn, sizeColumn, employeeColumn);

        List<Factory> factories = createData();
        TreeItem<Object> root = new TreeItem<>(null);
        for (Factory factory : factories) {
            TreeItem<Object> factoryItem = new TreeItem<>(factory);
            root.getChildren().add(factoryItem);
            for (Employee emp : factory.getEmployees()) {
                TreeItem<Object> employeeItem = new TreeItem<>(emp);
                factoryItem.getChildren().add(employeeItem);
            }
        }
        treeTable.setRoot(root);

        Scene scene = new Scene(treeTable, 800, 800);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private List<Factory> createData() {
        String[][] empNames = {
                {"John", "Jane", "Mary"},
                {"Susan", "Mike"},
                {"Alex", "Francois", "Joanne"}
        };
        List<Factory> factories = new ArrayList<>();
        for (String[] emps : empNames) {
            int count = factories.size()+1 ;
            Factory f = new Factory("Factory "+ count, count*10);
            for (String empName : emps) {
                f.getEmployees().add(new Employee(empName));
            }
            factories.add(f);
        }
        return factories ;
    }

    public static class Employee {
        private String name ;

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

        public String getName() {
            return name;
        }

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


    }

    public class Factory {
        private String name ;
        private double size ;
        private List<Employee> employees ;

        public Factory(String name, double size) {
            this.name = name ;
            this.size = size ;
            this.employees = new ArrayList<>();
        }

        public String getName() {
            return name;
        }

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

        public double getSize() {
            return size;
        }

        public void setSize(double size) {
            this.size = size;
        }

        public List<Employee> getEmployees() {
            return employees;
        }


    }

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

enter image description here


Another approach, which I think is a bit artificial, is to create a class representing the row in the table view, and then to make Factory and Employee subclasses of it:

public abstract class EmploymentEntity {

    public String getName() {
        return null ;
    }

    public Double getSize() {
        return null ;
    }

    public String getEmployeeName {
        return null ;
    }
}

then

public class Employee extends EmploymentEntity {
    private String name ;

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

    @Override
    public String getEmployeeName() {
        return name ;
    }

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

and

public class Factory extends EmploymentEntity {

    private String name ;
    private double size ;
    private List<Employee> employees ;

    public Factory(String name, double size) {
        this.name = name ;
        this.size = size ;
        this.employees = new ArrayList<>();
    }

    @Override
    public String getName() {
        return name ;
    }

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

    @Override
    public Double getSize() {
        return size ;
    }

    public void setSize(double size) {
        this.size = size ;
    }

    public List<Employee> getEmployees() {
        return employees ;
    }
}

This object model is really unnatural (to me, anyway), but it does make the table a little easier:

TreeTableView<EmploymentEntity> treeTable = new TreeTableView<>();
TreeTableColumn<EmploymentEntity, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getValue().getName()));
TreeTableColumn<EmploymentEntity, Number> sizeColumn = new TreeTableColumn<>("Size");
sizeColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<Number>(cellData.getValue().getValue().getSize()));
TreeTableColumn<EmploymentEntity, String> employeeColumn = new TreeTableColumn<>("Employee");
employeeColumn.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getValue().getEmployeeName()));
// etc...

Upvotes: 4

Related Questions