1

I need to have an expandable nestable key value list in JavaFX 8. This is similar in functionality to a TreeTableView, but it is oriented vertically, not horizontally. In other words, the rows would be the headers not the columns for a X-Y inverted axis table. Due to the limitation of TreeTableView being horizontal, I started exploring the options of TreeView. TreeView works nicely but only has one column where I need a key value pair in each row. What I really need is something like below:

example

Note the foo and bar can be unique.

How can I add a second column to a TreeView?

user1803551
  • 12,965
  • 5
  • 47
  • 74
beepboop
  • 57
  • 6
  • You mean `TreeTableView`? And can you add a mock image of what you want it to look like? – user1803551 Aug 04 '17 at 16:50
  • @user1803551 Yes! – beepboop Aug 04 '17 at 16:52
  • @user1803551 added image example. – beepboop Aug 04 '17 at 17:12
  • You ca replace the [cell factory in `TreeView`](https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TreeView.html#cellFactoryProperty--) to create 2 column-cells. – user1803551 Aug 04 '17 at 17:17
  • @user1803551 is there an example anywhere of using the `cellFactoryProperty`? I cant find one online anywhere. – beepboop Aug 07 '17 at 14:55
  • [1](https://stackoverflow.com/questions/34838341/javafx-custom-cell-factory-with-custom-objects), [2](https://stackoverflow.com/questions/27664440/how-do-you-create-a-table-cell-factory-in-javafx-to-display-a-choicebox), [3](https://stackoverflow.com/questions/12499269/javafx-tableview-detect-a-doubleclick-on-a-cell)... I'll post an answer later. – user1803551 Aug 07 '17 at 20:13
  • @user1803551 thanks! i have looked at those but still am unsure of how i can set multiple columns? – beepboop Aug 07 '17 at 23:05

1 Answers1

0

This is an idea based on a custom cell factory for TreeView that creates row-like cells. A custom cell is created which contains 2 labels in a grid and some resizing behavior settings.

enter image description here

public class CustomTree extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        TreeView<String[]> tree = new TreeView<>();

        tree.setCellFactory(cell -> new TreeCell<String[]>() {

            private GridPane pane = new GridPane();
            private Label keyLabel = new Label();
            private Label valueLabel = new Label();
            private Border keyBorder = new Border(
                    new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, null, new BorderWidths(0.5, 0.5, 0.5, 1)));
            private Border valueBorder = new Border(
                    new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, null, new BorderWidths(0.5, 1, 0.5, 0.5)));

            {
                pane.getChildren().addAll(keyLabel, valueLabel);
                GridPane.setConstraints(keyLabel, 0, 0);
                GridPane.setConstraints(valueLabel, 1, 0);

                ColumnConstraints column1 = new ColumnConstraints();
                column1.setPercentWidth(50);
                ColumnConstraints column2 = new ColumnConstraints();
                column2.setPercentWidth(50);
                pane.getColumnConstraints().addAll(column1, column2);

                keyLabel.setBorder(keyBorder);
                valueLabel.setBorder(valueBorder);
                keyLabel.setMaxWidth(Double.MAX_VALUE);
                valueLabel.setMaxWidth(Double.MAX_VALUE);

                setPadding(Insets.EMPTY);
            }

            @Override
            public void updateItem(String[] name, boolean empty) {
                super.updateItem(name, empty);
                if (empty || name == null) {
                    setText(null);
                    setGraphic(null);
                } else {
                    keyLabel.setText(name[0]);
                    valueLabel.setText(name[1]);
                    setGraphic(pane);
                }
            }
        });

        TreeItem<String[]> root = new TreeItem<>(new String[] { "", "" });
        TreeItem<String[]> sub1 = new TreeItem<>(new String[] { "Foo", "Bar" });
        TreeItem<String[]> sub2 = new TreeItem<>(new String[] { "Foo", "Bar" });
        TreeItem<String[]> sub3 = new TreeItem<>(new String[] { "Foo", "Bar" });
        TreeItem<String[]> sub4 = new TreeItem<>(new String[] { "Foo", "" });
        TreeItem<String[]> sub5 = new TreeItem<>(new String[] { "Foo", "Bar" });
        TreeItem<String[]> sub41 = new TreeItem<>(new String[] { "Foo", "Bar" });
        TreeItem<String[]> sub42 = new TreeItem<>(new String[] { "Foo", "Bar" });
        root.getChildren().addAll(sub1, sub2, sub3, sub4, sub5);
        sub4.getChildren().addAll(sub41, sub42);

        tree.setRoot(root);

        Scene scene = new Scene(tree);
        stage.setScene(scene);
        stage.show();
    }

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

Note that the root must also use this cell type, though it's possible to hide the root completely using tree.setShowRoot(false);. Two shortcomings of this approach are that each cell behaves as one unit - there is no concept of columns, and that the values of the cell are given as an array, thus losing type safety. You can expand on this example yourself.

It could be possible, though a lot more work, to use a (generic) TableView or TreeTableView approach.

user1803551
  • 12,965
  • 5
  • 47
  • 74