2

我正在使用 Netbeans 7.2 开发 JavaFX 2.2 应用程序。我正在使用树视图,并且我扩展了 TreeCell 以向每个 TreeItem 提供一个上下文菜单,该菜单带有一个具有“Colpase All”功能的 MenuItem。树视图的最大深度级别为 4。当用户右键单击级别 2 的 TreeItem 并单击“全部折叠”菜单项时,我想让所有级别 3 的 TreeItem 折叠(setExpanded(false))。您可以在下面看到我正在使用的代码。我的问题是此操作的内存和 CPU 成本。我将 250 个 TreeItems 插入到第 3 级。collapse all 操作的成本是每次collapseAll 点击时大约 200MB 的内存,并且花费了大约 2s 的时间!我的开发计算机的 CPU 是 Intel i5 (3.3GHz),我有 8GB 内存。这些硬件成本是正常的还是我在代码中做错了什么?我是否使用错误的方式来折叠它们?

此类管理 TreeView。从数据库加载数据,重新加载它们,知道选定的 TreeItem 并展开/折叠选定的子 TreeItem。

public final class TargetTree extends SqlConnectionManager {

    private TreeView tree;
    private TreeItem selectedItem;

    private TargetTree() {
        super();
        this.tree = null;
        this.selectedItem = null;
    }

    private TargetTree(TreeView tree) {
        super();
        this.tree = tree;
        this.selectedItem = null;
    }

    public static TargetTree construct(TreeView tree) {
        if (tree == null) {
            return null;
        }

        TargetTree targetTree = new TargetTree(tree);
        targetTree.load();
        return targetTree;
    }

    public void reload() {
        // Clear current tree.
        if (tree.getRoot() != null) {
            for (int i = 0; i < tree.getRoot().getChildren().size(); i++) {
                tree.getRoot().getChildren().clear();
            }
            tree.setRoot(null);
        }
        this.load();
    }

    public void prune() {
        //TODO
    }

