JavaFX 加载 FXML 的性能问题
JavaFX loading performance problems with FXML
我有一个带有切换按钮的主文件,单击其中一个按钮后,我会启动一个新的 FXMLLoader
,其中包含大约 10 个 SVG、15 个文本字段和 6 个微调器。此外,它有 CSS 相应地渲染节点...它加载正常,没有错误或任何问题,但在显示场景之前需要一两秒。
我猜这是由于同时初始化的节点数量所致。有没有办法在节点开始初始化之前显示场景?
注意:我的项目需要我在点击时导航到不同的场景。
ToggleButton 从它被选中的地方
if(settings.isSelected()){
Stage stage = (Stage) mainWrapper.getScene().getWindow();
Parent root = FXMLLoader.load(getClass().getResource("/fxmlFiles/settings.fxml"));
stage.setScene(new Scene(root, Screen.getPrimary().getVisualBounds().getWidth(),
Screen.getPrimary().getVisualBounds().getHeight()));
stage.centerOnScreen();
//stage.setMaximized(true);
stage.show();
}
fxml文件
<FlowPane xmlns:fx="http://javafx.com/fxml"
fx:controller="controllers.motelInfoController"
stylesheets="/cssFiles/motelInfo.css"
fx:id="content">
<VBox fx:id="mainWrapper">
<VBox fx:id="validationWrapper">
<Label fx:id="validationLabel" visible="false"/>
</VBox>
<VBox fx:id="generalInfo">
<HBox> <!--First Row-->
<HBox>
<Group>
<SVGPath fx:id="iconMotelName" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miName" promptText="Motel Name"/>
</Group>
</HBox>
<HBox>
<Group>
<TextField fx:id="miFranchiseName" promptText="Franchise Name"/>
</Group>
</HBox>
</HBox>
<HBox> <!--Second Row-->
<HBox>
<Group>
<SVGPath fx:id="iconMotelAddress" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miAddress" promptText="Street Name"/>
</Group>
</HBox>
<HBox>
<Group>
<TextField fx:id="miCity" promptText="City/Town Name"/>
</Group>
</HBox>
</HBox>
<HBox><!--Third Row-->
<HBox style="-fx-spacing: 25px;">
<Label></Label> <!--Empty label for purpose of leaving icon gap since N/A here-->
<Group>
<TextField fx:id="miState" promptText="State"/>
</Group>
</HBox>
<HBox>
<Group>
<TextField fx:id="miZipCode" promptText="Zip Code"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelContact" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miContact" promptText="Contact no."/>
</Group>
</HBox>
</HBox>
<HBox> <!--Fourth Row-->
<HBox>
<Group>
<SVGPath fx:id="iconMotelFax" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miFax" promptText="Fax no."/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelEmail" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miEmail" promptText="Email id"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelNoOfRooms" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miNOR" promptText="No. of Rooms"/>
</Group>
</HBox>
</HBox>
<HBox style="-fx-spacing: 50px"> <!--Fifth Row-->
<HBox>
<Group>
<SVGPath fx:id="iconMotelCheckInTime" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<Spinner fx:id="hourSpinnerCIT"/>
</Group>
<Group>
<Spinner fx:id="minSpinnerCIT"/>
</Group>
<Group>
<Spinner fx:id="secSpinnerCIT"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelCheckOutTime" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<Spinner fx:id="hourSpinnerCOT"/>
</Group>
<Group>
<Spinner fx:id="minSpinnerCOT"/>
</Group>
<Group>
<Spinner fx:id="secSpinnerCOT"/>
</Group>
</HBox>
</HBox>
</VBox>
<VBox fx:id="roomOccupancy">
<HBox> <!--First Row-->
<Label fx:id="labelMaxGuestSelection"
text="Maximum Allowed Guest in individual Rooms. Please state the max count in adjacent cells"/>
<VBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelSingleBed" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miSB" promptText="Single Bed"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelTwoBeds" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miTB" promptText="Two Beds"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelfamilyRoom" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miFR" promptText="Family Room"/>
</Group>
</HBox>
</VBox>
</HBox>
<HBox> <!--Second Row-->
<Label fx:id="labelMaxGuestCharged"
text="Rate Charged per Guest after max. allowed guest exceeds in a room"/>
<HBox>
<Group>
<SVGPath fx:id="iconMotelGuestExceedRate" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miGER" promptText="Rate/Guest Exceeded"/>
</Group>
</HBox>
</HBox>
</VBox>
<VBox fx:id="submissionWrapper">
<Group>
<Button fx:id="submit" text="UPDATE" onAction="#saveData"/>
</Group>
</VBox>
</VBox>
</FlowPane>
FXML 控制器
@Override
public void initialize(URL location, ResourceBundle resources) {
//Load the data if the motelInfo file exist
File checkTemp = new File("motelDetails.ser");
if(checkTemp.exists()){
readDataFromFile();
}
//Getting Values for the spinner
for(int i=0;i<24;i++){
hourList.add(String.format("%02d", i));
}
final SpinnerValueFactory<String> hourValuesCIT = new SpinnerValueFactory.ListSpinnerValueFactory<>(hourList);
final SpinnerValueFactory<String> hourValuesCOT = new SpinnerValueFactory.ListSpinnerValueFactory<>(hourList);
for(int i=0; i<60; i++){
minList.add(String.format("%02d", i));
secList.add(String.format("%02d", i));
}
final SpinnerValueFactory<String> minValuesCIT = new SpinnerValueFactory.ListSpinnerValueFactory<>(minList);
final SpinnerValueFactory<String> minValuesCOT = new SpinnerValueFactory.ListSpinnerValueFactory<>(minList);
final SpinnerValueFactory<String> secValuesCIT = new SpinnerValueFactory.ListSpinnerValueFactory<>(secList);
final SpinnerValueFactory<String> secValuesCOT = new SpinnerValueFactory.ListSpinnerValueFactory<>(secList);
styleSpinner(hourSpinnerCIT, hourValuesCIT);
styleSpinner(hourSpinnerCOT, hourValuesCOT);
styleSpinner(minSpinnerCIT, minValuesCIT);
styleSpinner(minSpinnerCOT, minValuesCOT);
styleSpinner(secSpinnerCIT, secValuesCIT);
styleSpinner(secSpinnerCOT, secValuesCOT);
}
看来你有两个问题:
- 运行 在阻塞更新的 UI 线程上进行长时间操作 (
readDataFromFile()
)
- 在 FXMLoader.load() 中完成所有操作,在完成之前不允许绘制任何东西
我建议通过 运行 在不同的线程中加载并在加载时显示加载屏幕来解决这两个问题。请参阅下一个示例:
public class Main extends Application {
@Override
public void start(Stage stage) throws Exception {
StackPane loadingRoot = new StackPane();
loadingRoot.getChildren().setAll(new ProgressIndicator());
final Scene scene = new Scene(loadingRoot);
stage.setWidth(400);
stage.setHeight(400);
stage.setScene(scene);
stage.show();
new Thread(() -> {
veryLongOperation();
// we need to wrap UI code in Platform
Platform.runLater(() -> {
Parent root;
try {
root = FXMLLoader.load(Main.class.getResource("FXMLDocument.fxml"));
scene.setRoot(root);
} catch (IOException ex) {
}
});
}).start();
}
public static void veryLongOperation() {
try {
// long-long operation
Thread.sleep(5000);
} catch (Exception ex) {
}
}
}
我有一个带有切换按钮的主文件,单击其中一个按钮后,我会启动一个新的 FXMLLoader
,其中包含大约 10 个 SVG、15 个文本字段和 6 个微调器。此外,它有 CSS 相应地渲染节点...它加载正常,没有错误或任何问题,但在显示场景之前需要一两秒。
我猜这是由于同时初始化的节点数量所致。有没有办法在节点开始初始化之前显示场景?
注意:我的项目需要我在点击时导航到不同的场景。
ToggleButton 从它被选中的地方
if(settings.isSelected()){
Stage stage = (Stage) mainWrapper.getScene().getWindow();
Parent root = FXMLLoader.load(getClass().getResource("/fxmlFiles/settings.fxml"));
stage.setScene(new Scene(root, Screen.getPrimary().getVisualBounds().getWidth(),
Screen.getPrimary().getVisualBounds().getHeight()));
stage.centerOnScreen();
//stage.setMaximized(true);
stage.show();
}
fxml文件
<FlowPane xmlns:fx="http://javafx.com/fxml"
fx:controller="controllers.motelInfoController"
stylesheets="/cssFiles/motelInfo.css"
fx:id="content">
<VBox fx:id="mainWrapper">
<VBox fx:id="validationWrapper">
<Label fx:id="validationLabel" visible="false"/>
</VBox>
<VBox fx:id="generalInfo">
<HBox> <!--First Row-->
<HBox>
<Group>
<SVGPath fx:id="iconMotelName" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miName" promptText="Motel Name"/>
</Group>
</HBox>
<HBox>
<Group>
<TextField fx:id="miFranchiseName" promptText="Franchise Name"/>
</Group>
</HBox>
</HBox>
<HBox> <!--Second Row-->
<HBox>
<Group>
<SVGPath fx:id="iconMotelAddress" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miAddress" promptText="Street Name"/>
</Group>
</HBox>
<HBox>
<Group>
<TextField fx:id="miCity" promptText="City/Town Name"/>
</Group>
</HBox>
</HBox>
<HBox><!--Third Row-->
<HBox style="-fx-spacing: 25px;">
<Label></Label> <!--Empty label for purpose of leaving icon gap since N/A here-->
<Group>
<TextField fx:id="miState" promptText="State"/>
</Group>
</HBox>
<HBox>
<Group>
<TextField fx:id="miZipCode" promptText="Zip Code"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelContact" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miContact" promptText="Contact no."/>
</Group>
</HBox>
</HBox>
<HBox> <!--Fourth Row-->
<HBox>
<Group>
<SVGPath fx:id="iconMotelFax" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miFax" promptText="Fax no."/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelEmail" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miEmail" promptText="Email id"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelNoOfRooms" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miNOR" promptText="No. of Rooms"/>
</Group>
</HBox>
</HBox>
<HBox style="-fx-spacing: 50px"> <!--Fifth Row-->
<HBox>
<Group>
<SVGPath fx:id="iconMotelCheckInTime" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<Spinner fx:id="hourSpinnerCIT"/>
</Group>
<Group>
<Spinner fx:id="minSpinnerCIT"/>
</Group>
<Group>
<Spinner fx:id="secSpinnerCIT"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelCheckOutTime" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<Spinner fx:id="hourSpinnerCOT"/>
</Group>
<Group>
<Spinner fx:id="minSpinnerCOT"/>
</Group>
<Group>
<Spinner fx:id="secSpinnerCOT"/>
</Group>
</HBox>
</HBox>
</VBox>
<VBox fx:id="roomOccupancy">
<HBox> <!--First Row-->
<Label fx:id="labelMaxGuestSelection"
text="Maximum Allowed Guest in individual Rooms. Please state the max count in adjacent cells"/>
<VBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelSingleBed" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miSB" promptText="Single Bed"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelTwoBeds" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miTB" promptText="Two Beds"/>
</Group>
</HBox>
<HBox>
<Group>
<SVGPath fx:id="iconMotelfamilyRoom" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miFR" promptText="Family Room"/>
</Group>
</HBox>
</VBox>
</HBox>
<HBox> <!--Second Row-->
<Label fx:id="labelMaxGuestCharged"
text="Rate Charged per Guest after max. allowed guest exceeds in a room"/>
<HBox>
<Group>
<SVGPath fx:id="iconMotelGuestExceedRate" scaleX="0.05" scaleY="0.05" fill="white"/>
</Group>
<Group>
<TextField fx:id="miGER" promptText="Rate/Guest Exceeded"/>
</Group>
</HBox>
</HBox>
</VBox>
<VBox fx:id="submissionWrapper">
<Group>
<Button fx:id="submit" text="UPDATE" onAction="#saveData"/>
</Group>
</VBox>
</VBox>
</FlowPane>
FXML 控制器
@Override
public void initialize(URL location, ResourceBundle resources) {
//Load the data if the motelInfo file exist
File checkTemp = new File("motelDetails.ser");
if(checkTemp.exists()){
readDataFromFile();
}
//Getting Values for the spinner
for(int i=0;i<24;i++){
hourList.add(String.format("%02d", i));
}
final SpinnerValueFactory<String> hourValuesCIT = new SpinnerValueFactory.ListSpinnerValueFactory<>(hourList);
final SpinnerValueFactory<String> hourValuesCOT = new SpinnerValueFactory.ListSpinnerValueFactory<>(hourList);
for(int i=0; i<60; i++){
minList.add(String.format("%02d", i));
secList.add(String.format("%02d", i));
}
final SpinnerValueFactory<String> minValuesCIT = new SpinnerValueFactory.ListSpinnerValueFactory<>(minList);
final SpinnerValueFactory<String> minValuesCOT = new SpinnerValueFactory.ListSpinnerValueFactory<>(minList);
final SpinnerValueFactory<String> secValuesCIT = new SpinnerValueFactory.ListSpinnerValueFactory<>(secList);
final SpinnerValueFactory<String> secValuesCOT = new SpinnerValueFactory.ListSpinnerValueFactory<>(secList);
styleSpinner(hourSpinnerCIT, hourValuesCIT);
styleSpinner(hourSpinnerCOT, hourValuesCOT);
styleSpinner(minSpinnerCIT, minValuesCIT);
styleSpinner(minSpinnerCOT, minValuesCOT);
styleSpinner(secSpinnerCIT, secValuesCIT);
styleSpinner(secSpinnerCOT, secValuesCOT);
}
看来你有两个问题:
- 运行 在阻塞更新的 UI 线程上进行长时间操作 (
readDataFromFile()
) - 在 FXMLoader.load() 中完成所有操作,在完成之前不允许绘制任何东西
我建议通过 运行 在不同的线程中加载并在加载时显示加载屏幕来解决这两个问题。请参阅下一个示例:
public class Main extends Application {
@Override
public void start(Stage stage) throws Exception {
StackPane loadingRoot = new StackPane();
loadingRoot.getChildren().setAll(new ProgressIndicator());
final Scene scene = new Scene(loadingRoot);
stage.setWidth(400);
stage.setHeight(400);
stage.setScene(scene);
stage.show();
new Thread(() -> {
veryLongOperation();
// we need to wrap UI code in Platform
Platform.runLater(() -> {
Parent root;
try {
root = FXMLLoader.load(Main.class.getResource("FXMLDocument.fxml"));
scene.setRoot(root);
} catch (IOException ex) {
}
});
}).start();
}
public static void veryLongOperation() {
try {
// long-long operation
Thread.sleep(5000);
} catch (Exception ex) {
}
}
}