import com.sun.javafx.application.PlatformImpl; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.Scene; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.stage.Stage; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * @author Alexander Gulko */ public class TestTreeView1 extends TreeView { private static Executor executor = Executors.newSingleThreadExecutor(); private static int count = 0; private static final int CHILDREN_COUNT = 3; private static final int MAX_EXPAND_LEVEL = 3; private CategoryTreeItem root = createNode(new Category("root", true, 0)); public TestTreeView1() { setRoot(root); setShowRoot(false); init(); } private static CategoryTreeItem createNode(Category category) { CategoryTreeItem treeItem = new CategoryTreeItem(category); if (category.hasChildren) { treeItem.expandedProperty().addListener(new ExpandListener(treeItem)); treeItem.getChildren().add(new CategoryTreeItem(new Category("Loading...", false, category.level + 1))); } return treeItem; } private void init() { root.setExpanded(true); } private static void fillNode(final CategoryTreeItem node, List categories) { List children = new ArrayList(); for (Category category : categories) { children.add(createNode(category)); } node.getChildren().clear(); node.getChildren().addAll(children); expandChildren(node); } private static void expandChildren(CategoryTreeItem node) { if (node.getValue().level < MAX_EXPAND_LEVEL) { for (final TreeItem child : node.getChildren()) { PlatformImpl.runLater(new Runnable() { @Override public void run() { child.setExpanded(true); } }); } } } private static List createNodes(int level) { List result = new ArrayList(); for (int i = 0; i < CHILDREN_COUNT; ++i) { result.add(new Category(String.valueOf(++count), true, level)); } return result; } public static class ExpandListener implements ChangeListener { private CategoryTreeItem treeItem; public ExpandListener(CategoryTreeItem treeItem) { this.treeItem = treeItem; } @Override public void changed(ObservableValue observableValue, Boolean oldValue, Boolean newValue) { if (newValue != null && newValue) { final Category category = treeItem.getValue(); if (treeItem.isCompleted()) { return; } treeItem.setCompleted(true); if (category.hasChildren) { new Loader() { @Override public List loadNodes() { return createNodes(category.level + 1); } @Override public void onLoadFinish(List categories) { fillNode(treeItem, categories); } }.run(); } } } } private static abstract class Loader { public final void run() { executor.execute(new Runnable() { @Override public void run() { try { final List result = loadNodes(); PlatformImpl.runLater(new Runnable() { @Override public void run() { onLoadFinish(result); } }); } catch (Exception e) { e.printStackTrace(); } } }); } public abstract List loadNodes(); public abstract void onLoadFinish(List categories); } public static class CategoryTreeItem extends TreeItem { private boolean completed; public CategoryTreeItem(Category category) { super(category); } public boolean isCompleted() { return completed; } public void setCompleted(boolean completed) { this.completed = completed; } } public static class Category { private String name; private boolean hasChildren; private int level; public Category(String name, boolean hasChildren, int level) { this.name = name; this.hasChildren = hasChildren; this.level = level; } @Override public String toString() { return name; } } public static class TreeApp extends Application { @Override public void start(final Stage stage) throws Exception { stage.setResizable(true); stage.setTitle("Tree view"); Scene scene = new Scene(new TestTreeView1(), 400, 600); stage.setScene(scene); stage.setVisible(true); } public static void main(final String[] args) { try { Application.launch(TreeApp.class, args); } catch (Exception e) { e.printStackTrace(); } } } }