    private void load() {
        // New root Item.
        final TreeItem<Object> treeRoot = new TreeItem<>((Object) "Root");
        treeRoot.setExpanded(true);

        // This integers help to find when to build a new department/section/measure.
        int lastDepartmentId = -1;
        int lastSectionId = -1;
        int lastMeasureId = -1;
        int lastTargetId = -1;

        //The temp treeitems.
        TreeItem<Object> departmentTreeItem = null;
        TreeItem<Object> sectionTreeItem = null;
        TreeItem<Object> measureTreeItem = null;
        TreeItem<Object> targetTreeItem = null;

        // Get the new TreeItems from the database.
        super.errorMessage = "";
        try {
            // Establishing connection with db.
            super.openConnection();

            // Query to be executed. Selects everything from the database.
            preparedStmt = connection.prepareStatement(
                    "SELECT.....ORDER BY....;");
            resultSet = preparedStmt.executeQuery();

            while (resultSet.next()) {
                // Department Creation.
                if (lastDepartmentId != resultSet.getInt("departmentId")) {
                    final Department department = Department.initEmpty();
                    department.setId(resultSet.getInt("departmentId"));
                    department.setName(resultSet.getString("departmentName"));

                    // Create the treeitem for this department.
                    departmentTreeItem = new TreeItem<>((Object) department);
                    departmentTreeItem.setExpanded(true);
                    treeRoot.getChildren().add(departmentTreeItem);

                    // Reset the children ids to ensure that they will be recreated.
                    lastDepartmentId = resultSet.getInt("departmentId");
                    lastSectionId = -1;
                    lastMeasureId = -1;
                    lastTargetId = -1;
                }

                // Section Creation.
                if (lastSectionId != resultSet.getInt("sectionId")) {
                    final Section section = Section.initEmpty();
                    section.setId(resultSet.getInt("sectionId"));
                    section.setName(resultSet.getString("sectionName"));

                    // Create the treeitem for this section.
                    sectionTreeItem = new TreeItem<>((Object) section);
                    sectionTreeItem.setExpanded(true);
                    departmentTreeItem.getChildren().add(sectionTreeItem);

                    // Reset the children ids to ensure that they will be recreated.
                    lastSectionId = resultSet.getInt("sectionId");
                    lastMeasureId = -1;
                    lastTargetId = -1;
                }

                // Measure Creation.
                if (lastMeasureId != resultSet.getInt("measureId")) {
                    final Measure measure = Measure.initEmpty();
                    measure.setId(resultSet.getInt("measureId"));
                    measure.setLastname(resultSet.getString("measureLastname"));
                    measure.setFirstname(resultSet.getString("measureFirstName"));

                    // Create the treeitem for this measure.
                    measureTreeItem = new TreeItem<>((Object) measure);
                    measureTreeItem.setExpanded(true);
                    sectionTreeItem.getChildren().add(measureTreeItem );

                    // Reset the children ids to ensure that they will be recreated.
                    lastMeasureId = resultSet.getInt("measureId");
                    lastTargetId = -1;
                }

                // Target Creation.
                if (lastTargetId != resultSet.getInt("targetId")) {
                    final Target target = Target.initEmpty();
                    target.setId(resultSet.getInt("targetId"));
                    target.setText(resultSet.getString("targetText"));

                    // Create the treeitem for this target.
                    targetTreeItem = new TreeItem<>((Object) target);
                    targetTreeItem.setExpanded(false);
                    measureTreeItem.getChildren().add(targetTreeItem);

                    // Reset the children ids to ensure that they will be recreated.
                    lastTargetId = resultSet.getInt("targetId");
                }
            }

            closeAll();
        } catch (SQLException ex) {
            super.errorMessage = ex.getMessage();
        }

        tree.setRoot(treeRoot);
        final TargetTree targetTree = this;
        tree.setCellFactory(new Callback<TreeView<Object>, TreeCell<Object>>() {
            @Override
            public TreeCell<Object> call(TreeView<Object> p) {
                return new TargetTreeCell(targetTree);
            }
        });

        // Select a Tree Item.
        tree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observable, Object oldValue, Object newValue) {
                selectedItem = (TreeItem) newValue;
            }
        });
    }

    public void collapseChildren() {
        Thread thread = new Thread(new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < selectedItem.getChildren().size(); i++) {
                            TreeItem<Object> current = (TreeItem<Object>) selectedItem.getChildren().get(i);
                            if (!current.isLeaf()) {
                                current.setExpanded(false);
                            }
                            current = null;
                        }
                        selectedItem.setExpanded(false);
                        System.gc();
                    }
                });
                return null;
            }
        });
        thread.setDaemon(true);
        thread.start();
    }

    public void expandChildren() {
        Thread thread = new Thread(new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < selectedItem.getChildren().size(); i++) {
                            TreeItem<Object> current = (TreeItem<Object>) selectedItem.getChildren().get(i);
                            if (!current.isLeaf()) {
                                current.setExpanded(true);
                            }
                            current = null;
                        }
                        selectedItem.setExpanded(true);
                        System.gc();
                    }
                });
                return null;
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
}

下面是自定义 TreeCell 类。

public class TargetTreeCell extends TreeCell<Object> {

    private TargetTree targetTree;

    public TargetTreeCell(TargetTree targetTree) {
        super();
        this.targetTree = targetTree;
    }

    @Override
    public void updateItem(Object item, boolean empty) {
        super.updateItem(item, empty);

        if (item != null) {
            if (item instanceof Target) {
                initTarget(item);
            } else if (item instanceof Measure) {
                initMeasure(item);
            } else if (item instanceof Section) {
                initSection(item);
            } else if (item instanceof Department) {
                initDepartment(item);
            } else if (item instanceof String) {
                initRoot(item);
            }
        }
    }

    ///<editor-fold defaultstate="collapsed" desc="Tree Item Initialization">
    private void initRoot(Object item) {
        // Create Menu Items.
        MenuItem expandAllMenuItems = new MenuItem("Expand All");
        MenuItem collapseAllMenuItems = new MenuItem("Collapse All");

        // Event Haddlers for each Menu Items.
        expandAllMenuItems.setOnAction(new EventHandler() {
            @Override
            public void handle(Event event) {
            }
        });
        collapseAllMenuItems.setOnAction(new EventHandler() {
            @Override
            public void handle(Event event) {
                targetTree.collapseChildren();
            }
        });

        // Create Menu and add Menu Items.
        ContextMenu contextMenu = new ContextMenu();
        contextMenu.getItems().addAll(expandAllMenuItems, collapseAllMenuItems);

        //Init Root Tree Item.
        String root = (String) item;
        setText(root);
        setContextMenu(contextMenu);
    }

