java - 来自 PreLoader 的多线程 - JavaFX
问题描述
我正在开发一个 JavaFX 应用程序,该应用程序需要在启动主应用程序阶段之前从文件中加载资源。我完成此任务的解决方案是使用 PreLoader,以便用户在加载资源之前无法与应用程序交互(非常标准的东西)。
我有一个扩展 PreLoader 类的类,它创建一个加载 .fxml 文件的对象,这是 PreLoader 场景将显示的内容。在我尝试插入代码来加载文件之前,它工作得很好而且很花哨。
我想同时执行此操作,以便在加载文件期间将状态更新到屏幕上。我已经研究过在这里使用任务和论坛,但没有帮助,因为我不知道将代码放在哪里。
类扩展 PreLoader:
public class SplashPreLoader extends Preloader {
private Stage stage;
private SplashScene loadScreen;
public void start(Stage stage) throws Exception {
SplashScene intro = new SplashScene();
this.loadScreen = intro;
this.stage = stage;
stage.setScene(new Scene(intro, 475, 425));
stage.initStyle(StageStyle.UNDECORATED);
stage.show();
}
@Override
public void handleProgressNotification(ProgressNotification pn) {
//bar.setProgress(pn.getProgress());
}
@Override
public void handleStateChangeNotification(StateChangeNotification evt) {
if (evt.getType() == StateChangeNotification.Type.BEFORE_LOAD) {
//loadScreen.setStatusMessage("Hello Mister");
}
if (evt.getType() == StateChangeNotification.Type.BEFORE_START) {
/* Task<Void> task = new Task<Void>() {
@Override
public Void call() {
updateMessage("this mesashlkjfhkjlsd");
return null ;
}
};
statusMessage.textProperty().bind(task.messageProperty());
task.messageProperty().addListener((obs, oldMessage, newMessage) -> loadScreen.setStatusMessage(newMessage));
Thread th = new Thread(task);
th.setDaemon(true);
th.start();*/
//loadScreen.loadWebDriver();
stage.hide();
}
}
场景组件:
public class SplashScene extends StackPane {
@FXML public Label statusMessage;
public SplashScene() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("splash_scene.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
//statusMessage.setTextFill(Color.WHITE);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public String getStatusMessage() { return statusMessageProperty().getValue(); }
public void setStatusMessage(String value) {
statusMessageProperty().set(value);
System.out.println("at hert");
}
public StringProperty statusMessageProperty() { return statusMessage.textProperty(); }
public void loadWebDriver() {
//This is the function that I want to call to load all the files.
}
statusMessage
是我在加载文件时要修改的标签。我尝试将任务(线程)放在loadWebDriver()
函数的末尾start()
和前面的 if 子句中,但它没有产生我想要的结果。
我也觉得很奇怪,因为当我在没有 的情况下尝试这个时task
,我在文件加载代码之前有标签更改代码,但它们总是以相反的顺序执行。
我觉得这可以从文档中得到帮助,但这对我来说没有任何意义..有人知道这意味着什么吗?
请注意,预加载器遵循与其他 JavaFX 应用程序相同的规则,包括 FX 线程规则。特别是,类构造函数和 init() 方法将在非 FX 线程上调用,而 start() 将在 FX 应用程序线程上执行。这也意味着应用程序构造函数/init() 将与预加载器 start() 同时运行。
我需要使用 init() 方法吗?
解决方案
这是一个希望对您有所帮助的示例。您可以做的一件事是使用 aTask
加载所有数据。完成Task
后,使用Applying MVC with JavaFX中的想法将其传递Model
给任何Controller
需要它的人。
主要的
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import javafx.concurrent.Task;
/**
* JavaFX App
*/
public class App extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("primary.fxml"));
Parent root = loader.load();
PrimaryController primaryController = loader.getController();
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
final Task<DataModel> task = new Task<DataModel>(){
@Override
protected DataModel call() throws Exception
{
updateProgress(0, 3);
DataModel dataModel = new DataModel();
dataModel.loadListViewData();
Thread.sleep(2000);
updateProgress(1, 3);
dataModel.loadComoBoxData();
Thread.sleep(2000);
updateProgress(2, 3);
dataModel.loadTextAreaData();
Thread.sleep(2000);
updateProgress(3, 3);
Thread.sleep(1000);
return dataModel;
}
};
task.setOnSucceeded((event) -> {
try
{
FXMLLoader secondaryLoader = new FXMLLoader(getClass().getResource("secondary.fxml"));
Stage secondaryStage = new Stage();
Parent secondaryRoot = secondaryLoader.load();
SecondaryController secondaryController = secondaryLoader.getController();
secondaryController.initModel(task.getValue());
secondaryStage.setTitle("Scene One");
secondaryStage.setScene(new Scene(secondaryRoot, 500, 500));
secondaryStage.show();
primaryStage.close();
} catch (IOException e)
{
e.printStackTrace();
}
});
primaryController.getPBSplashValue().progressProperty().bind(task.progressProperty());
primaryController.getPISplash().progressProperty().bind(task.progressProperty());
new Thread(task).start();
}
public static void main(String[] args) {
launch();
}
}
主控制器/闪屏
import javafx.fxml.FXML;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
public class PrimaryController
{
@FXML
ProgressBar pbSplash;
@FXML
ProgressIndicator piSplash;
public ProgressBar getPBSplashValue()
{
return pbSplash;
}
public ProgressIndicator getPISplash()
{
return piSplash;
}
}
初级FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sed.test.maventtestproject.PrimaryController">
<children>
<VBox maxHeight="-Infinity" maxWidth="-Infinity">
<children>
<ProgressBar fx:id="pbSplash" prefWidth="200.0" progress="0.0" />
<ProgressIndicator fx:id="piSplash" progress="0.0" />
</children>
</VBox>
</children>
</StackPane>
SecondayController/FirstSceneAfterSplashScreen
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
public class SecondaryController {
@FXML ListView<String> lvMain;
@FXML ComboBox<String> cbMain;
@FXML TextArea taMain;
private DataModel model ;
public void initModel(DataModel model) {
if (this.model != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.model = model ;
lvMain.setItems(this.model.getListViewData());
cbMain.setItems(this.model.getComboBoxData());
taMain.setText(this.model.getTextAreaData());
}
}
二级FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sed.test.maventtestproject.SecondaryController">
<children>
<ListView fx:id="lvMain" prefHeight="200.0" prefWidth="200.0" />
<ComboBox fx:id="cbMain" prefWidth="150.0" />
<TextArea fx:id="taMain" prefHeight="200.0" prefWidth="200.0" wrapText="true" />
</children>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</VBox>
模型
import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
/**
*
* @author blj0011
*/
public class DataModel
{
private final ObservableList<String> listViewData = FXCollections.observableArrayList();
private final ObservableList<String> comboBoxwData = FXCollections.observableArrayList();
private final StringProperty textAreaDataProperty = new SimpleStringProperty();
public DataModel()
{
}
public void loadListViewData()
{
listViewData.addAll(retrieveListViewDataFromDB());
}
public ObservableList<String> getListViewData()
{
return listViewData;
}
public void loadComoBoxData()
{
comboBoxwData.addAll(retrieveComboBoxDataFromDB());
}
public ObservableList<String> getComboBoxData()
{
return comboBoxwData;
}
public void loadTextAreaData()
{
textAreaDataProperty.set(retrieveTextAreaDataFromDB());
}
public StringProperty getTextAreaDataProperty()
{
return textAreaDataProperty;
}
public String getTextAreaData()
{
return textAreaDataProperty.get();
}
//Private methods that fake retrieving data from the database.
private List<String> retrieveListViewDataFromDB()
{
List<String> dataFromDB = new ArrayList();
for(int i = 0; i < 1000; i++)
{
dataFromDB.add(Integer.toString(i));
}
return dataFromDB;
}
private List<String> retrieveComboBoxDataFromDB()
{
List<String> dataFromDB = new ArrayList();
dataFromDB.add("A");
dataFromDB.add("B");
dataFromDB.add("C");
dataFromDB.add("D");
dataFromDB.add("E");
dataFromDB.add("F");
return dataFromDB;
}
private String retrieveTextAreaDataFromDB()
{
return "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
}
}
推荐阅读
- php - 如何在 wordpress 的单个页面中显示特定帖子?
- c++ - 为什么当窗口在一个类中时,SFML 窗口不显示?
- c - RBIE 中断只工作一次 - PIC16F877A
- smpp - kannel收货问题
- python - 度量标准 Python 休息 api
- google-maps - Flutter中的谷歌地图没有响应触摸事件
- java - 如何在字符串中查找模式并删除所有不匹配的内容
- python - 如何在 pygame 中使用 pygame.time.set_timer()?
- cassandra - 如何在 mac 上安装 dsbulk?
- rust - Bindgen 跳过 C++ 头文件中的定义