2016-01-19 5 views
0

У моего исходного файла fxml (скажем home.fxml) есть много функциональных возможностей, поэтому для полной загрузки требуется много времени. Поэтому, чтобы избежать временного разрыва между запуском программы и загрузкой fxml, я ввел еще один fxml (скажем loader.fxml) с изображением gif, которое должно появиться во время загрузки основного файла fxml. Проблема заключается в том, что изображение gif в моем loader.fxml не перемещается, так как в программе висит до тех пор, пока home.fxml не будет загружен полностью. Чтобы избежать этого, я переместил home.fxml загрузку в поток, как показано ниже.Загрузить fxml в качестве фонового процесса - Javafx

public class UATReportGeneration extends Application { 

    private static Stage mainStage; 

    @Override 
    public void start(Stage stage) { 
     Parent loaderRoot = null; 
     try { 
      loaderRoot = FXMLLoader.load(getClass().getResource("/uatreportgeneration/fxml/Loader.fxml")); 
     } catch (IOException ex) { 
      Logger.getLogger(UATReportGeneration.class.getName()).log(Level.SEVERE, null, ex); 
     } 
     Scene loadScene = new Scene(loaderRoot); 
     stage.setScene(loadScene); 
     stage.initStyle(StageStyle.UNDECORATED); 
     stage.getIcons().add(new Image(this.getClass().getResourceAsStream("/uatreportgeneration/Images/logo.png"))); 

     stage.show(); 


     mainStage = new Stage(StageStyle.UNDECORATED); 
     mainStage.setTitle("Upgrade Analysis"); 
     mainStage.getIcons().add(new Image(this.getClass().getResourceAsStream("/uatreportgeneration/Images/logo.png"))); 
     setStage(mainStage); 

     new Thread(() -> { 
      Platform.runLater(() -> { 
       try { 
        FXMLLoader loader = new FXMLLoader(); 
        Parent root = loader.load(getClass().getResource("/uatreportgeneration/fxml/Home.fxml")); 

        Scene scene = new Scene(root); 
        mainStage.setScene(scene); 
        mainStage.show(); 
        stage.hide(); 
        System.out.println("Stage showing"); 
        // Get current screen of the stage 
        ObservableList<Screen> screens = Screen.getScreensForRectangle(new Rectangle2D(mainStage.getX(), mainStage.getY(), mainStage.getWidth(), mainStage.getHeight())); 
        // Change stage properties 
        Rectangle2D bounds = screens.get(0).getVisualBounds(); 
        mainStage.setX(bounds.getMinX()); 
        mainStage.setY(bounds.getMinY()); 
        mainStage.setWidth(bounds.getWidth()); 
        mainStage.setHeight(bounds.getHeight()); 
        System.out.println("thread complete"); 
       } catch (IOException ex) { 
        Logger.getLogger(UATReportGeneration.class.getName()).log(Level.SEVERE, null, ex); 
       } 

      }); 
     }).start(); 

    } 

    public static Stage getStage() { 
     return mainStage; 
    } 

    public static void setStage(Stage stage) { 
     mainStage = stage; 
    } 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 

     launch(args); 
    } 

} 

Но после этого кода моя программа висит (изображение GIF не перемещается). Если я загружаю fxml за пределами Platform.runLater(), я получаю исключение Not on FX Thread.

Я также устал, используя Task(), но из-за этого изображение gif перемещается, но fxml не загружается в фоновом режиме, если я пытаюсь загрузить fxml за пределами Platform.runLater().

Может кто-нибудь, пожалуйста, помогите мне и скажите, как я могу исправить код, чтобы мой fxml загружался в фоновом режиме, не нарушая процесс переднего плана.

ответ

4

Используйте Task. Вам необходимо организовать создание сцены и обновить сцену в потоке приложения FX. Чистый способ заключается в использовании Task<Parent>:

Task<Parent> loadTask = new Task<Parent>() { 
    @Override 
    public Parent call() throws IOException { 
     FXMLLoader loader = new FXMLLoader(); 
     Parent root = loader.load(getClass().getResource("/uatreportgeneration/fxml/Home.fxml")); 
     return root ; 
    } 
}; 

loadTask.setOnSucceeded(e -> { 
    Scene scene = new Scene(loadTask.getValue()); 

    mainStage.setScene(scene); 
    mainStage.show(); 
    stage.hide(); 
    System.out.println("Stage showing"); 
    // Get current screen of the stage 
    ObservableList<Screen> screens = Screen.getScreensForRectangle(new Rectangle2D(mainStage.getX(), mainStage.getY(), mainStage.getWidth(), mainStage.getHeight())); 
    // Change stage properties 
    Rectangle2D bounds = screens.get(0).getVisualBounds(); 
    mainStage.setX(bounds.getMinX()); 
    mainStage.setY(bounds.getMinY()); 
    mainStage.setWidth(bounds.getWidth()); 
    mainStage.setHeight(bounds.getHeight()); 
    System.out.println("thread complete"); 
}); 

loadTask.setOnFailed(e -> loadTask.getException().printStackTrace()); 

Thread thread = new Thread(loadTask); 
thread.start(); 

Вот SSCCE с помощью этой техники:

main.fxml:

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.layout.VBox?> 
<?import javafx.geometry.Insets?> 
<?import javafx.scene.control.Button?> 
<?import javafx.scene.control.TextField?> 

<VBox spacing="10" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController"> 
    <padding> 
     <Insets top="24" left="24" right="24" bottom="24"/> 
    </padding> 
    <TextField /> 
    <Button fx:id="button" text="Show Window" onAction="#showWindow"/> 
