使用进度指示器更新 javafx treeview
Updating javafx treeview in with progress indicator
我正在尝试创建一个 class,它基本上是一个具有计算机文件结构的树项(是的,我正在尝试在这里创建一个文件管理器)。我已将 TreeView 设置为延迟加载,并且效果很好。但是对于非常长的目录(有很多文件夹的目录,例如 %Windir%/WinSxS
我已经实现了进度指示器(工作还没有完成,但这是我想要的)需要一些时间。它有效也是,但它不会更新 ui.
我知道任务更新进度指示器必须在另一个线程(javafx 线程除外)中 运行。所以我尝试了Thread
、Platform#runLater
、CountdownLatch
等(可能我实现的方式不对)。但是我无法将进度指示器 ui 或 运行 更新为 Concurrent Modification
错误。以下是我到目前为止的 class 代码(我多次尝试之一):
class FileTreeItemImpl extends TreeItem<String> {
private boolean isLeaf = false;
private boolean childrenCached = false;
private boolean leafCached = false;
// File is basically one of root dirs
private final File file;
// pi is passed as constructor from the controller class (in which pi is injected via FXML)
private volatile ProgressIndicator pi;
// This is to control pi's progress
private static volatile double x = 0;
public FileTreeItemImpl() {
this("Root", new File("/"), null);
}
public FileTreeItemImpl(String file, File fileObj, ProgressIndicator pi) {
super(file);
this.pi = pi;
this.file = fileObj;
}
@Override
public ObservableList<TreeItem<String>> getChildren() {
if (!childrenCached) {
childrenCached = true;
if (file != null && file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
x = 0;
new Thread() {
@Override
public void run() {
for (File childFile : files) {
pi.setProgress(x / (files.length - 1));
x++;
if (childFile.isDirectory())
getChildren().add(new FileTreeItemImpl(childFile.getName(), childFile, pi));
}
}
}.start();
}
}
}
return super.getChildren();
}
@Override
public boolean isLeaf() {
if (!leafCached) {
leafCached = true;
isLeaf = file.isFile();
}
return isLeaf;
}
}
注意:上面的代码完全符合我的要求,但直到该目录的子文件夹数量很少。如果太多(同样,如 %winddir%/WinSxS
),那么我会得到 ConcurrentModificationException
.
编辑:
无论如何,这里是堆栈跟踪:
Exception in thread "JavaFX Application Thread" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:386)
at java.util.AbstractList$Itr.next(AbstractList.java:355)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:892)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:880)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:894)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:880)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:894)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:880)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:894)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:880)
at javafx.scene.control.TreeUtil.getExpandedDescendantCount(TreeUtil.java:40)
at javafx.scene.control.TreeUtil.updateExpandedItemCount(TreeUtil.java:49)
at javafx.scene.control.TreeView.updateExpandedItemCount(TreeView.java:1042)
at javafx.scene.control.TreeView.getExpandedItemCount(TreeView.java:614)
at javafx.scene.control.TreeView$TreeViewFocusModel.getItemCount(TreeView.java:1649)
at javafx.scene.control.FocusModel.isFocused(FocusModel.java:128)
at javafx.scene.control.TreeCell.updateFocus(TreeCell.java:557)
at javafx.scene.control.TreeCell.indexChanged(TreeCell.java:476)
at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:116)
at com.sun.javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1957)
at com.sun.javafx.scene.control.skin.VirtualFlow.addLeadingCells(VirtualFlow.java:1246)
at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1194)
at javafx.scene.Parent.layout(Parent.java:1079)
at javafx.scene.Parent.layout(Parent.java:1085)
at javafx.scene.Parent.layout(Parent.java:1085)
at javafx.scene.Parent.layout(Parent.java:1085)
at javafx.scene.Scene.doLayoutPass(Scene.java:552)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2397)
at com.sun.javafx.tk.Toolkit.lambda$runPulse(Toolkit.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:354)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:381)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:510)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit4(QuantumToolkit.java:319)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null8(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
我有一个类似的实现,使用 ProgressIndicator
作为 TreeItem
的一部分。 - 我通过使用 Task
来解决它,我用它来更新 UI。
我的 Task
实现如下所示:
class ProgressWorker extends Task<Void> {
...
@Override
protected Void call() throws Exception {
while (getCurrentRow() < getMaxRow()) {
Thread.sleep(100);
updateProgress(getCurrentRow(), getMaxRow());
updateMessage(getTextMessage());
}
return null;
}
}
使用bind
将任务progressProperty绑定到指标:
progressIndicator.progressProperty().bind(this.worker.progressProperty());
现在将工作线程作为线程启动。 - 你的操作作为线程。
在您的线程内部,您应该能够更新其 属性 绑定到指标的任务。
(很郁闷,用英语解释这个比我想象的要难)
我正在尝试创建一个 class,它基本上是一个具有计算机文件结构的树项(是的,我正在尝试在这里创建一个文件管理器)。我已将 TreeView 设置为延迟加载,并且效果很好。但是对于非常长的目录(有很多文件夹的目录,例如 %Windir%/WinSxS
我已经实现了进度指示器(工作还没有完成,但这是我想要的)需要一些时间。它有效也是,但它不会更新 ui.
我知道任务更新进度指示器必须在另一个线程(javafx 线程除外)中 运行。所以我尝试了Thread
、Platform#runLater
、CountdownLatch
等(可能我实现的方式不对)。但是我无法将进度指示器 ui 或 运行 更新为 Concurrent Modification
错误。以下是我到目前为止的 class 代码(我多次尝试之一):
class FileTreeItemImpl extends TreeItem<String> {
private boolean isLeaf = false;
private boolean childrenCached = false;
private boolean leafCached = false;
// File is basically one of root dirs
private final File file;
// pi is passed as constructor from the controller class (in which pi is injected via FXML)
private volatile ProgressIndicator pi;
// This is to control pi's progress
private static volatile double x = 0;
public FileTreeItemImpl() {
this("Root", new File("/"), null);
}
public FileTreeItemImpl(String file, File fileObj, ProgressIndicator pi) {
super(file);
this.pi = pi;
this.file = fileObj;
}
@Override
public ObservableList<TreeItem<String>> getChildren() {
if (!childrenCached) {
childrenCached = true;
if (file != null && file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
x = 0;
new Thread() {
@Override
public void run() {
for (File childFile : files) {
pi.setProgress(x / (files.length - 1));
x++;
if (childFile.isDirectory())
getChildren().add(new FileTreeItemImpl(childFile.getName(), childFile, pi));
}
}
}.start();
}
}
}
return super.getChildren();
}
@Override
public boolean isLeaf() {
if (!leafCached) {
leafCached = true;
isLeaf = file.isFile();
}
return isLeaf;
}
}
注意:上面的代码完全符合我的要求,但直到该目录的子文件夹数量很少。如果太多(同样,如 %winddir%/WinSxS
),那么我会得到 ConcurrentModificationException
.
编辑: 无论如何,这里是堆栈跟踪:
Exception in thread "JavaFX Application Thread" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:386)
at java.util.AbstractList$Itr.next(AbstractList.java:355)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:892)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:880)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:894)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:880)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:894)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:880)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:894)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:880)
at javafx.scene.control.TreeUtil.getExpandedDescendantCount(TreeUtil.java:40)
at javafx.scene.control.TreeUtil.updateExpandedItemCount(TreeUtil.java:49)
at javafx.scene.control.TreeView.updateExpandedItemCount(TreeView.java:1042)
at javafx.scene.control.TreeView.getExpandedItemCount(TreeView.java:614)
at javafx.scene.control.TreeView$TreeViewFocusModel.getItemCount(TreeView.java:1649)
at javafx.scene.control.FocusModel.isFocused(FocusModel.java:128)
at javafx.scene.control.TreeCell.updateFocus(TreeCell.java:557)
at javafx.scene.control.TreeCell.indexChanged(TreeCell.java:476)
at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:116)
at com.sun.javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1957)
at com.sun.javafx.scene.control.skin.VirtualFlow.addLeadingCells(VirtualFlow.java:1246)
at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1194)
at javafx.scene.Parent.layout(Parent.java:1079)
at javafx.scene.Parent.layout(Parent.java:1085)
at javafx.scene.Parent.layout(Parent.java:1085)
at javafx.scene.Parent.layout(Parent.java:1085)
at javafx.scene.Scene.doLayoutPass(Scene.java:552)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2397)
at com.sun.javafx.tk.Toolkit.lambda$runPulse(Toolkit.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:354)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:381)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:510)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit4(QuantumToolkit.java:319)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null8(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
我有一个类似的实现,使用 ProgressIndicator
作为 TreeItem
的一部分。 - 我通过使用 Task
来解决它,我用它来更新 UI。
我的 Task
实现如下所示:
class ProgressWorker extends Task<Void> {
...
@Override
protected Void call() throws Exception {
while (getCurrentRow() < getMaxRow()) {
Thread.sleep(100);
updateProgress(getCurrentRow(), getMaxRow());
updateMessage(getTextMessage());
}
return null;
}
}
使用bind
将任务progressProperty绑定到指标:
progressIndicator.progressProperty().bind(this.worker.progressProperty());
现在将工作线程作为线程启动。 - 你的操作作为线程。 在您的线程内部,您应该能够更新其 属性 绑定到指标的任务。
(很郁闷,用英语解释这个比我想象的要难)