Everything works fine after switching to scene with my method (swichScene) at the first time.
But if I go to another scene and switch back (second time opening scene) the Buttons in my GameController aren't reassigned, so I have the old Buttons and can't change anything.
The variables get updated when pressing the Button.
Steps to recreate:
create 2 fxml scenes with at least 2 buttons in the second
switch to second scene and change the Image in the button which isn't pressed (When the other one is pressed)
-> works
Switch back to scene 1 and repeat step 2
-> doesn't work
My Controller:
public class GameController {
... some stuff ...
#FXML
private Button gameField0;
... some stuff...
My Button in game.fxml which has GameController as controller
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="1000.0" style="-fx-background-color: #0E328C;" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.example.tictactoe.GameController">
... some stuff ...
<Button fx:id="gameField0" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" onAction="#pressedField0" prefHeight="100.0" prefWidth="100.0" styleClass="tttfield">
<graphic>
<ImageView fitHeight="75.0" fitWidth="75.0" pickOnBounds="true" preserveRatio="true" />
</graphic>
</Button>
... some stuff ...
My start():
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource("startpage.fxml"));
Scene scene = new Scene(fxmlLoader.load());
// Stage setup
stage.setTitle("TicTacToe");
Image icon = new Image(App.class.getResource("icon.png").toString());
stage.getIcons().add(icon);
stage.setScene(scene);
stage.show();
this.stage = stage;
this.scene = scene;
}
Switching to game.fxml which has GameController as controller
public static void swichScene(Scene currentScene, Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(game.fxml));
currentScene.setRoot(fxmlLoader.load());
stage.setScene(scene);
stage.show();
}
Related
I am attempting to create a method in my main screen controller to set a new scene in my program. When attempting to do so, I get the following error:
Error resolving onAction='#partAddButtonPushed', either the event handler is not in the Namespace or there is an error in the script.
The relevant fxml code is:
<AnchorPane id="AnchorPane" prefHeight="420.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="john.Doe.View_Controller.MainController">
<children>
<Button id="addPartsButton" fx:id="partAdd" layoutX="190.0" layoutY="340.0" maxWidth="70.0" minWidth="70.0" mnemonicParsing="false" onAction="#partAddButtonPushed" prefWidth="70.0" text="Add">
and the relevant code in the controller is:
public class MainController implements Initializable {
#FXML
private Button partAdd;
#FXML
private void partAddButtonPushed(ActionEvent event) throws IOException {
Parent partAddParent = FXMLLoader.load(getClass().getResource("/View/AddPart.fxml"));
Scene AddPartScene = new Scene (partAddParent);
Stage window = (Stage)((Node)event.getSource()).getScene().getWindow();
window.setScene(AddPartScene);
window.show();
}
Would anyone happen to know why they are not linking up?
The nodes get an automatic resize at the first load of FXML, but it stays the same when I change the texts in label during the run time.
Minimal, Complete, and Verifiable example
Main Class:
public class Main extends Application {
private Stage stage;
private StackPane stackPane;
#Override
public void start(Stage primaryStage) throws Exception{
stage = primaryStage;
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = (Parent) loader.load();
stackPane = new StackPane(root);
Scene scene = new Scene(stackPane);
scene.setFill(Color.WHITE);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller Class:
public class Controller implements Initializable{
#FXML private Label label1;
#FXML private Button changeLabel;
private StringProperty labelString = new SimpleStringProperty();
#Override
public void initialize(URL location, ResourceBundle resources) {
labelString.setValue("aasdasd sad asdasd asdasda");
label1.textProperty().bind(labelString);
}
#FXML
public void clicked(MouseEvent e){
labelString.setValue("asdsadasd asdasdasd sfdgsfwoef fgtrhfgbdrgdf dfgdfivbjkfd gdfgidfjvdf gdfgjldkvbdf gjdilgjdfv dfgojdflkgdf ");
}
}
sample.fxml:
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10" maxHeight="Infinity">
<children>
<VBox fx:id="body"
alignment="CENTER"
maxHeight="Infinity"
GridPane.vgrow="SOMETIMES"
prefWidth="300"
GridPane.columnIndex="0"
GridPane.rowIndex="1" spacing="5">
<Label alignment="CENTER" VBox.vgrow="ALWAYS" wrapText="true" textAlignment="CENTER" fx:id="label1" maxWidth="268"/>
<Button fx:id="changeLabel" text="Change" minWidth="50" onMouseClicked="#clicked" maxWidth="Infinity" />
</VBox>
</children>
</GridPane>
Expectation:
When I press 'Change' button, Everything should resize to just give enough space for text to be shown.
Problem:
When I press 'Change' button, UI remains same not showing the full text.
The auto-adjustment depends on the layout pane that you are using. I suggest you take a look at some of the documentation, e. g. Using Built-in Layout Panes.
As for your case, you want to resize the stage. Nobody could possibly guess what exactly you want. Do you want it to change the width or the height in order to meet your requirements?
However, here's an example solution for your case.
Add this to the controller:
public StringProperty getStringProperty() {
return labelString;
}
and this to the main class:
Controller controller = loader.getController();
StringProperty stringproperty = controller.getStringProperty();
stringproperty.addListener( (ChangeListener<String>) (observable, oldValue, newValue) -> {
stage.setHeight(400); // TODO: calculate width/height depending on what your requirements are
});
Alternatively you can hand over the stage to the controller. As I said that's just an example of many possible solutions. You need to be more specific.
As you can see, my texts are constrained by labels. How do I make the label resize to exactly fit the text and also to resize the VBox and GridPane accordingly. I only want to resize vertically.
My FXML:
<VBox fx:id="body"
alignment="TOP_CENTER"
prefHeight="150"
GridPane.vgrow="SOMETIMES"
prefWidth="300"
GridPane.columnIndex="0"
GridPane.rowIndex="1" spacing="5">
<Label alignment="CENTER" textAlignment="JUSTIFY" fx:id="word" maxWidth="268"/>
<Pane prefHeight="10" VBox.vgrow="NEVER"/>
<Label alignment="CENTER" textAlignment="JUSTIFY" wrapText="true" fx:id="meaning" maxWidth="268" />
<Label alignment="CENTER" textAlignment="JUSTIFY" wrapText="true" fx:id="sentence" maxWidth="268" />
</VBox>
This VBox is inside the GridPane:
<GridPane fx:id="base"
alignment="CENTER"
prefHeight="210.0"
maxWidth="310.0"
stylesheets="#style.css"
vgap="0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sample.Controller">
...
Minimal, Complete, and Verifiable example as requested by #James_D
Main Class:
public class Main extends Application {
private Stage stage;
private StackPane stackPane;
#Override
public void start(Stage primaryStage) throws Exception{
stage = primaryStage;
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = (Parent) loader.load();
stackPane = new StackPane(root);
Scene scene = new Scene(stackPane);
scene.setFill(Color.WHITE);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller Class:
public class Controller implements Initializable{
#FXML private Label label1;
#FXML private Button changeLabel;
private StringProperty labelString = new SimpleStringProperty();
#Override
public void initialize(URL location, ResourceBundle resources) {
labelString.setValue("aasdasd sad asdasd asdasda");
label1.textProperty().bind(labelString);
}
#FXML
public void clicked(MouseEvent e){
labelString.setValue("asdsadasd asdasdasd sfdgsfwoef fgtrhfgbdrgdf dfgdfivbjkfd gdfgidfjvdf gdfgjldkvbdf gjdilgjdfv dfgojdflkgdf ");
}
}
sample.fxml:
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10" maxHeight="Infinity">
<children>
<VBox fx:id="body"
alignment="CENTER"
maxHeight="Infinity"
GridPane.vgrow="SOMETIMES"
prefWidth="300"
GridPane.columnIndex="0"
GridPane.rowIndex="1" spacing="5">
<Label alignment="CENTER" VBox.vgrow="ALWAYS" wrapText="true" textAlignment="CENTER" fx:id="label1" maxWidth="268"/>
<Button fx:id="changeLabel" text="Change" minWidth="50" onMouseClicked="#clicked" maxWidth="Infinity" />
</VBox>
</children>
Expectation:
When I press 'Change' button, Everything should resize to just give enough space for text to be shown.
Problem:
When I press 'Change' button, UI remains same not showing the full text.
Assuming you want the text to wrap, the labels will need to be able to grow vertically. Add the attribute
maxHeight="Infinity"
to each label. You are also constraining the overall height of the VBox with prefHeight="150"; remove that attribute and let the VBox figure out its own height. Similarly, you probably want to remove the prefHeight attribute from the GridPane.
I tortured google with multiple questions but still cant get a point.
I want to build app with 2 FXML scenes populated by SceneBuilder. Each have it's own controller.
I use ControlledScreen to swap Scenes and this works.
But then i cant change anything on scene.
For example: i have label in Scene Controller:
public class ControllerForm implements ControlledScreen, Initializable {
ScreensController myController;
GraphicsContext GC;
#FXML
private Label fitnessLabel;
public void updateFitnessLabel(double data) {
fitnessLabel.setText(String.valueOf(data));
}
public void initialize(URL location, ResourceBundle resources) {
this.GC = neuroCanvas.getGraphicsContext2D();
}
It's defined in FXML file as:
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="684.0" prefWidth="918.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="general.ControllerForm">
<left>
<VBox prefHeight="400.0" prefWidth="189.0" BorderPane.alignment="CENTER">
<children>
<Label fx:id="fitnessLabel" text="Label">
<graphic>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Fitness:" />
</graphic>
</Label>
And function to update label is updateFitnessLabel.
In initialize method i use GC cuz i have canvas on this scene and want to draw on it.
I have method that is calling for this fucntion as:
public class Net {
private ControllerForm controller;
public void drawStart() throws IOException {
FXMLLoader loader = new FXMLLoader(
getClass().getResource(
Main.ScreenFile
)
);
BorderPane cv = loader.load();
// load actual screen
ControllerForm controller =
loader.<ControllerForm>getController();
controller.updateFitnessLabel(12);}
But no effect. Label wont be updated.
What am i missing ?
I tried to use Timeline, but still no effect.
How to actuall draw something on this scene ?
Ok, i found solution.
loader.load returns Object. But i need initial Object not a new one.
Used this answer - FXMLLoader getController returns NULL?
This is not optimal, but i have no idea how to get initial object.
I load the stage the following way:
public void start(Stage stage) throws Exception{
stage.setTitle("title");
Scene scene = (Scene)FXMLLoader.load(getClass().getResource("../view/MainMenu.fxml"));
stage.setScene(scene);
stage.setWidth(1080);
stage.setHeight(720);
stage.setFullScreen(false);
stage.show();
}
Change Scene:
#FXML protected void click(ActionEvent event) throws Exception{
Stage stage = (Stage)menu.getScene().getWindow();
Scene scene = (Scene)FXMLLoader.load(getClass().getResource("../view/MainMenu.fxml"));
stage.setScene(scene);
stage.show();
}
Fxml:
<Scene fx:controller="controller.CtrlMainMenu" xmlns:fx="http://javafx.com/fxml" stylesheets="view/Style.css">
<AnchorPane fx:id="menu">
<VBox spacing="8"
AnchorPane.topAnchor="0.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
<Button text="click" onAction="#click"/>
</VBox>
</AnchorPane>
</Scene>
The problem comes when changing the scene; the new scene is smaller and appears on top left and not fully fitting the window (window keeps its size, and after resizing the window, the scene refits again to the window, but before, not).
Also, is it possible to send an object to the controller of the view, since I'll need to work with the model with the controller? (using MVC architecture)
Now tried this method 2 with fx:root and have the following:
Aplication:
public void start(Stage stage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = (Pane) fxmlLoader.load(getClass().getResource("../view/MainMenu.fxml").openStream());
CtrlMainMenu controller = (CtrlMainMenu) fxmlLoader.getController();
controller.sendString("test"); //send object test
stage.setScene(new Scene(p));
stage.setWidth(1080);
stage.setHeight(720);
stage.setFullScreen(false);
stage.show();
}
Fxml:
<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml" fx:controller="controller.CtrlMainMenu">
<children>
<AnchorPane fx:id="menu">
<VBox spacing="8"
AnchorPane.topAnchor="0.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
<Button text="click" onAction="#click"/>
</VBox>
</AnchorPane>
</children>
</fx:root>
Change Scene:
#FXML protected void click(ActionEvent event) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = (Pane) fxmlLoader.load(getClass().getResource("../view/Options.fxml").openStream());
Stage stage = (Stage)menu.getScene().getWindow();
stage.setScene(new Scene(p));
stage.show();
}
overlap:
#FXML protected void Options(ActionEvent event) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = (Pane) fxmlLoader.load(getClass().getResource("../view/Options.fxml").openStream());
menu.getChildren().add(p);
}
fxml (old one did not have the fx:root line on it):
<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml"
fx:controller="controller.CtrlOptions" stylesheets="view/Style.css">
<children>
<GridPane xmlns:fx="http://javafx.com/fxml" alignment="center"
hgap="10" vgap="10" id="optionBackgorund"
AnchorPane.topAnchor="50.0" AnchorPane.bottomAnchor="50.0"
AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="50.0"
fx:id="options">
<!-- the view -->
</GridPane>
</children>
</fx:root>
ok, now can send an object and making a scene with fx:root, but still have the problem of the scene not fitting the stage
and now have a new problem, I did not have without fx:root, and this one was the one of using another AnchorPane to overlap the current one, now doesn't overlap it, it just appears in the middle but the size is kept small (before fitted with the anchor)
found the way to fit the scene to the stage:
#FXML protected void click(ActionEvent event) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = (Pane) fxmlLoader.load(getClass().getResource("../view/Options.fxml").openStream());
Scene scene= menu.getScene();
scene.setRoot(p);
}