Search code examples
javajavafx-8treetableview

How to visualize UNLIMITED number of rows of data in TreeTableView in JavaFX?


It is said in documentation, that

TreeTableView control is designed to visualize an unlimited number of rows of data

Unfortunately, I can't see any means for this in the doc.

All elements are stored in TreeItem<> classes and stored in memory. Although, one can implement "unlimitness" with ObservableList<> I see no way to implement this on nested observable lists.

"Unlimitness" require loading data by-demand, not storing it in memory.

So is it finally possible to display data on demand in TreeTableView?


Solution

  • As pointed out in the comments, the documentation for TreeItem has an example of lazily-populating the child nodes of the tree item.

    Here's a very simple complete example of an infinite tree table. In this example, the child nodes are removed when the tree item is collapsed, allowing them to be garbage collected.

    import java.math.BigInteger;
    import java.util.function.Function;
    
    import javafx.application.Application;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.collections.ObservableList;
    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 UnlimitedTreeTableView extends Application {
    
    
        @Override
        public void start(Stage primaryStage) {
            TreeTableView<Item> treeTable = new TreeTableView<>();
            treeTable.setRoot(createTreeItem(BigInteger.ZERO));
    
            treeTable.getColumns().add(column("Item", Item::getName));
            treeTable.getColumns().add(column("Value", Item::getValue));
    
            primaryStage.setScene(new Scene(treeTable));
            primaryStage.show();
        }
    
        private TreeItem<Item> createTreeItem(BigInteger value) {
            TreeItem<Item> item = new TreeItem<Item>(new Item(String.format("Item %,d", value), value)) {
    
                private boolean childrenComputed = false ;
    
                {
                    expandedProperty().addListener((obs, wasExpanded, isNowExpanded) -> {
                        if (! isNowExpanded) { // remove child nodes...
                            super.getChildren().clear();
                            childrenComputed = false ;
                        }
                    });
                }
    
                @Override
                public ObservableList<TreeItem<Item>> getChildren() {
                    if (! childrenComputed) {
                        Item item = getValue();
                        BigInteger value = item.getValue() ;
                        BigInteger valueTimes10 = value.multiply(BigInteger.TEN);
                        for (int i = 0 ; i < 10 ; i++) {
                            BigInteger v = BigInteger.valueOf(i);
                            super.getChildren().add(createTreeItem(valueTimes10.add(v)));
                        }
                        childrenComputed = true ;
                    }
                    return super.getChildren();
                }
    
                @Override
                public boolean isLeaf() {
                    return false ;
                }
            };
    
            return item ;
        }
    
        private static <S,T> TreeTableColumn<S,T> column(String title, Function<S,T> property) {
            TreeTableColumn<S,T> column = new TreeTableColumn<>(title);
            column.setCellValueFactory(cellData -> 
                new SimpleObjectProperty<T>(property.apply(cellData.getValue().getValue())));
            column.setPrefWidth(200);
            return column ;
        }
    
        public static class Item {
            private final BigInteger value ;
            private final String name ;
    
            public Item(String name, BigInteger value) {
                this.name = name ;
                this.value = value ;
            }
    
            public BigInteger getValue() {
                return value;
            }
    
            public String getName() {
                return name;
            }
    
    
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }