Многопоточность из PreLoader - JavaFX - PullRequest
1 голос
/ 27 января 2020

Я разрабатываю приложение JavaFX, для которого требуются ресурсы, загружаемые из файла, прежде чем можно будет запустить основную стадию приложения. Мое решение для выполнения sh этой задачи состоит в том, чтобы использовать PreLoader, чтобы пользователь не мог взаимодействовать с приложением, пока ресурсы не были загружены (довольно стандартные вещи).

У меня есть класс, расширяющий PreLoader класс, который создает объект, который загружает файл .f xml, который отображает сцена 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 () будет работать одновременно с preloader start ().

Нужно ли использовать метод init ()?

1 Ответ

2 голосов
/ 28 января 2020

Вот пример, который, надеюсь, будет полезен. Вы можете использовать Task для загрузки всех данных. Когда Task закончен, используйте идеи Применение MVC с JavaFX , чтобы передать Model любому Controller, который в этом нуждается.

Main

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();
    }

}

PrimaryController / SplashScreen

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;
    }

}

PrimaryF XML

<?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());
    }
}

SecondaryF XML

<?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.";  
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...