    private void initDepartment(Object item) {
        // Create Menu Items.
        MenuItem expandAllMenuItems = new MenuItem("Expand All");
        MenuItem collapseAllMenuItems = new MenuItem("Collapse All");

        // Event Haddlers for each Menu Items.
        expandAllMenuItems.setOnAction(new EventHandler() {
            @Override
            public void handle(Event event) {
                targetTree.expandChildren();
            }
        });
        collapseAllMenuItems.setOnAction(new EventHandler() {
            @Override
            public void handle(Event event) {
                targetTree.collapseChildren();
            }
        });

        // Create Menu and add Menu Items.
        ContextMenu contextMenu = new ContextMenu();
        contextMenu.getItems().addAll(expandAllMenuItems, collapseAllMenuItems);

        //Init Department Tree Item.
        Department department = (Department) item;
        setText(department.getName());
        setContextMenu(contextMenu);
    }

    private void initSection(Object item) {
        // Create Menu Items.
        MenuItem expandAllMenuItems = new MenuItem("Expand All");
        MenuItem collapseAllMenuItems = new MenuItem("Collapse All");

        // Event Haddlers for each Menu Items.
        expandAllMenuItems.setOnAction(new EventHandler() {
            @Override
            public void handle(Event event) {
                targetTree.expandChildren();
            }
        });
        collapseAllMenuItems.setOnAction(new EventHandler() {
            @Override
            public void handle(Event event) {
                targetTree.collapseChildren();
            }
        });

        // Create Menu and add Menu Items.
        ContextMenu contextMenu = new ContextMenu();
        contextMenu.getItems().addAll(expandAllMenuItems, collapseAllMenuItems);

        //Init Section Tree Item.
        Section section = (Section) item;
        setText(section.getName());
        setContextMenu(contextMenu);
    }

    private void initMeasure(Object item) {
        // Create Menu Items.
        MenuItem expandAllMenuItems = new MenuItem("Expand");
        MenuItem collapseAllMenuItems = new MenuItem("Collapse");

        // Event Haddlers for each Menu Items.
        expandAllMenuItems.setOnAction(new EventHandler() {
            @Override
            public void handle(Event event) {
                targetTree.expandChildren();
            }
        });
        collapseAllMenuItems.setOnAction(new EventHandler() {
            @Override
            public void handle(Event event) {
                targetTree.collapseChildren();
            }
        });

        // Create Menu and add Menu Items.
        ContextMenu contextMenu = new ContextMenu();
        contextMenu.getItems().addAll(expandAllMenuItems, collapseAllMenuItems);

        //Init Section Tree Item.
        Measure measure = (Measure) item;
        setText(measure.getLastname() + " " + measure.getFirstname());
        setContextMenu(contextMenu);
    }

    private void initTarget(Object item) {
        //Init Section Tree Item.
        Target target = (Target) item;
        setText(target.getText());
    }
    ///</editor-fold>
}

如果我有复制粘贴错误,请原谅我..我编译没有问题。代码运行没有错误。我的问题出在头等舱的 expandChildren() 和 collapseChildren() 方法上。在以前的版本中,我没有使用线程,而是使用递归来使所有子 TreeItems(及其子 TreeItems..)崩溃,但内存成本更高。

4

1 回答 1

4

我找到了我的问题的答案!我将用一个例子来解释它。我用 100 个 TreeItem 初始化了一个 TreeView,结果是一个具有 3 个级别的树结构。在屏幕上,树只显示了其中的 45 个。要查看其他我必须向上/向下滚动或展开折叠的 TreeItems。在每种情况下,都会调用 updateItem 方法来构造新的 TreeItem,这些 TreeItem 将出现在屏幕树上的可见部分,因此它们都出现在屏幕中。

当我折叠展开的 TreeItem 时,updateItem 方法将运行。这就是内存和cpu成本的原因!我不得不折叠〜200个TreeItems,它们的父级扩展了。

我用一种非常简单的方法解决了我的问题。就在我开始折叠所有内容之前,我折叠了父 TreeItem。因此,我首先折叠了父级,然后是所有子级。当子项从源代码(setExpanded(false))中一一折叠时,updateItem 方法没有运行,因为它们的父项和子项 TreeItems 都不存在于屏幕中。

通过这种方式,我节省了很多内存和 CPU 时间,而这些时间我就像一个假人一样度过。

于 2013-03-19T20:12:41.383 回答