[英]Manually expand/collapse all treeitems memory cost javafx 2.2
我正在使用 Netbeans 7.2 開發一個 JavaFX 2.2 應用程序。 我正在使用 treeview 並擴展了 TreeCell 以向每個 TreeItem 提供一個上下文菜單,其中的 MenuItem 具有“Collpase All”功能。 treeview 的最大深度級別為 4。當用戶右鍵單擊級別 2 的 TreeItem 並單擊“全部折疊”菜單項時,我想讓級別 3 的所有 TreeItem 折疊 (setExpanded(false))。 您可以在下面看到我正在使用的代碼。 我的問題是這個操作的 memory 和 CPU 成本。 我在第 3 級插入了 250 個 TreeItem。每次 collapseAll 單擊時,一次 collapse all 操作的成本約為 200MB memory,花費大約 2s 的時間。 我的開發者計算機的 CPU 是 Intel i5 (3.3GHz),我有 8GB 的 memory? 這些硬件成本是正常的還是我的代碼有問題? 我是否使用了錯誤的方式來折疊它們?
這個 class 管理 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 class。
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>
}
如果我有復制粘貼錯誤請原諒我..我編譯沒有問題。 代碼運行沒有錯誤。 我的問題出在第一個 class 的方法 expandChildren() 和 collapseChildren() 上。在以前的版本中,我沒有使用線程,而是使用遞歸來使所有子 TreeItems(及其子 TreeItems..)崩潰,但是memory 成本更高。
我找到了問題的答案! 我將用一個例子來解釋。 我用100個TreeItems初始化一個TreeView,結果是一個具有3個級別的樹結構。 在屏幕上,樹只顯示了其中的45個。 要查看其他項目,我必須向上/向下滾動或展開折疊的TreeItems。 在每種情況下,都會調用方法updateItem來構造新的TreeItem,這些新的TreeItem將在屏幕樹上顯示為可見,因此它們都出現在屏幕上。
當我折疊展開的TreeItem時,updateItem方法將運行。 這就是內存和CPU成本的原因! 我不得不折疊〜總共200個TreeItem,然后它們的父級展開了。
我用一種非常簡單的方法解決了我的問題。 在我開始折疊所有內容之前,我折疊了父級TreeItem。 因此,我首先讓父母崩潰,然后讓所有孩子崩潰。 從源代碼(setExpanded(false))逐一折疊子項時,由於子項的父項和子項TreeItem在屏幕中不存在,因此updateItem方法未運行。
通過這種方式,我節省了很多內存和CPU時間,而我像一個虛擬人一樣度過。
我犯了同樣的錯誤,發生在我實施 MenuItem 以完全折疊整個 TreeItems(當前選擇作為父項)子分支時。 但是 collapse 方法將選擇清除為減一 (-1),並且該更改不可見,因為它之后沒有刷新父項單元格。 所以乍一看似乎沒有任何變化,因為焦點仍然在同一行上可見。 我想需要清除皮膚選擇器才能折疊子項,或者接管選擇索引。 所以首先折疊父項,所有子項都應該折疊,然后重置選擇索引,然后再次展開父項。 非常感謝
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.