在异步加载ListView项期间显示一个ProgressIndicator [英] Display a ProgressIndicator during an async loading of ListView items
问题描述
我正在尝试执行异步背景ListView
项目加载时显示ProgressIndicator
.我想要的行为是:
I am trying to display a ProgressIndicator
while performing an async background ListView
item loading. The behaviour that I desire is:
- Before start loading the
ListView
items, display aProgressIndicator
with a indeterminate progress; - Asynchronously start loading the
ListView
items; - After the
ListView
items loading was finished, hide theProgressIndicator
.
这是我未成功尝试的 ssce :
public class AsyncLoadingExample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<String>();
final ObservableList<String> listItems = FXCollections.observableArrayList();
final ProgressIndicator loadingIndicator = new ProgressIndicator();
final Button button = new Button("Click me to start loading");
primaryStage.setTitle("Async Loading Example");
listView.setPrefSize(200, 250);
listView.setItems(listItems);
loadingIndicator.setVisible(false);
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// I have hoped it whould start displaying the loading indicator (actually, at the end of this
// method execution (EventHandler.handle(ActionEvent))
loadingIndicator.setVisible(true);
// asynchronously loads the list view items
Platform.runLater(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000l); // just emulates some loading time
// populates the list view with dummy items
while (listItems.size() < 10) listItems.add("Item " + listItems.size());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
loadingIndicator.setVisible(false); // stop displaying the loading indicator
}
}
});
}
});
VBox root = VBoxBuilder.create()
.children(
StackPaneBuilder.create().children(listView, loadingIndicator).build(),
button
)
.build();
primaryStage.setScene(new Scene(root, 200, 250));
primaryStage.show();
}
}
在此示例中,ListView
项是异步加载的.但是,ProgressIndicator
不会出现.仍然在此示例中,如果我省略所有Platform.runLater(...)
代码,则会显示ProgressIndicator
,但是,当然不会加载ListView
项.
In this example, the ListView
items are loaded asynchronously. However, the ProgressIndicator
do not show up. Still in this example, if I omit all the Platform.runLater(...)
code, the ProgressIndicator
shows up, but, of course, the ListView
items are not loaded.
因此,我该如何实现所需的行为?
Thus, how can I achieve the desired behaviour?
推荐答案
问题是,在给出的示例中,我误用了Platform.runLater(...)
方法,因此也误用了JavaFX Application Thread.
The problem is that, at the presented example, I am misusing the Platform.runLater(...)
method, and consequently, the JavaFX Application Thread.
As mentioned at the Platform.runLater()
method documentation, this method
将来在某些未指定的时间在JavaFX Application Thread上运行指定的Runnable.
Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future.
并且,JavaFX Application Thread是开发人员代码可以从中访问和修改JavaFX场景图的线程,从而直观地反映了所执行的修改.
And, the JavaFX Application Thread is the thread from which the JavaFX scene graph can be accessed and modified by the developer code, visually reflecting the performed modifications.
因此,当我开始从此线程加载ListView
项时,UI变得无响应(这也表示为
Thus, when I start loading the ListView
items from this thread, the UI becomes unresponsive (this is also stated here) until the loading is finished.
要解决此问题,必须在另一个线程上加载ListView
项目,并且必须在应用程序线程上仅执行ListView
更新.
To solve the problem, the ListView
items must be loaded at another thread and only the ListView
update must be performed at Application Thread.
上述更正如下:
public class AsyncLoadingExample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<String>();
final ObservableList<String> listItems = FXCollections.observableArrayList();
final ProgressIndicator loadingIndicator = new ProgressIndicator();
final Button button = new Button("Click me to start loading");
primaryStage.setTitle("Async Loading Example");
listView.setPrefSize(200, 250);
listView.setItems(listItems);
loadingIndicator.setVisible(false);
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
final List<String> loadedItems = new LinkedList<String>();
// clears the list items and start displaying the loading indicator at the Application Thread
listItems.clear();
loadingIndicator.setVisible(true);
// loads the items at another thread, asynchronously
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000l); // just emulates some loading time
// populates the list view with dummy items
while (loadedItems.size() < 10) loadedItems.add("Item " + loadedItems.size());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// just updates the list view items at the
// Application Thread
Platform.runLater(new Runnable() {
@Override
public void run() {
listItems.addAll(loadedItems);
loadingIndicator.setVisible(false); // stop displaying the loading indicator
}
});
}
}
}).start();
}
});
VBox root = VBoxBuilder.create()
.children(
StackPaneBuilder.create().children(listView, loadingIndicator).build(),
button
)
.build();
primaryStage.setScene(new Scene(root, 200, 250));
primaryStage.show();
}
}
这篇关于在异步加载ListView项期间显示一个ProgressIndicator的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!