I am new in Javafx (and Java).
I am trying to display a label "title" to different scenes. Therefore this label in a instance variable. I create it using the method title().
The problem is that the title does not appear in my scenes. I tried to initialize it at class level, or inside the start(), or inside scene1(). Nothing works but when I create a local label inside the scene1() method.
Thanks in advance for your help.
ps: full repo here if needed
private Label title;
public void start(Stage primaryStage) {
title();
scene1();
scene2();
[some more code...]
}
public Node title() {
title = new Label();
title.setText("SNAKE");
title.setTextFill(Color.YELLOW);
title.setFont(Font.font(STYLESHEET_MODENA, FontWeight.BOLD, 80));
return title;
}
public Scene scene1() {
Label lbl1scene1 = new Label("Welcome to Game !");
Label lbl2scene1 = new Label("Click to start the game");
Button btnStart = new Button("Start the game now!");
btnStart.setOnAction(e -> {
runGame();
});
VBox vbox1 = new VBox(title, lbl1scene1, lbl2scene1, btnStart);
vbox1.setAlignment(Pos.CENTER);
vbox1.setSpacing(10);
scene1 = new Scene(vbox1, width, height);
return scene1;
}
I am trying to display a label "title" to different scenes.
Please note that a node can have only a single parent.
Create multiple instances using local variables instead of a title member field.
Related
So I want to do a title Menu for a video game project for college. I want to display a message press any key to continue... then when the user pressed any key, including the mouse, a method would run to set the stage to the next menu.
I posted all the relevant code bellow but short version is:
BackgroundImangeDisplayPane extends display pane and adds a background image.
TitleCard extends BackgroundImangeDisplayPane adds a VBox and 2 labels to the VBox
I'm using public void start(Stage primaryStage) throws Exception as the main, I set the setOnActionxxx methods here
I have tried using the set on action method on root and Vbox and non of them work... when I click nothing happens... But when I resize the window The root.setOnActionXXX "activates".
If I write the setOnAction methods on the TitleCard class It kind of works but then I cant switch the stage.
I will post the code bellow as well an explanation of the Scene structure its not to complicated:
// this will be the borderpane for every scene it recives a backgund
//images that will be present in every menu
public BackgroundImangeDisplayPane() {
try {
stream = new FileInputStream(imagePath.toString());
Image image = new Image(stream);
ImageView imageView = new ImageView();
imageView.setImage(image);
imageView.setFitWidth(1920);
imageView.setPreserveRatio(true);
this.getChildren().add(imageView);
BackgroundSize backgoundSize = new BackgroundSize(AUTO, AUTO, true, true, true, true);
BackgroundImage backgroundImage = new BackgroundImage(image, NO_REPEAT, NO_REPEAT, CENTER, backgoundSize);
Background background = new Background(backgroundImage);
this.setBackground(background);
} catch (Exception e) {
}
}
//This extends `BackgroundImangeDisplayPane` and places on top of it a A Vbox with two lables: the title and "press any key to continue..."
// it then adds styles to the labels
public class TitleCard extends BackgroundImangeDisplayPane {
Label title = new Label("Boats & Docks"); // lable 1
Label subtitle = new Label("Press any key to continue ..."); label2
public TitleCard(){
super();
VBox vbox = new VBox();
vbox.getChildren().add(title);
vbox.getChildren().add(subtitle);
this.setCenter(vbox);
this.setAlignment(vbox, Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
title.setFont(new Font(170)); // set to Label
title.setTextFill(Color.SNOW);
title.setEffect(new DropShadow());
subtitle.setFont( new Font (30));
}
}
...
//Works as the "main" in javaFX
private Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
TitleCard root = new TitleCard();
/*BasicMenu menu = new BasicMenu(5);
menu.ButtonSetOnAction(0, e -> changeScene() );
BackgroundImangeWithCustomMenu background = new
BackgroundImangeWithCustomMenu(menu,50,50);
root.setCenter(background);*/
Button b = new Button();
b.setOnAction(e -> changeSceneToLoginMenu());
System.out.println(root.getChildren().get(1).getClass());
root.getChildren().get(1).setFocusTraversable(true);
root.getChildren().get(1).setOnMouseClicked(e -> changeSceneToLoginMenu());
root.getChildren().get(1).setOnKeyPressed(e -> changeSceneToLoginMenu());
root.getChildren().get(0).setOnMouseClicked(e -> changeSceneToLoginMenu());
root.getChildren().get(0).setOnKeyPressed(e -> changeSceneToLoginMenu());
/*
root.setOnMouseClicked(e -> changeSceneToLoginMenu());
root.setOnKeyReleased(e -> changeSceneToLoginMenu());
root.setOnKeyPressed(e -> changeSceneToLoginMenu());
*/
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
this.primaryStage = primaryStage;
}
As proposed by James in comments, when a key is pressed on the scene, navigate to the next scene (or replace the root in the current scene, and remove the key press handler).
scene.setOnKeyPressed(e -> navigateToNextScene());
I managed to find a very simple working solution but I don't fully undestand why it does work. I Noticed that if I set the handler in the same class the node was instanciated the handler would work fine But if I tried to get the node with a method to the main fuction via root.getChildren().get(1) and then cast it to the VBox element the handler would not work.
As a solution I made the VBox a field and wrote a setter method for the VBox event Handler in the TitleCard Class. This fixed the problem.
I marked the code added as solution code with comments
public class TitleCard extends BackgroundImangeDisplayPane {
Label title = new Label("Boats & Docks"); // lable 1
Label subtitle = new Label("Press any key to continue ..."); label2
VBox vbox = new VBox; // solution code
public TitleCard(){
super();
VBox vbox = new VBox();
vbox.getChildren().add(title);
vbox.getChildren().add(subtitle);
this.setCenter(vbox);
this.setAlignment(vbox, Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
title.setFont(new Font(170)); // set to Label
title.setTextFill(Color.SNOW);
title.setEffect(new DropShadow());
subtitle.setFont( new Font (30));
}
// Solution Code
public void setVBoxHandler(EventHandler<? super MouseEvent> value){
vbox.setOnMouseClicked(value);
}
}
Then I set the handler in the start method:
public void start(Stage primaryStage) throws Exception {
TitleCard root = new TitleCard();
VBox vBox =(VBox) root.getChildren().get(1);
root.setVBoxHandler(e->changeSceneToLoginMenu() ); // solution Code
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
this.primaryStage = primaryStage;
}
public void changeSceneToLoginMenu() {
System.out.println("It finally worked");
Scene currentScene = new Scene(new Group(),100,100); // just a demo
primaryStage.setScene(currentScene);
}
notes: The type of value on the method setVBoxHandler(EventHandler<? super MouseEvent> value) will depend on the setOnXXX method used. For example I tested and this soultion also works for buttons just need to change the type to EventHandler<ActionEvent> value.
Some coments on the question posted links on "how to use handlers", this posts used anomimous classes. I belived This way is outdated. I used lambdas in the code the end result is the same but more redable code
Just for reference if future readers are using anomimous classes the solution would be the same just change the way you set up the handler:
// lambdas
setVBoxHandler( e -> System.out.println("Code run if mouse is clicked "));
// anomimous classes
setVBoxHandler(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event){
System.out.println("Code run if mouse is clicked ");
}
});
I was working on JavaFX, and I realised something which kept me thinking. So, there's a scene in my constructor which adds the variable root of the type BorderPane. In the main class, this class' constructor is called first, thereby initialising the BorderPane in the Scene. However, the BorderPane is being modified and edited in a method called build. I am confused how the BorderPane is still being able to display its updated version from the build() since in the constructor it has already been commanded to display.
Here's my code:
public class SnacksOrDrinks
{
private BorderPane root;
private Stage primaryStage;
private Scene scene;
private Cart crt;
public SnacksOrDrinks(Stage primaryStage, Cart crt)
{
// BorderPane is being initialised and is being put inside the scene:
BorderPane root1 = new BorderPane();
this.scene = new Scene(root1, 800, 475);
this.crt = crt;
ImageView imgview = new ImageView("./application/MountainPainting.jpg");
imgview.fitWidthProperty().bind(primaryStage.widthProperty());
imgview.fitHeightProperty().bind(primaryStage.heightProperty());
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
this.primaryStage = primaryStage;
this.root = root1;
root.setStyle("-fx-background-color: blue");
root.getChildren().add(imgview);
}
public void build(Dispenser dis)
{
// ItemsPaneProp extends GridPane and stores the common properties shared in other classes.
ItemsPaneProp pane = new ItemsPaneProp();
// Header Created:
CommHeaderProp header = new CommHeaderProp("Lopes Vending Machine");
// However, BorderPane is being modified here, and the constructor has already been called.
BorderPane.setAlignment(header, Pos.CENTER);
root.setTop(header);
// Create a Line:
Line line = new Line(100, 10, 300, 10);
line.setFill(Color.GOLDENROD);
line.setStrokeWidth(2);
line.setSmooth(true);
pane.add(line, 0, 0);
// Create all the Scene's Components and setup the Event Handlers
ImageView img = new ImageView("./application/softdrink.jpg");
img.setFitWidth(75);
img.setFitHeight(75);
Button btn1 = new Button("Select to get Drinks", img);
btn1.setStyle("-fx-background-color: white");
btn1.setFont(Font.font("Times New Roman", FontWeight.BOLD, FontPosture.REGULAR, 12));
btn1.setContentDisplay(ContentDisplay.TOP);
pane.add(btn1, 0, 1);
ImageView img1 = new ImageView("./application/potato-chips.png");
img1.setFitWidth(75);
img1.setFitHeight(75);
Button btn2 = new Button("Select to get Snacks", img1);
btn2.setStyle("-fx-background-color: white");
btn2.setFont(Font.font("Times New Roman", FontWeight.BOLD, FontPosture.REGULAR, 12));
btn2.setContentDisplay(ContentDisplay.TOP);`enter code here`
pane.add(btn2, 1, 1);
root.setCenter(pane); // The BorderPane is being modified here too, but the constructor isn't updating the scene. Then, how does it still work?
btn1.setOnMouseClicked(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
// Create, build, and show Scene 2
DrinksList scene = new DrinksList(primaryStage, crt);
scene.build(dis);
scene.show();
}
});
btn2.setOnMouseClicked(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
// Create, build, and show Scene 2
SnacksList scene = new SnacksList(primaryStage, crt);
scene.build(dis);
scene.show();
}
});
}
public void show()
{// setting scene:
primaryStage.setScene(scene);
}
}
Take a look at https://docs.oracle.com/javase/8/javafx/scene-graph-tutorial/scenegraph.htm#JFXSG107
The JavaFX scene graph is a retained mode API, meaning that it maintains an internal model of all graphical objects in your application. At any given time, it knows what objects to display, what areas of the screen need repainting, and how to render it all in the most efficient manner.
Your borderPane - part of SceneGraph, so JavaFX knows, when you change anything in your borderPane, and automatically invoking redraw method for it.
The title may be a bit vague, so allow me to define it a little better. I have a working piece of code (down below): a simple main menu for a game I am working on. Everything works well, except for the Start button.
What I want to be able to do is click the Start button, and have a new scene appear on the same stage (window). I do not want to see a new window open. I have talked with someone more experienced in Java, and they told me to create separate classes for the MenuFX and the GameFX. If that is the case, I would need to call some start or launch method on the GameFX class from the MenuFX class, correct? Is this the best approach, or would I want to keep all FX-related code in one class? Also, I should keep the same stage for all FX work, no?
This post shed some light on things, but I am not well-versed in some of the terms discussed- for instance, I still do not understand the concept of Root.
Also, this post talks about a similar application, but I am not using FXML or SceneBuilder... I do not know if any of it is relatable.
MenuFX.java - I have removed some of the working code, simply for brevity. You can see that all I need help with is tying the Start button to some functionality that makes a new empty scene.
/*
* This is simply working on the title screen.
*/
// Asssume all imports are correct
import java.everythingNeeded
public class MenuFX extends Application {
#Override
public void start (Stage primaryStage) {
// Make the window a set size...
primaryStage.setResizable(false);
// Create menu vbox and set the background image
VBox menuVBox = new VBox(30);
menuVBox.setBackground(new Background(new BackgroundImage(new
Image("image/bambooBG.jpg"), null, null, null, new BackgroundSize(45,
45, true, true, true, true))));
// Create start button
Button startButton = new Button("Start Game");
// TODO Some things...
// Need assistance here
// Create help button
Button helpButton = new Button("Help");
helpButton.setOnAction(e -> THINGS);
// Create music toggle button
ToggleButton musicButton = new ToggleButton("Music On/Off");
musicButton.setOnAction(e -> THINGS);
// Create credits button
Button creditsButton = new Button("Credits");
creditsButton.setOnAction(THINGS);
// Create exit button and set it to close the program when clicked
Button endButton = new Button("Exit Game");
endButton.setOnAction(e -> Platform.exit());
// Add all nodes to the vbox pane and center it all
// Must be in order from top to bottom
menuVBox.getChildren().addAll(startButton, helpButton, musicButton, creditsButton, endButton);
menuVBox.setAlignment(Pos.CENTER);
// New scene, place pane in it
Scene scene = new Scene(menuVBox, 630, 730);
// Place scene in stage
primaryStage.setTitle("-tiles-");
primaryStage.setScene(scene);
primaryStage.show();
}
// Needed to run JavaFX w/o the use of the command line
public static void main(String[] args) {
launch(args);
}
}
Restating: I want to click the Start button and have the currently open window change to an empty scene.
Here is a pastebin of the MenuFX class in its entirety:
http://pastebin.com/n6XbQfhc
Thank you for any help,
Bagger
The basic idea here is you would do something like:
public class GameFX {
private final BorderPane rootPane ; // or any other kind of pane, or Group...
public GameFX() {
rootPane = new BorderPane();
// build UI, register event handlers, etc etc
}
public Pane getRootPane() {
return rootPane ;
}
// other methods you may need to access, etc...
}
Now back in the MenuFX class you would do
Button startButton = new Button("Start Game");
startButton.setOnAction(e -> {
GameFX game = new GameFX();
primaryStage.getScene().setRoot(game.getRootPane());
});
I'm developing a chat and decided to stick to the following structure:
All the chat messages consist of an outer VBox and inner VBox (chatBlock). Inner VBox contains Pane with Label blocks. One Pane + Label block is for nickname, and another one is for the message.
So I want to colorize nickname panel and message panel separately. But setStyle() works only for inner VBox. Here is the code of what I'm trying to do:
public class ChatMessageBlock extends VBox {
private VBox chatBlock = new VBox();
private Pane nickNameBox = new Pane();
private Label nickname;
private Pane messageBox = new Pane();
private Label message;
public ChatMessageBlock(String nickname, String message) {
this.nickname = new Label(nickname);
this.message = new Label(message);
this.message.setWrapText(true);
initGUI();
}
private void initGUI() {
nickNameBox.setStyle("-fx-background-color: red"); //DOESN'T WORK
messageBox.setStyle("-fx-background-color: green"); //DOESN'T WORK
// chatBlock.setStyle("-fx-background-color: blue"); //WORK
nickNameBox.getChildren().add(nickname);
messageBox.getChildren().add(message);
chatBlock.getChildren().addAll(nickname, message);
this.getChildren().add(chatBlock);
}
}
How to achieve colorization of separate blocks inside VBox panel?
Try this:
nickNameBox.setStyle("-fx-background-color: #992222;");
EDIT:
You should add nickNameBox and messageBox to the VBox. These are not related to anything.
for example write these lines before this.getChildren().add(chatBlock); in initGUI():
this.getChildren().add(nickNameBox);
this.getChildren().add(messageBox);
It's good practice to use singleton to create new window?
I have main window and i want create another one. This window will be used only to change properties in main window.
My code:
Main Window
public class MainWindow {
private StackPane root = new StackPane();
private Stage primaryStage = new Stage();
public void run(){
primaryStage.setTitle("v0.2-alpha");
Scene scene = new Scene(root, 600, 400);
scene.getStylesheets().addAll("css/style.css");
MainMenu mmb = new MainMenu();
VBox vBox = new VBox();
vBox.getChildren().add(mmb.createMenuBar());
ISplitPane lsp = new SplitPaneLeftImpl();
ISplitPane csp = new SplitPaneCenterImpl();
ISplitPane rsp = new SplitPaneRightImpl();
HBox hboxpane = new HBox();
hboxpane.getChildren().addAll(spl.createSplitPane(), spc.createSplitPane(), spr.createSplitPane());
root.getChildren().addAll(vBox,hboxpane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Create new window class
public interface IStage {
public void createStage();
}
class StageOptionsImpl implements IStage{
private OptionsStage(){}
private Stage stageOptions = new Stage();
private static StageOptionsImpl os = null;
public static StageOptionsImpl getInstance(){
if(os == null){
synchronized(StageOptionsImpl.class){
if(os == null){
os = new StageOptionsImpl();
}
}
}
return os;
}
#Override
public void createStage(){
GridPane gp = new GridPane();
TabPane tabPane = new TabPane();
tabPane.setSide(Side.LEFT);
Tab tabSecurity = new Tab("Security");
tabSecurity.setContent(new SecurityTab().tabCreate());
Tab tab2 = new Tab("System Data");
tab2.setContent(new DataTab().tabCreate());
Tab tab3 = new Tab("tab 3");
tab3.setContent(new SecurityTab().tabCreate());
tabPane.getTabs().addAll(tabSecurity, tab2, tab3);
Scene sceneOptions = new Scene(tabPane, 400, 300, Color.AQUA);
stageOptions.setScene(sceneOptions);
stageOptions.show();
}
}
If it's only used from there, why would you want a singleton. Whole point of a Singleton is so you can use the same intance of it from anywhere.
It's good practice to use singleton to create new window?
Well, that comes down to a design question.
If you only ever want to have a single, shared instance of a Component in you're application, then I would say a singlton is not a bad approach. It will force this choice.
If, however, you want to display multiple instances of the same Component but want, say, a simpler way to construct it, or want to expose a non-concrete interface through the API, then a Factory pattern would be of a better choice.
The best approach to manage these windows is usually by creating a class that manages all of the windows/panels in the program.
If you wanted to open the panel called 'ClientForm' it should have a method like this:
public void OpenClientForm(){
// Set the other forms to their default form(when != null) and set their visibility as false (when != null).
RestorePanels();
// In case it hasn't been created yet
if (ClientForm == null){
// calls a factory that creates the form.
}
else{
// Set the form as visible
}
}
When 'disposing' of panels/forms inside of the program you should have a method that clears the panels, forcing them back to their original state and setting their visible as false.
Swing is pretty buggy, disposing of the forms and recreating them could mess up a lot of things in your interface. So, back to your question... implemeting a singleton or a factory to manage ur forms should be the best approach.
Hope this helps.