Search code examples
javajavafxscene

Problem creating a scene in a while loop in Java


what I want to do is : When the program starts, user is required to input a number. When the number is 1, it will enter a loop of rectangle graphic. A graphic of rectangle stacks() will appear in screen. Then, the user needs to close the graphic(scene) and the program asks again for an input from user. If user answers 1, the rectangle graphic will appear again and so on until user gives answer other than 1. My problem is that the rectangle graphic will not appear when I insert input '1' and the window showing rectangle graphic is not responding. Also, when I give answer other than 1 at the first of the program, I thought the program would end with "Process finished with exit code 0", but it didn't. I could only force stop it with the 'stop' button. Can anyone help me? Thank you.

  package sample;

import java.io.*;
import java.util.*;
import javafx.application.Application;
import javafx.scene.layout.AnchorPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.scene.Scene;

import static javafx.scene.paint.Color.BLUE;
import static javafx.scene.paint.Color.RED;

public class Main extends Application{
    @Override
    public void start (Stage primaryStage) throws Exception
    {
        System.out.println("Enter a number: ");
        Scanner scanner = new Scanner(System. in);
        int xx = scanner.nextInt();
        while(xx==1)
        {
            Rectangle rectangle;
            ArrayList<Boolean> bool = new ArrayList<Boolean>();
            AnchorPane root = new AnchorPane();
            for(int i=0; i<10; i++)
            {
                if(i%2 ==0)
                    bool.add(true);
                else
                    bool.add(false);
            }
            //Coordinate variables
            int y=50;
            //Rectangle loop
            for(int i=0; i<10; i++)
            {
                rectangle = new Rectangle();
                if(bool.get(i))
                {
                    rectangle.setX(50);
                    rectangle.setY(y);
                    rectangle.setWidth(150.0);
                    rectangle.setHeight(50.0);
                    rectangle.setFill(RED);
                }
                else
                {
                    rectangle.setX(50);
                    rectangle.setY(y);
                    rectangle.setWidth(150.0);
                    rectangle.setHeight(50.0);
                    rectangle.setFill(BLUE);
                }
                y+=50;
                root.getChildren().add(rectangle);
            }
            primaryStage.setTitle("Rectangle");
            Scene scene = new Scene(root ,600, 700);
            primaryStage.setScene(scene);
            primaryStage.show();
            primaryStage.setResizable(false);
            xx=scanner.nextInt();
        }
    }

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

Solution

  • There are 2 issues here:

    1. You must not block the JavaFX application thread. Methods that are executed on that thread (like Application.start) need to return in a short time for the GUI to stay responsive. You need to use a seperate thread for the loop, if you want to keep using System.in (which I do not recommend).
    2. Closing the last stage will automatically shut down JavaFX by default. This can be fixed using Platform.setImplicitExit()
    @Override
    public void start(Stage primaryStage) throws Exception {
        // don't automatically shutdown toolkit on window close
        Platform.setImplicitExit(false);
    
        primaryStage.setTitle("Rectangle");
        AnchorPane root = new AnchorPane();
        Scene scene = new Scene(root, 600, 700);
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
    
        // semaphore for synchronizing the closing 
        final Semaphore semaphore = new Semaphore(1);
    
        // notify loop of window being closed
        primaryStage.setOnHidden(evt -> semaphore.release());
    
        final Scanner scanner = new Scanner(System.in);
        System.out.println("Enter a number: ");
    
        Runnable runnable = () -> {
    
            while (true) {
                // wait for window to be closed
                try {
                    semaphore.acquire();
                } catch (InterruptedException ex) {
                    return;
                }
    
                // read number
                int xx = scanner.nextInt();
    
                if (xx == 1) {
                    ArrayList<Boolean> bool = new ArrayList<>();
                    for (int i = 0; i < 10; i++) {
                        if (i % 2 == 0) {
                            bool.add(true);
                        } else {
                            bool.add(false);
                        }
                    }
                    //Coordinate variables
                    int y = 50;
    
                    Node[] children = new Node[bool.size()];
    
                    //Rectangle loop
                    for (int i = 0; i < children.length; i++) {
                        Rectangle rectangle = new Rectangle(50, y, 150.0, 50.0);
                        rectangle.setFill(bool.get(i) ? RED : BLUE);
                        y += 50;
                        children[i] = rectangle;
                    }
    
                    // update gui
                    Platform.runLater(() -> {
                        root.getChildren().setAll(children);
                        primaryStage.show();
                    });
                } else {
                    // shutdown javafx
                    Platform.exit();
                    break;
                }
            }
        };
        new Thread(runnable).start();
    }