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);
}
Related
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();
}
I have the main application class that does the following just fine:
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"RecordScreen.fxml"));
Parent root = (Parent) loader.load();
Scene newScene = new Scene(root);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
It launches a table view that displays people. I select a person, hit the edit button, and try to launch a window that will let me edit them.
#FXML
public void editPerson() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"PersonEditor.fxml"));
PersonEditorCtrl ctrl = loader.getController();
ctrl.init(table.getSelectionModel().getSelectedItem());
Parent root = (Parent) loader.load();
Scene newScene = new Scene(root);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
The problem is, getController is returning null. I have been following this pattern for the past 2 weeks with no problems whatsoever. What am I doing wrong now? These untraceable bugs are aggravating!!!
Here are my two fxmls:
The screen with tableview:
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="application.RecordsCtrl">
<!-- TODO Add Nodes -->
<children>
<VBox id="VBox" alignment="CENTER" spacing="0.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<TableView fx:id="table" prefHeight="-1.0" prefWidth="-1.0">
<columns>
<TableColumn prefWidth="75.0" text="Name" fx:id="nameCol" />
<TableColumn prefWidth="75.0" text="Age" fx:id="ageCol" />
</columns>
</TableView>
<Button mnemonicParsing="false" onAction="#editPerson" text="Edit" />
</children>
</VBox>
</children>
</AnchorPane>
The person editor:
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="application.PersonEditorCtrl">
<!-- TODO Add Nodes -->
<children>
<VBox layoutX="0.0" layoutY="0.0" prefHeight="-1.0" prefWidth="-1.0">
<children>
<TextField fx:id="nameField" prefWidth="200.0" />
<TextField fx:id="ageField" prefWidth="200.0" />
<Button mnemonicParsing="false" text="Button" />
</children>
</VBox>
</children>
</AnchorPane>
Change this
#FXML
public void editPerson() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"PersonEditor.fxml"));
PersonEditorCtrl ctrl = loader.getController();
ctrl.init(table.getSelectionModel().getSelectedItem());
Parent root = (Parent) loader.load();
Scene newScene = new Scene(root);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
To that:
#FXML
public void editPerson() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"PersonEditor.fxml"));
Parent root = (Parent) loader.load();
PersonEditorCtrl ctrl = loader.getController();
ctrl.init(table.getSelectionModel().getSelectedItem());
Scene newScene = new Scene(root);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
You first have to run loader.load() then you can get the Controller.
Patrick
I want to create new label in another fxml when a button is clicked. I used the following code but the following exception is thrown
java.lang.RuntimeException:
java.lang.reflect.InvocationTargetException
The code:
Controller.java
public class Controller {
public AnchorPane anchorPane;
public void OpenSecond() throws Exception{
Stage primaryStage = new Stage();
Parent root = FXMLLoader.load(getClass().getResource("sample1.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public void AddLabe(){
Label label = new Label("Label");
anchorPane.getChildren().add(label);
}
}
second.java
public class Second {
public void AddLiabel()
{
Controller controller = new Controller();
controller.AddLabe();
}
}
I am using IntelliJ IDEA and Scene builder
You should be getting this exception because Java couldn't find the specified file or it is empty. Search for
Caused by: javafx.fxml.LoadException:
in the exception.
Adding the "sample1.fxml" and initializing it with a pane should fix the problem.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="275.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" />
Most probably you miss annotation for anchorPane and JavaFX didn't initialize it. Try next:
#FXML
public AnchorPane anchorPane;
and make sure anchorPane is listed in your FXML file.
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.
I want to find a VBox node in a scene loaded with FXMLoader thanks to Node#lookup() but I get the following exception :
java.lang.ClassCastException: com.sun.javafx.scene.control.skin.SplitPaneSkin$Content cannot be cast to javafx.scene.layout.VBox
The code :
public class Main extends Application {
public static void main(String[] args) {
Application.launch(Main.class, (java.lang.String[]) null);
}
#Override
public void start(Stage stage) throws Exception {
AnchorPane page = (AnchorPane) FXMLLoader.load(Main.class.getResource("test.fxml"));
Scene scene = new Scene(page);
stage.setScene(scene);
stage.show();
VBox myvbox = (VBox) page.lookup("#myvbox");
myvbox.getChildren().add(new Button("Hello world !!!"));
}
}
The fxml file:
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" >
<children>
<SplitPane dividerPositions="0.5" focusTraversable="true" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<VBox fx:id="myvbox" prefHeight="398.0" prefWidth="421.0" />
</items>
</SplitPane>
</children>
</AnchorPane>
I would like to know :
1. Why lookup method return a SplitPaneSkin$Content and not a VBox ?
2. How I can get the VBox in another manner ?
Thanks in advance
The easiest way to get a reference to the VBox is by calling FXMLLoader#getNamespace(). For example:
VBox myvbox = (VBox)fxmlLoader.getNamespace().get("myvbox");
Note that you'll need to create an instance of FXMLLoader and call the non-static version of load() in order for this to work:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("test.fxml"));
AnchorPane page = (AnchorPane) fxmlLoader.load();
SplitPane puts all items in separate stack panes (fancied as SplitPaneSkin$Content). For unknown reason FXMLLoader assign them the same id as root child. You can get VBox you need by next utility method:
public <T> T lookup(Node parent, String id, Class<T> clazz) {
for (Node node : parent.lookupAll(id)) {
if (node.getClass().isAssignableFrom(clazz)) {
return (T)node;
}
}
throw new IllegalArgumentException("Parent " + parent + " doesn't contain node with id " + id);
}
and use it next way:
VBox myvbox = lookup(page, "#myvbox", VBox.class);
myvbox.getChildren().add(new Button("Hello world !!!"));
you can use Controller and add autopopulated field:
#FXML
VBox myvbox;