JavaFX more Scenes - user-interface

Hi Guys i build a GUI and on this GUI is a Button and when I press the Button a second GUI appears, on the second GUI is also a Button and when i press the Button it goes back
GU1
btn.setOnAction(new EventHandler <ActionEvent>(){
public void handle(ActionEvent arg0) {
try {
new GUI2().start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
}
});
My Questions!
Is GUI1 still running when i press the Button?
GUI2
btn.setOnAction(new EventHandler <ActionEvent>(){
public void handle(ActionEvent arg0) {
try {
//back to the main menu
new GUI1().start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
}
});
When i press the Button, does it go back to the same instance when beginning the program? Or make it a new Instance witch has the same look, and use it more RAM;
How should it works, when i want to open the second GUI in a external Window

When i press the Button, does it go back to the same instance when beginning the program?
No, a new instance is created based on your code new GUI2().start(primaryStage);. Always remember that thenew keyword ALWAYS creates a new object.
How should it works, when i want to open the second GUI in a external Window?
There are lots of ways to do this.
Method 1
If it happen that you created two applications, both extending the Application class, this method should work.
public class MultiWindowFX {
private static final Logger logger = Logger.getGlobal();
public static class GUI1 extends Application {
private final Button buttonShowGUI2;
private final GUI2 gui2;
public GUI1() {
buttonShowGUI2 = new Button("Show GUI 2");
gui2 = new GUI2();
}
public Button getButtonShowGUI2() {
return buttonShowGUI2;
}
#Override
public void start(Stage primaryStage) throws Exception {
//add an action event on GUI2's buttonShowGUI1 to send front GUI1
gui2.getButtonShowGUI1().setOnAction(gui2ButtonEvent -> {
if (primaryStage.isShowing()) primaryStage.toFront();
else primaryStage.show();
});
//button with action to show GUI 2
buttonShowGUI2.setOnAction(actionEvent -> {
try {
if (gui2.getPrimaryStage() == null) gui2.start(new Stage());
else gui2.getPrimaryStage().toFront();
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
}
});
//set scene and its root
Pane root = new StackPane(buttonShowGUI2);
Scene stageScene = new Scene(root, 400, 250);
//set stage
primaryStage.setScene(stageScene);
primaryStage.centerOnScreen();
primaryStage.setTitle("GUI 1");
primaryStage.show();
}
public static void launchApp(String... args) {
GUI1.launch(args);
}
}
public static class GUI2 extends Application {
private Stage primaryStage;
private final Button buttonShowGUI1;
public GUI2() {
buttonShowGUI1 = new Button("Show GUI 1");
}
public Button getButtonShowGUI1() {
return buttonShowGUI1;
}
public Stage getPrimaryStage() {
return primaryStage;
}
#Override
public void start(Stage primaryStage) throws Exception {
//get stage reference
this.primaryStage = primaryStage;
//set scene and its root
Pane root = new StackPane(buttonShowGUI1);
Scene stageScene = new Scene(root, 400, 250);
//set stage
primaryStage.setScene(stageScene);
primaryStage.centerOnScreen();
primaryStage.setTitle("GUI 2");
primaryStage.show();
}
public static void launchApp(String... args) {
GUI2.launch(args);
}
}
public static void main(String... args) {
GUI1.launchApp(args);
}
}
Method 2
For me, this is the best approach especially if you want window ownership and modality works.
public class GUI1 extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Show GUI2");
btn.setOnAction(actionEvent -> {
//prepare gui2
Stage gui2Stage = createGUI2();
//set window modality and ownership
gui2Stage.initModality(Modality.APPLICATION_MODAL);
gui2Stage.initOwner(primaryStage);
//show
gui2Stage.show();
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 400, 250);
primaryStage.setTitle("GUI 1");
primaryStage.setScene(scene);
primaryStage.show();
}
private Stage createGUI2() {
Button btn = new Button();
btn.setText("Show GUI1");
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 150);
Stage gui2Stage = new Stage();
gui2Stage.setTitle("GUI 2");
gui2Stage.setScene(scene);
//add an action event to GUI2's button, which hides GUI2 and refocuses to GUI1
btn.setOnAction(actionEvent -> gui2Stage.hide());
return gui2Stage;
}
public static void main(String[] args) {
launch(args);
}
}
...and among other methods. Choose the approach that fits to your requirements.