</VBox> 

MainController (использует Task подход, показанный выше):

import java.io.IOException; 

import javafx.concurrent.Task; 
import javafx.fxml.FXML; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.stage.Stage; 

public class MainController { 
    @FXML 
    private Button button ; 
    @FXML 
    private void showWindow() { 
     Task<Parent> loadTask = new Task<Parent>() { 
      @Override 
      public Parent call() throws IOException, InterruptedException { 

       // simulate long-loading process: 
       Thread.sleep(5000); 

       FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml")); 
       Parent root = loader.load(); 
       return root ; 
      } 
     }; 

     loadTask.setOnSucceeded(e -> { 
      Scene scene = new Scene(loadTask.getValue()); 
      Stage stage = new Stage(); 
      stage.initOwner(button.getScene().getWindow()); 
      stage.setScene(scene); 
      stage.show(); 
     }); 

     loadTask.setOnFailed(e -> loadTask.getException().printStackTrace()); 

     Thread thread = new Thread(loadTask); 
     thread.start(); 
    } 
} 

test.fxml:

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.layout.BorderPane?> 
<?import javafx.scene.control.Label?> 
<?import javafx.scene.control.Button?> 
<?import javafx.geometry.Insets?> 

<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="TestController"> 
    <padding> 
     <Insets top="24" left="24" right="24" bottom="24"/> 
    </padding> 
    <center> 
     <Label fx:id="label" text="This is a new window"/> 
    </center> 
    <bottom> 
     <Button text="OK" onAction="#closeWindow" BorderPane.alignment="CENTER"> 
      <BorderPane.margin> 
       <Insets top="5" bottom="5" left="5" right="5"/> 
      </BorderPane.margin> 
     </Button> 
    </bottom> 
</BorderPane> 

TestController:

import javafx.fxml.FXML; 
import javafx.scene.control.Label; 

public class TestController { 
    @FXML 
    private Label label ; 
    @FXML 
    private void closeWindow() { 
     label.getScene().getWindow().hide(); 
    } 
} 

Основное применение:

import java.io.IOException; 

import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 

public class Main extends Application { 

    @Override 
    public void start(Stage primaryStage) throws IOException { 
     primaryStage.setScene(new Scene(FXMLLoader.load(getClass().getResource("main.fxml")))); 
     primaryStage.show(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

Обратите внимание, что после нажатия на кнопку, вы можете ввести в текстовое поле во время пять секунд, чтобы «загрузить» FXML, поэтому пользовательский интерфейс остается отзывчивым.

+0

Я уже пробовал 'Задача '. Это дало мне исключение «IllegalStateException: Not on FX application thread». Не могли бы вы рассказать мне, как я могу выполнить его в потоке приложения FX? Использование 'Platform.runLater' висит первым fxml. :( –

+0

Убедитесь, что вы не создаете сцену или сцену в фоновом потоке. Они должны быть созданы в приложении приложения FX, как здесь, в примере кода. –

+0

Я пробовал код, упомянутый выше. одно и то же исключение. –

0

Ваш подход с использованием Task был правильным. вы просто не хватает немного больше: вы просто не хватает еще Platform#invokeLater() для обновления пользовательского интерфейса:

new Thread(new Task() { 

     @Override 
     protected Object call() throws Exception { 
      // Simulating long loading 
      Thread.sleep(5000); 

      FXMLLoader loader = new FXMLLoader(getClass().getResource("home.fxml")); 
      Parent root = loader.load(); 

      Scene scene = new Scene(root); 

      // Updating the UI requires another Platform.runLater() 
      Platform.runLater(new Runnable() { 

       @Override 
       public void run() { 
        mainStage.setScene(scene); 
        mainStage.show(); 
        stage.hide(); 
        System.out.println("Stage showing"); 
        // Get current screen of the stage 
        ObservableList<Screen> screens = Screen.getScreensForRectangle(new Rectangle2D(mainStage.getX(), mainStage.getY(), mainStage.getWidth(), mainStage.getHeight())); 
        // Change stage properties 
        Rectangle2D bounds = screens.get(0).getVisualBounds(); 
        mainStage.setX(bounds.getMinX()); 
        mainStage.setY(bounds.getMinY()); 
        mainStage.setWidth(bounds.getWidth()); 
        mainStage.setHeight(bounds.getHeight()); 
        System.out.println("thread complete"); 
       } 
      }); 
      return null; 
     } 
    }).start(); 
+0

Я пробовал свой код. Он даже не входит в контроллер 'home.fxml'. Gif движется как ожидалось, но home.fxml не загружается. –

+0

не предполагалось добавить вслепую. хотя это был мой рабочий пример: http://pastebin.com/RThnUe7i – KnusperPudding

+0

Вы раздавите все исключения. Добавьте обработчик 'setOnFailed' к задаче и посмотрите, что он что-то бросает. Я думаю, вы получаете 'IllegalStateException', создавая' Scene' в фоновом потоке. –

0

В дополнение к @James_D ответа. Как уже упоминалось, Stages и Scene не должны добавляться в фоновый поток, они должны добавляться только в основной поток FX.

Я добавил tooltips в мой home.fxml, что является ничем иным, как PopupWindow. Поэтому к фоновому потоку он появился как новый этап. Поэтому он бросил IllegalStateException.После удаления tooltips из файла fxml fxml смог загрузить фоновый процесс, поскольку в этом потоке не было никаких этапов.

Смежные вопросы