Я разрабатываю приложение JavaFX 2.2 с использованием Netbeans 7.2. Я работаю с древовидным представлением, и я расширил TreeCell, чтобы предоставить каждому TreeItem контекстное меню с MenuItem с функцией «Свернуть все». Максимальный уровень глубины древовидного представления - 4. Когда пользователь щелкает правой кнопкой мыши элемент TreeItem уровня 2 и щелкает «Свернуть все» MenuItem, я хочу, чтобы все элементы TreeItem уровня 3 были свернуты (setExpanded (false)). Ниже вы можете увидеть код, который я использую. Моя проблема заключается в затратах памяти и ЦП для этой операции. Я вставил 250 TreeItems на уровень 3. Стоимость всей операции свертывания составляла ~ 200 МБ памяти на каждый щелчок мыши и занимает около 2 секунд времени! ЦП моего компьютера разработчика - Intel i5 (3,3 ГГц), а у меня 8 ГБ памяти. Это нормальная стоимость оборудования или я что-то не так делаю в своем коде? Я использую неправильный способ свернуть их?

Этот класс управляет TreeView. Загружает данные из базы данных, перезагружает их, знает выбранный TreeItem и разворачивает / свертывает выбранные дочерние TreeItems.

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 ...) свернуться, но затраты памяти были больше.

2
Georgios Syngouroglou 19 Мар 2013 в 05:59
Чтобы быстрее получить более качественную помощь, опубликуйте SSCCE (sscce.org).
 – 
gontard
19 Мар 2013 в 14:13

2 ответа

Лучший ответ

Я нашел ответ на свою проблему! Я объясню это на примере. Я инициализирую TreeView с помощью 100 TreeItems, и в результате получается трехуровневая древовидная структура. На экране дерева отображалось всего 45 из них. Чтобы просмотреть другие, мне пришлось прокрутить вверх / вниз или развернуть свернутые TreeItems. В каждом случае вызывается метод updateItem для создания новых TreeItems, которые будут отображаться в дереве, видимом на экране, и, следовательно, все они отображались на экране.

Когда я сворачиваю развернутый TreeItem, запускается метод updateItem. Это было причиной стоимости памяти и процессора! Мне пришлось свернуть ~ 200 TreeItems, вот и все, и их родительский элемент расширился.

Я решил свою проблему очень простым способом. Незадолго до того, как я начал сворачивать все, я свернул родительский элемент TreeItem. Таким образом, я сначала свернул родительский элемент, а затем все дочерние элементы. Когда дочерние элементы были свернуты один за другим из исходного кода (setExpanded (false)), метод updateItem НЕ работал, потому что их родительский элемент и, следовательно, дочерние элементы TreeItems не существовали на экране.

Таким образом я сэкономил много памяти и времени процессора, которые тратил на пустышку.

4
Georgios Syngouroglou 20 Мар 2013 в 20:52
Большое вам спасибо, у меня сегодня была большая проблема с этим в древовидном представлении с 1000 элементами .. :)
 – 
GOXR3PLUS
17 Сен 2017 в 20:23

Я сделал ту же ошибку, когда я реализовал MenuItem, чтобы полностью свернуть все дочерние ветви TreeItems (текущий выбор в качестве родительского). Но метод сворачивания очищает выделение до минус одного (-1), и это изменение не было видно, потому что впоследствии ячейка родительского элемента не обновлялась. Так что на первый взгляд казалось, что ничего не изменилось, потому что фокус все еще был виден в той же строке. Я предполагаю, что селектор скинов должен быть очищен, чтобы свернуть дочерние элементы или взять на себя индекс выбора. поэтому просто сверните родительский элемент, сначала из которого должны быть свернуты все дочерние элементы, и затем сбросьте индекс выбора, а затем снова разверните родительский элемент.

0
Henryk Zschuppan 2 Янв 2023 в 18:11