Related

javafx User interface not updating?

I'm new to JavaFX.
I'm building a GUI for a project of mine, and I have a problem with it -
it seems that once I show my UI it stops updating.
In my case, it supposed to update a fill colour for a circle but it does not.
I tried to move the line:
someCircleId.setFill(color));
to be above the line:
Main.stage.show();
in the initialize function in my code and it did change the color of
the certain circle.
public class UIController implements Initializable {
public UIController() {
fillMap();
}
#Override
public void initialize(URL location, ResourceBundle resources) {
Main.stage.show();
}
#FXML
public void pressExitButton() {
Main.dropDBSchema();
System.exit(0);
}
public void changeCircleColor(String circleKey, Color color) {
Platform.runLater(
() -> {
Circle circle = this.circles.get(circleKey);
PauseTransition delay = new PauseTransition(Duration.seconds(10));
delay.setOnFinished(event -> circle.setFill(color));
delay.play();
}
);
}
}
And this is the class that uses these functions:
public class MonitoringLogicImpl implements MonitoringLogic {
public void updateUI(UUID flowUUID, String sender, String receiver, DATA_STATUS status){
String key = flowUUID.toString()+"_"+sender+"_"+receiver;
Color color = this.uiController.getColorAccordingToStatus(status);
this.uiController.changeCircleColor(key, color);
}
}
This is the FXML initialization :
public void start(Stage stage){
this.stage = stage;
FXMLLoader loader = new FXMLLoader();
try {
loader.setLocation(getClass().getResource("/UserInterface.fxml"));
this.root = loader.load();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
stage.setTitle("Troubleshooting project");
stage.setScene(new Scene(root, 900, 700));
stage.show();
}
I created my UI using Scene-Builder tool.

Detect if object is over pane during transition

I'm trying to detect if an elements that I move using PathTransition are entering the space of Pane.
This is my code:
public class Demo extends Application {
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, 800, 600));
Rectangle rect = new Rectangle(5, 5, Color.BLACK);
root.getChildren().add(rect);
Path path = new Path();
path.getElements().add (new MoveTo (0, 350));
path.getElements().add (new LineTo(400, 350));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(5000));
pathTransition.setPath(path);
pathTransition.setNode(rect);
pathTransition.setAutoReverse(false);
AnchorPane pane = new AnchorPane();
pane.setPrefSize(250, 200);
pane.relocate(300, 300);
pane.setStyle("-fx-border-color: #000000;");
root.getChildren().add(pane);
pathTransition.play();
}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Is there a way to bind an event to the pane object and when the rect goes over, it will detect it?
Try
BooleanBinding intersects = new BooleanBinding() {
{
this.bind(pane.boundsInParentProperty(), rect.boundsInParentProperty());
}
#Override
protected boolean computeValue() {
return pane.getBoundsInParent().intersects(rect.getBoundsInParent());
}
};
intersects.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if (newValue) {
System.out.println("Intersecting");
} else {
System.out.println("Not intersecting");
}
}
});
If you are still using old (pre-Java 8) versions of Java, you will need to declare pane and rect as final.

Using one event handler for multiple actions

