I've created a game and I wanted to add a start screen to it, I've added it using FXML as well as added 2 buttons (start and quit).
Upon pressing the start button, I want the game to load and switch scenes to the start of the game. I've got a rough idea of how to do it but i'm struggling a bit as my SampleController class does not know anything about starting up the game etc, as all of that code (as well as the code to load up the initial start menu) is in my Main class, so this is the kind of thing I tried:
#FXML
void startGame(ActionEvent event) {
background.start();
primaryStage.setScene(scene);
start();
}
I tried doing it using a function to switch scenes and it didn't work, also tried to get the information about the stage using Stage window = (Stage)((Node)event.getSource()).getScene().getWindow(); as I saw it as a possible solution from a YouTube video however it was telling me that Node cannot be resolved to a type.
This is my code:
Main
public class Main extends Application
{
AnimationTimer timer;
MyStage background;
Animal animal; //This is the player/frog that is moved
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/startMenu.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Game");
primaryStage.show();
//All the code after this point is essentially what I want to be executed upon pressing the button
background = new MyStage();
Scene scene = new Scene(background,600,800);
BackgroundImage froggerback = new BackgroundImage("file:resources/froggerBackground.png");
background.add(froggerback);
//At this point, a bunch of code like the line below is called to add in the different obstacles to the background
background.add(new Turtle(500, 376, -1, 130, 130));
//background.start();
//primaryStage.setScene(scene);
//primaryStage.show();
//start();
}
SampleControllerClass
public class SampleController {
#FXML
private Button quitButton;
#FXML
private Button startButton;
#FXML
void startGame(ActionEvent event) {
}
#FXML
void quitGame(ActionEvent event) {
}
Appreciate any help.
Thanks.
Assuming:
Your SampleController is the FXML controller which links the startButton for a game.
In your SampleController, you have an action event handler defined for your start button startGame.
You have another FXML file defined for your game layout, called game.fxml.
Then, you can get the current scene from the start button and replace the root of the scene with the new parent derived when loading the game.fxml:
#FXML
private Button startButton;
#FXML
void startGame(ActionEvent event) {
Parent gameRoot = FXMLLoader.load(
getClass().getResource("game.fxml")
);
startButton.getScene().setRoot(gameRoot);
}
You don't need to create a new scene or stage (unless that is something you really want to do for some other reason). If you really need to do that (which you probably don't), then that is outside the scope of what I am supplying in this answer.
Related
For a GUI I'm making the minimize, maximize, close buttons similar to that of Spotify by using an anchorpane for each 'button'. I created the buttons via scenebuilder and they are loaded to the class via fxml. I can't figure out how to call a particular instance of an anchorpane in the controller class to change its background color when the mouse enters or exits.
public static Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setScene(new Scene(root, 1280, 800));
...
this.primaryStage = primaryStage;
etc.
is how the UI class is set up
I expect the color of the anchorpane to change when the mouse enters the bounds, but as of now I have no idea how to call it.
Solved it, had to set an fxid to the anchorpane and then initiate it in the controller class after an #FXML.
Kinda like this:
#FXML
Anchorpane someButton;
#FXML
public void makeButtonWhite(MouseEvent event)
{
someButton.setStyle("-fx-background-color: #ffffff");
}
The most convenient way of doing this imho is using a stylesheet to assign the background.
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" stylesheets="#style.css">
<children>
<AnchorPane prefWidth="30" prefHeight="20" styleClass="my-button"/>
</children>
</AnchorPane>
style.css (same directory as fxml)
/* default style */
.my-button {
-fx-background-color: blue;
}
/* style when mouse is inside the region */
.my-button:hover {
-fx-background-color: red;
}
This allows you to easily add the style to multiple Regions; you simply need to add the style class it (styleClass="my-button").
the code you need for starting this must be the starting class
public class Class extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(this.getClass().getResource("/Folder/File.fxml"));
loader.setController(yourControllerClass());
//only if you do something with the controller class afterwards
YourControllerClass controller = loader.setController();
Parent parent = loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(parent);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
you know the code method in scenebuilder there you can set on mouse entered and exited for a button, a label etc. then if for example the button with the id "closeButton" and the OnMouseEntered "closeButtonOnEntered" and the OnMouseExited "closeButtonOnExited"
this will be the code you need
public yourcontrollerclass {
#FXML
Button closeButton;
#FXML
private void closeButtonOnEntered() {
//sets button red
button.setStyle("-fx-background-color: #ff0000");
}
#FXML
private void closeButtonOnExited() {
//sets button your first color
button.setStyle("-fx-background-color: transparent");
}
}
this can be done with nearly every thing in sceneBuilder
I hope i could help you(sorry for my bad english)
I am working on Java GUI I've got method (standardLoginScreen) which initialize Login screen, method is called by different controller.
Login screen has submit button which is handled by handleSubmitButton. I am struggling with closing Login screen using submit button.
I have tried various combinations but nothing seems to be working.
Please share your thoughts.
public class StandardController {
public Button submitButton;
public TextField textField;
public PasswordField passwordField;
//Call Standard Login screen
public void standardLoginSceen() throws IOException {
Stage standardStage = new Stage();
standardStage.setTitle("Standard Login Screen");
Parent root = FXMLLoader.load(getClass().getResource("StandardSignGui.fxml"));
standardStage.initModality(Modality.APPLICATION_MODAL);
standardStage.setScene(new Scene(root));
standardStage.show();
}
//Handles Submit button
public void handleSubmitButton() throws IOException {
//Closing standardStage
}
}
}
This is solution which I found in different thread :
close fxml window by code, javafx
#FXML private javafx.scene.control.Button closeButton;
#FXML
private void closeButtonAction(){
// get a handle to the stage
Stage stage = (Stage) closeButton.getScene().getWindow();
// do what you have to do
stage.close();
}
I'm a beginning Java programmer, finishing up the "Java 101" class at my local university. I'm also pushing myself to learn some extra topics on the side, including Java FX. I've worked through the Java FX tutorials on Oracle's website, plus sat through some YouTube videos, plus read "Java FX for Dummies" (which was the best book I could find for a beginner.) All of this material has taught me a lot of the basics, but some stuff that (should be) relatively simple escapes me.
For example: Let's say I have a Java FX program that uses multiple scenes on one stage. When the user clicks a "Switch!" button, the second scene is swapped out for the first. Easy. I can do all of this in one .java file, no problem. (See code below)
But my .java class file is getting really long and cumbersome to troubleshoot. It would be great if I could define/declare/initialize one scene as one class in one .java file and the second scene as another class in another .java file. This would make keeping track of the components of each scene much, much easier. The problem is, I can't figure out how to do this.
I'd imagine that you would write a Scene1.java class and then a Scene2.java class, and simply pass the stage object between the two when you want to switch scenes. But I can't find an example of how this is done, and all my attempts result in compiler errors or really scary runtime errors.
Does anyone know how this can be done? If so, what would I have to do to modify the SwitchScenes2() method below to create the new Scene2 object and pass it the stage?
Thanks! RAO
/*
JavaFXExample.java
*/
import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.geometry.*;
public class JavaFXExample extends Application{
public static void main(String[] args){
launch(args);
}
Button btnSw1;
Button btnSw2;
Button btnClose;
HBox hbox1;
VBox vbox1;
Scene scene1;
Scene scene2;
Stage stage;
#Override public void start(Stage primaryStage){
btnSw1 = new Button("Switch Scenes!");
btnSw1.setOnAction(
e -> SwitchScenes2() );
btnSw2 = new Button("Switch back!");
btnSw2.setOnAction(
e -> SwitchScenes1() );
btnClose = new Button();
btnClose.setText("Close me!");
btnClose.setOnAction(e -> CloseWindowClick());
hbox1 = new HBox(10);
hbox1.getChildren().addAll(btnSw1);
vbox1 = new VBox(10);
vbox1.getChildren().addAll(btnSw2, btnClose);
scene1 = new Scene(hbox1, 300, 300);
scene2 = new Scene(vbox1, 200, 400);
stage = primaryStage;
stage.setScene(scene1);
stage.setTitle("Example App");
stage.show();
}
public void SwitchScenes1(){
stage.setScene(scene1);
}
public void SwitchScenes2(){
stage.setScene(scene2);
}
public void CloseWindowClick(){
stage.close();
}
}
Pete as I understand you wish to separate one big java file into small files,create Java classes in each class create method(function) that will return layout(HBox,VBox, Flowpane or ....)then in your main create an object of that Java class and use those methods to build on big application.
in my sample I made one main and one separated class with one function,just to show you how its works. In my main there is 2 lables, 2 buttons one layout and one object of the separated class, by clicking the buttons scenes will change
My Main:
public class SwitchSceneSample extends Application {
public static void main(String[] args) {
launch(args);
}
Stage window;
Scene scene1, scene2;
#Override
public void start(Stage primaryStage) throws Exception {
// I am using window as primaryStage
window = primaryStage;
// Label 1
Label label1 = new Label("Welcome to the first scene!");
// Label 2
Label label2 = new Label("This is second scene!");
// Button 1, by pressing this button primaryStage will be set as scene 2
Button button1 = new Button("Go to scene 2");
button1.setOnAction(e -> window.setScene(scene2));
// Button 2, by pressing this button primaryStage will be set as scene 1
Button button2 = new Button("Click to go scene 1");
button2.setOnAction(e -> window.setScene(scene1));
// Creating an object of the class'LayoutOne.java'
LayoutOne l1 = new LayoutOne();
// set my scene 1(by calling method called 'sceneView1()' from class 'LayoutOne.java')
scene1 = new Scene(l1.sceneView1(label1, button1), 200, 200);
// Set my scene 2 inside my main class
StackPane layout2 = new StackPane();
layout2.getChildren().addAll(label2, button2);
scene2 = new Scene(layout2, 600, 300);
// Making my
window.setScene(scene1);
window.setTitle("Scene Switch Sample");
window.show();
}
}
My Second Class:
public class LayoutOne {
public VBox sceneView1(Label label, Button button) {
// Layout 1 - children are laid out in vertical column
VBox layout1 = new VBox(20);
layout1.getChildren().addAll(label, button);
return layout1;
}
}
What you will want to do is create separate classes that both have functions to return the scene. From there you will want to initialize these classes and with a button call a function that will add data to these scene or create a new blank scene (as a quick way to "delete" the scene). But if you want a more professional way to switch between scenes like this you will want to check out the TabPane().
Scene1 scene1 = new Scene1();
Scene2 scene2 = new Scene2();
TabPane tabPane = new TabPane();
Tab tab1 = new Tab();
tab1.setContent(scene1);
tabPane.getTabs().add(tab1);
Tab tab2 = new Tab();
tab2.setContent(scene2);
tabPane.getTabs().add(tab2);
Create a Manager class that contain the main method & initialize the first screen. eg.
public class VMCSManager extends Application {
private Parent content;
private static VMCSManager instance;
public VMCSManager() {
instance=this;
}
public static void main(String[] args) {
launch(args);
}
public static VMCSManager getInstance() {
return instance;
}
#Override
public void start(Stage primaryStage) throws Exception {
initializePanel();
Scene scene = new Scene(content);
stageStyle(primaryStage);
primaryStage.setScene(scene);
primaryStage.show();
}
private void initializePanel() throws IOException{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("fxml/SimulatorDisplay.fxml"));
content = loader.load();
}
public void openCustomerPanel() throws IOException{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("fxml/CustomerDisplay.fxml"));
content = loader.load();
Scene scene = new Scene(content);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Create main controller class for the first screen. eg;
public class SimulatorController implements Initializable{
#FXML
public void clickCustomer (ActionEvent event) throws IOException{
log.info("Starting Customer Panel");
VMCSManager.getInstance().openCustomerPanel();
}
#FXML
public void clickMaintainer(ActionEvent event) throws IOException{
log.info("Starting Maintainer Panel");
VMCSManager.getInstance().openMaintainerPanel();
}
}
Lastly Create the controller class for the specified screen. eg`
public class CustomerController extends SimulatorController{
#FXML
private Label brand1Lbl;
#FXML
private Label brand2Lbl;
#FXML
private Label brand3Lbl;
#FXML
private Label brand4Lbl;
#FXML
private Label brand5Lbl;
#FXML
private Label statusLbl1;
#FXML
private Label statusLbl2;
private static final Logger log=LoggerFactory.getLogger(CustomerController.class);
public CustomerController() {
context= new BuyingStateContext();
}
public void initialize(URL location, ResourceBundle resources) {
this.location = location;
this.rb = resources;
coinsValidityFlash.setVisible(false);
insertCoinTxt.setDisable(true);
brand1Btn.setStyle("-fx-background-color: #CACACA;");
brand2Btn.setStyle("-fx-background-color: #CACACA;");
brand3Btn.setStyle("-fx-background-color: #CACACA;");
brand4Btn.setStyle("-fx-background-color: #CACACA;");
brand5Btn.setStyle("-fx-background-color: #CACACA;");
populateVending();
}
.
.
.
}
`
I'm totally new in JavaFX... Until now, for my basic app I had all in one class - Main class. Now I want to extend my app and do this in proper way, so I would like to make controllers. As far as I know, I need to put package of my controller in root element in FXML file.
In my case it is AnchorPane element and I put something like this:
fx:controller="hr.controller.MainWindowController"
And in this controller should be all injections for ID's and methods, right? By injections I mean #FXML annotation. But how I connect this with my Main class? What should be in Main class? I know (I thinks so :P) that Main should extends Application class. So it contains this method:
#Override
public void start(Stage stage) {}
What's more and what I said in the beginning I also want be able to load/open new window after some action. Let's say that I have file mainWindow.fxml with above reference to the controller. The second file window2.fxml should be loaded after action on button in the 1st window.
Could you tell me, how should I achive this?
UPDATE!
Main class:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root));
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MainWindowController:
public class MainWindowController{
#FXML
private Button btnSave;
#FXML
private MenuBar menuBar;
#FXML
private MenuItem exit;
#FXML
public void handleAction(ActionEvent event) throws IOException {
Stage stage = null;
Parent root = null;
if(event.getSource()==exit){
stage = (Stage) menuBar.getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("window2.fxml"));
} else {
stage =(Stage) btnSave.getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
}
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
Is this the proper way? It works how I want to, but I'm not sure if it should looks like this :)
Let's say I want to create a method that adds a button to a StackPane. then I want to access to that button, for exemple I want to add an EventHandler from the main:
public class Test extends Application {
#Override
public void start(Stage primaryStage) throws ParseException {
StackPane root = new StackPane();
addButton(root);
// HERE I WANT TO ADD AN EVENT HANDLER TO b BUT I CANNOT ACCESS IT
// b.addEventHandler();
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public void addButton(StackPane sp){
final Button b = new Button("Test");
sp.getChildren().add(b);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Add event handler to the button when you creating the button
public void addButton(StackPane sp){
final Button b = new Button("Test");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
//your logic
}});
sp.getChildren().add(b);
}
Since button is local to your addButton you need to have a reference to it. Return the reference of your button from addButton method and use it.
It would be better to use it this way
public Button addButton(){
//create Button
}
You can add this button to the StackPane in your start()
Alternatively,
This approach is not recommended.
You can get the Button out of the StackPane
Button button = (Button)root.getChildren().get(0);
N.B. Use this only if you are sure of the position of the Button
UPDATE
If you just want to separate your design from the action performed by the controls, JavaFX provides an eligant way to do it, using FXML.
It helps you to design the UI without any indulgence of the actions that they need to perform. And later, these FXML's can be binded to their actions through Controllers(Java Interface)
Scope the UI controls to the instance of the application class, instead of making them local to the method.
public class Test extends Application {
private Button b ;
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
addButton(root);
b.addEventHandler(...);
// ...
}
private void addButton(Pane pane) {
b = new Button("Test");
pane.getChildren().add(b);
}
// ...
}
Or, if you really want to separate the layout from the event handling, do the layout in FXML and inject the controls into a controller class, which can be responsible for the event handlers.