Javafx Threads Update UI and load System in the background

Asked

Viewed 1,251 times

4

I have a Stage Main that is my Login:

SS

In the FXML of that Stage I have a Region and a ProgressIndicator with the property setVisible(false)

After successfully logging into the application the intention would be to display this Region and the ProgressIndicator as a thread started the application, but what happens is that after logging in the thread starts running by loading the application, but the Region and the ProgressIndicator, are not visible.

My thread

 Task t = new Task() {
    
    @Override
    protected Object call() throws Exception {
        
        Platform.runLater(() -> {
            try {
                
                new SisgeFX().start();
            } catch (IOException ex) {
                Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, ex);
            }
        });
        return null;
    }
};
region.visibleProperty().bind(t.runningProperty());
pi.visibleProperty().bind(t.runningProperty());
Thread th = new Thread(t);
th.start();  

I tried to make two threads: one to load the system and one to update the ProgressIndicator while the system is not loaded, but unsuccessfully, does not generate Exception, I’ve tried several ways.

What I realized in my various attempts:

  1. One Thread is started and the other is not (Starts only the Thread that loads the system).

  2. To Thread does not start.

  3. To Thread initiates but the ProgressIndicator gets stuck, doesn’t get excited.

Log-in snippet:

@FXML
private void sysLogin() {
    String user = ctfUserLogin.getText();
    String pass = ctfPassLogin.getText();
    LoginDAO loginDAO = DAOFactory.make(LoginDAO.class);
    Login login = loginDAO.getLogin(user, pass);
    
    if (login != null) {
        runThread(); // aqui chamo a Thread postado acima.
        ctfPassLogin.setStyle(null);
        ctfUserLogin.setStyle(null);
    } else {
        ctfPassLogin.clear();
        ctfUserLogin.clear();
        ctfPassLogin.setStyle("-fx-border-color:red;");
        ctfUserLogin.setStyle("-fx-border-color:red;");
        //new ShakeTransition(vBox).play();
        new WobbleTransition(vBox).play();
        //new TadaTransition(vBox).play();
    }
}

After login successfully how to display the ProgressIndicartor while the system is loaded in the background?

2 answers

1

An elegant way to solve your problem is to create a Preloader, considered a good programming practice in Javafx. Preloader is especially useful for improving the user experience when it needs to wait for heavy operations to be completed before the application starts. There is even a way to make a Preloader Login, explained with details in this tutorial from Oracle - Section 9.3.5.

However, in the examples cited, loading is done before or during the login and not after. (Maybe there is a workaround to achieve this effect)

(Netbeans) Creating a Prealoder:

  1. Create a Preloader in New Project > Javafx > Javafx Preloader;
  2. Right-click on the project go to Properties (Properties) > Run > Mark "Use Preloader" > Click "Browse" > "Choose Preloader from Project" > Select your Preloader folder;
  3. Create the public void init() method in your main application.

Within the init() method you can all the essential loading for your application by scoring progress through the method notifyPreloader(new ProgressNotification(0.10)) // Para 10%;

Here’s an example preloader with a simple progress bar:

public class AppPreloader extends Preloader{

private Stage stage;
private ProgressBar bar;
private boolean noLoadingProgress = true;

private Scene createPreloaderScene() {
    bar = new ProgressBar();

    VBox vb = new VBox();
    vb.setAlignment(Pos.CENTER);
    bar.setPrefWidth(150);
    vb.getChildren().addAll(bar);

    return new Scene(vb, 300, 150);        
}

@Override
public void start(Stage stage){  
    this.stage = stage;
    stage.setScene(createPreloaderScene());        
    stage.show();
}

@Override
public void handleStateChangeNotification(StateChangeNotification scn) {

}

@Override
public void handleProgressNotification(ProgressNotification pn) {
    //application loading progress is rescaled to be first 50%
    //Even if there is nothing to load 0% and 100% events can be
    // delivered
    if (pn.getProgress() != 1.0 || !noLoadingProgress) {
      bar.setProgress(pn.getProgress()/2);
      if (pn.getProgress() > 0) {
          noLoadingProgress = false;
      }
    }
}

@Override
public void handleApplicationNotification(PreloaderNotification pn) {
    if (pn instanceof ProgressNotification) {
       //expect application to send us progress notifications 
       //with progress ranging from 0 to 1.0
       double v = ((ProgressNotification) pn).getProgress();
       if (!noLoadingProgress) {
           //if we were receiving loading progress notifications 
           //then progress is already at 50%. 
           //Rescale application progress to start from 50%               
           v = 0.5 + v/2;
       }
       bar.setProgress(v);            
    } else if (pn instanceof StateChangeNotification) {
        //hide after get any state update from application
        stage.hide();
    }
}
}

0

I didn’t see that in your code:

progressBar.progressProperty().bind(task.progressProperty());

This line that will make the progressibar rotate.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.