I was doing some homework today and I've accomplished all of the goals of the assignment, which I'm sure will get me full points.
In an earlier class, however, we used the same Event Handler for more than one action (in this example, you either type a color in the text field, or click a button to change the background color of the box).
I can't figure out how I would do that in this case... do I have to choose a Type in the constructor? If the first parameter could be a button or a textfield then I think that would help.
I'm just trying to figure out how to apply DRY (Don't Repeat Yourself), where ever I can.
public class ColorChooserApplication extends Application
{
#Override
public void start(Stage stage)
{
// Create all UI components
VBox backgroundBox = new VBox(10);
backgroundBox.setPadding(new Insets(10));
HBox topBox = new HBox(10);
HBox bottomBox = new HBox(10);
TextField colorPrompt = new TextField();
colorPrompt.setOnAction(new ColorHandler(colorPrompt, backgroundBox));
Button redButton = new Button("Red");
redButton.setOnAction(new ButtonHandler(redButton, backgroundBox));
Button whiteButton = new Button("White");
whiteButton.setOnAction(new ButtonHandler(whiteButton, backgroundBox));
Button blueButton = new Button("Blue");
blueButton.setOnAction(new ButtonHandler(blueButton, backgroundBox));
// Assemble
topBox.getChildren().add(colorPrompt);
bottomBox.getChildren().addAll(redButton, whiteButton, blueButton);
backgroundBox.getChildren().addAll(topBox, bottomBox);
backgroundBox.setAlignment(Pos.CENTER);
topBox.setAlignment(Pos.CENTER);
bottomBox.setAlignment(Pos.CENTER);
// Set scene and show
stage.setScene(new Scene(backgroundBox));
stage.show();
}
class ColorHandler implements EventHandler<ActionEvent>
{
TextField colorTf;
VBox bgVbox;
public ColorHandler(TextField colorTf, VBox bgVbox)
{
this.colorTf = colorTf;
this.bgVbox = bgVbox;
}
#Override
public void handle(ActionEvent event)
{
String color = colorTf.getText();
bgVbox.setStyle("-fx-background-color:" + color);
}
}
class ButtonHandler implements EventHandler<ActionEvent>
{
Button colorButton;
VBox bgVbox;
public ButtonHandler(Button colorButton, VBox bgVbox)
{
this.colorButton = colorButton;
this.bgVbox = bgVbox;
}
#Override
public void handle(ActionEvent event)
{
String color = colorButton.getText();
bgVbox.setStyle("-fx-background-color:" + color);
}
}
public static void main(String[] args)
{
launch(args);
}
}
If you're using Java 8, you can do
class ColorHandler implements EventHandler<ActionEvent> {
Supplier<String> colorSupplier ;
VBox bgVbox ;
public ColorHandler(Supplier<String> colorSupplier, VBox bgVbox) {
this.colorSupplier = colorSupplier ;
this.bgVbox = bgVbox ;
}
#Override
public void handle(ActionEvent event) {
String color = colorSupplier.get();
bgVbox.setStyle("-fx-background-color: "+color);
}
}
and then
colorPrompt.setOnAction(new ColorHandler(colorPrompt::getText, backgroundBox));
redButton.setOnAction(new ColorHandler(redButton::getText, backgroundBox));
Note that all you need to provide for the first parameter is some function that returns the correct string for use in the css. So you can do things like
whiteButton.setOnAction(new ColorHandler(() -> "#ffffff", backgroundBox));
blueButton.setOnAction(new ColorHandler(() -> "cornflowerblue", backgroundBox));
etc.

JavaFx State change Listener

I am working on an application where I want to implement a menu. I have a GameState and a MainMenu class. Both extends Group. I can't figure out how to write a change listener to the Main.state, so when it changes from .MENU to .GAME the scenes will switch.
Here's is a part of the MainMenu class:
public class MainMenu extends Group {
private final Image background;
private final Rectangle bgRect;
private final int buttonNo = 3;
private MenuButton[] buttons;
private final double xStart = -200;
private final double yStart = 100;
private Group root;
private Scene scene;
public MainMenu () {
background = new Image(getClass().getResource("mainmenuBg.png").toString());
bgRect = new Rectangle(660,660);
bgRect.setFill(new ImagePattern(background));
root = new Group();
scene = new Scene(root, 650, 650);
scene.setCamera(new PerspectiveCamera());
root.getChildren().add(bgRect);
initButtons(root);
//Start game
buttons[0].setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event t) {
Main.state = STATE.GAME;
}
});
//Options
buttons[1].setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event t) {
//options menu will come here
}
});
//Exit
buttons[2].setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event t) {
Platform.exit();
}
});
}
//...
}
The main class:
public class Main extends Application {
public int difficulty = 1;
GameState gameState;
MainMenu mainMenu;
public enum STATE {
MENU,
GAME
}
public static STATE state = STATE.MENU;
#Override
public void start(Stage stage) {
stage.resizableProperty().setValue(false);
stage.setTitle("Main");
Scene scene = new Scene(new StackPane(), 650, 650);
stage.setScene(scene);
stage.show();
if(Main.state == STATE.MENU)
enterMainMenu(stage);
if(Main.state == STATE.GAME)
enterGameState(stage);
}
//...
}
Any help would be appreciated.
I have succeeded to find a good solution.
I have removed the scene field from these classes, and added the super method in the constructors, than added the elements to the class (this.getChildren().addAll(..)).
Finally, here's my main controller:
public class Main extends Application {
public int difficulty = 1;
public GameState gameState = new GameState(difficulty);
public MainMenu mainMenu = new MainMenu();;
StackPane stackPane = new StackPane();
#Override
public void start(final Stage stage) {
stage.resizableProperty().setValue(false);
stage.setTitle("Main");
Scene scene = new Scene(stackPane, 650, 650);
scene.setCamera(new PerspectiveCamera());
stage.setScene(scene);
stage.show();
stackPane.getChildren().add(mainMenu);
mainMenu.getStartButton().setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
changeScene(gameState);
try {
gameState.startGame();
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public void changeScene(Parent newPage) {
stackPane.getChildren().add(newPage);
EventHandler<ActionEvent> finished = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
stackPane.getChildren().remove(0);
}
};
final Timeline switchPage = new Timeline(
new KeyFrame(Duration.seconds(0), new KeyValue(stackPane.getChildren().get(1).opacityProperty(), 0.0), new KeyValue(stackPane.getChildren().get(0).opacityProperty(), 1.0)),
new KeyFrame(Duration.seconds(3), finished, new KeyValue(stackPane.getChildren().get(1).opacityProperty(), 1.0), new KeyValue(stackPane.getChildren().get(0).opacityProperty(), 0.0))
);
switchPage.play();
}
}

JavaFX stop Timeline

I use FXML. I've created a button to stop/restart a live chart. For the animation I've used Timeline. I'would like to control it from the guiController (from an other class), but it is not working. How can I stop a Timeline from an other class?
Thank you!
FXML:
<Button id="button" layoutX="691.0" layoutY="305.0" mnemonicParsing="false" onAction="#btn_startmes" prefHeight="34.0" prefWidth="115.0" text="%start" />
guiController:
#FXML
private void btn_stopmes(ActionEvent event) {
MotionCFp Stopping = new MotionCFp();
Stopping.animation.stop();
}
MotionCFp.java:
#Override
public void start(final Stage stage) throws Exception {
else{
ResourceBundle motionCFp = ResourceBundle.getBundle("motionc.fp.Bundle", new Locale("en", "EN"));
AnchorPane root = (AnchorPane) FXMLLoader.load(MotionCFp.class.getResource("gui.fxml"), motionCFp);
final guiController gui = new guiController();
Scene scene = new Scene(root);
stage.setTitle(motionCFp.getString("title"));
stage.setResizable(false);
stage.setScene(scene);
root.getChildren().add(gui.createChart());
animation = new Timeline();
animation.getKeyFrames().add(new KeyFrame(Duration.millis(1000/60), new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// 6 minutes data per frame
for(int count=0; count < 6; count++) {
gui.nextTime();
gui.plotTime();
animation.pause();
animation.play();
}
}
}));
animation.setCycleCount(Animation.INDEFINITE);
stage.show();
animation.play();
}
}
What you need is a reference in your controller to the original animation created in the start method of your application. This will allow you to code the button event handler in the controller to stop the animation.
The MotionCFp class can contain the code:
final FXMLLoader loader = new FXMLLoader(
getClass().getResource("gui.fxml"),
ResourceBundle.getBundle("motionc.fp.Bundle", new Locale("en", "EN"))
);
final Pane root = (Pane) loader.load();
final GuiController controller = loader.<GuiController>getController();
...
animation = new Timeline();
controller.setAnimation(animation);
And the GuiController class can contain the code:
private Timeline animation;
public void setAnimation(Timeline animation) {
this.animation = animation;
}
#FXML private void btn_stopmes(ActionEvent event) {
if (animation != null) {
animation.stop();
}
}
MotionCFp is your application class. You only need one instance of it. That instance is created by the the JavaFX launcher, you should never do new MotionCFp().
Please note that these kinds of questions are much easier to answer quickly and correctly if the code in the question is simple, complete, compilable and executable.

Resources