So I am new to JavaFX, and I am trying to do an app for school and I've been reading lots of related posts, but I can't seem to figure it how to do for my own case. Here Is a picture
So the big screen is my main screen and the one with the red border are children of the Vbox. And for the child, I have a separate FXML and a controller. When I hit the "Close Deal" button I want to remove the specific child of the Vbox.
I know I should do parent.getChildren().remove(specific_child_node); The problem is I don't know how to get the specific_child_node for the child for which has been the "Closed Deal" button pressed. The close button is in the child's controller and the Vbox is in the main page controller
Has anyone any idea of how I could do it?
I created a small example to show how you could do it:
MainController Class:
package sample;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import java.io.IOException;
public class MainController {
#FXML
private VBox middleVBox;
#FXML
public void handleAddChildBtnClick() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("deal.fxml"));
GridPane root = null;
try {
root = loader.load();
} catch (IOException ex) {
ex.printStackTrace();
}
if (root == null) return;
// Get the controller instance:
DealController controller = loader.getController();
// Set a reference for the parent vbox:
controller.setParent(middleVBox);
middleVBox.getChildren().add(1, root);
}
}
FXML File "main.fxml":
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<GridPane alignment="center" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.MainController">
<children>
<VBox style="-fx-background-color: black;">
<children>
<Label text="Welcome, Client" textFill="WHITE" />
</children>
</VBox>
<VBox fx:id="middleVBox" GridPane.columnIndex="1">
<children>
<Label text="Generate Public Auction" />
<VBox>
<children>
<Label text="dfgh" textFill="#1aab0d" />
</children>
</VBox>
</children>
</VBox>
<VBox GridPane.columnIndex="2">
<children>
<Button mnemonicParsing="false" onAction="#handleAddChildBtnClick" text="Add Child" />
</children>
</VBox>
</children>
<columnConstraints>
<ColumnConstraints />
<ColumnConstraints />
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints prefHeight="200.0" />
</rowConstraints>
</GridPane>
DealController Class:
package sample;
import javafx.fxml.FXML;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
public class DealController {
#FXML
private GridPane dealRoot;
private VBox parent;
#FXML
public void handleCloseDealBtnClick() {
parent.getChildren().remove(dealRoot);
}
void setParent(VBox parent) {
this.parent = parent;
}
}
FXML File "deal.fxml":
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane fx:id="dealRoot" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.DealController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" />
<ColumnConstraints hgrow="SOMETIMES" />
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="SOMETIMES" />
<RowConstraints vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="cvbn" textFill="#008612" />
<Button mnemonicParsing="false" onAction="#handleCloseDealBtnClick" text="Close Deal" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
</GridPane>
Main Class:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I created this custom component:
public class IconButton extends Button {
#FXML private ImageView imageView;
private IconButtonState state;
private String fullIconUrl;
private String outlineIconUrl;
public IconButton(#NamedArg("fullIconUrl") String fullIconUrl,
#NamedArg("outlineIconUrl") String outlineIconUrl) {
URL url = getClass().getResource(View.ICON_BUTTON.getFileName());
FXMLLoader loader = new FXMLLoader(url);
loader.setRoot(this);
loader.setController(this);
state = IconButtonState.NOT_INITIALIZED;
this.fullIconUrl = fullIconUrl;
this.outlineIconUrl = outlineIconUrl;
try {
loader.load();
} catch (IOException exception) {
exception.printStackTrace();
throw new RuntimeException(exception);
}
}
#FXML
public void initialize() {
this.state = IconButtonState.ACTIVE;
String url = buildUrl(fullIconUrl);
Image image = new Image(url);
imageView.setImage(image);
}
}
<!-- icon-button.fxml -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<fx:root mnemonicParsing="false" prefHeight="65.0" prefWidth="98.0" style="-fx-background-color: transparent;" type="Button" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<graphic>
<ImageView fx:id="imageView" fitHeight="64.0" fitWidth="48.0" pickOnBounds="true" preserveRatio="true">
</ImageView>
</graphic>
</fx:root>
Then, I instantiated my IconButton component in another fxml file like this:
<!-- home.fxml -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.layout.*?>
<?import agill.deshopp.components.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="768.0" prefWidth="1024.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<top>
<HBox styleClass="app-bar" stylesheets="#../css/home.css">
<padding>
<Insets bottom="15.0" top="15.0" />
</padding>
<children>
<IconButton fullIconUrl="form-full" outlineIconUrl="form-outline"></IconButton>
<IconButton fullIconUrl="message-full" outlineIconUrl="message-outline"></IconButton>
<IconButton fullIconUrl="chart-full" outlineIconUrl="chart-outline"></IconButton>
<Region HBox.hgrow="ALWAYS"></Region>
<IconButton fullIconUrl="settings-full" outlineIconUrl="settings-outline"></IconButton>
</children>
</HBox>
</top>
<center>
<Pane BorderPane.alignment="CENTER" />
</center>
</BorderPane>
The code runs fine and the screen renders as expected. However, I can't open the file in SceneBuilder. It prompts me with this exception:
java.lang.RuntimeException: Cannot create instance of agill.deshopp.components.IconButton with given set of properties: [fullIconUrl, outlineIconUrl]
javafx.fxml.LoadException:
/home/allan/IdeaProjects/california/src/main/resources/agill/deshopp/fxml/home.fxml:14
How do I fix this?
The code posted in the question is not mre and can not be invoked.
However the following code is an mre. It runs with no exceptions.
Fxml files can be edited using ScreenBuilder.
Modify it to your needs to find out what's wrong in the code posted in the question.
Note that form-full throws exception so I used form_full and that the fxml assignment should include $ sign: fullIconUrl="$form_full".
package fx_tests.test;
import java.io.IOException;
import java.net.URL;
import javafx.beans.NamedArg;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class IconButton extends Button {
#FXML private ImageView imageView;
private final String fullIconUrl;
public IconButton(#NamedArg("fullIconUrl") String fullIconUrl) {
this.fullIconUrl = fullIconUrl;
URL url = getClass().getResource("icon-button.fxml");
FXMLLoader loader = new FXMLLoader(url);
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException exception) {
exception.printStackTrace();
}
}
#FXML
public void initialize() {
Image image = new Image(fullIconUrl);
imageView.setImage(image);
}
}
Home.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.layout.*?>
<?import fx_tests.test.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="168.0"
prefWidth="124.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<top>
<HBox>
<padding>
<Insets bottom="15.0" top="15.0" />
</padding>
<children>
<IconButton fullIconUrl="$form_full"></IconButton>
</children>
</HBox>
</top>
<center>
<Pane BorderPane.alignment="CENTER" />
</center>
</BorderPane>
icon-button.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<fx:root mnemonicParsing="false" prefHeight="65.0" prefWidth="98.0" style="-fx-background-color: transparent;"
type="Button" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<graphic>
<ImageView fx:id="imageView" fitHeight="64.0" fitWidth="48.0" pickOnBounds="true" preserveRatio="true">
</ImageView>
</graphic>
</fx:root>
Test with:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private static final String IMAGE = "https://www.shareicon.net/data/128x128/2015/03/28/14104_animal_256x256.png";
#Override
public void start(Stage currentStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Home.fxml"));
loader.getNamespace().put("form_full", IMAGE);
Parent root=loader.load();
currentStage.setScene(new Scene(root));
currentStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
When you create a custom component in JavaFX, SceneBuilder need to know the definition of this component so it can load it. I have a few custom components of my own and had to export them as a jar, and import them into SceneBuilder. Here is an answer that gives instructions on how to do this:
Adding a custom component to SceneBuilder 2.0
One catch. SceneBuilder only supports up to Java 11. So your custom component must be built and exported to a jar with this version. I had problems with Java 14 and had to backport my specific JavaFX components to Java 11. Good luck!
I am working on simple application which I need to make executable.
I am trying to switch from one scene to another in same window. Everything work perfectly when running in Intellij. However, when I created executable app suddenly control buttons stopped working.
There is my main class:
package code;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("FxmlLoginMenu.fxml"));
primaryStage.setTitle("Quiz application");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller Class:
One method inside which is executed when button pressed
package code;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import java.io.IOException;
public class Controller {
#FXML public Button signUp;
#FXML public void signUp() throws IOException {
Parent tableViewParent = FXMLLoader.load(getClass().getResource("FXMLRegister.fxml"));
Scene tableViewScene = new Scene(tableViewParent);
Stage window = (Stage) signUp.getScene().getWindow();
window.setScene(tableViewScene);
window.show();
}
}
And the last FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.effect.InnerShadow?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Line?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane fx:controller="code.Controller" prefHeight="385.0" prefWidth="437.0" xmlns="http://javafx.com/javafx/9.0.4" xmlns:fx="http://javafx.com/fxml/1" >
<children >
<MenuBar />
<AnchorPane id="login" maxHeight="-1.0" maxWidth="-1.0" prefHeight="418.0" prefWidth="626.0" style="-fx-background-color: #74AFAD#74AFAD;" VBox.vgrow="ALWAYS">
<children>
<AnchorPane id="login2" layoutX="325.0" layoutY="40.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="291.0" prefWidth="313.0" style="-fx-background-color: #D9853B#D9853B;">
<children>
<TextField fx:id="loginField" alignment="TOP_CENTER" layoutX="75.0" layoutY="95.0" promptText="Login">
<effect>
<InnerShadow />
</effect>
</TextField>
<PasswordField fx:id="passwordField" alignment="TOP_CENTER" layoutX="75.0" layoutY="159.0" promptText="Password">
<effect>
<InnerShadow blurType="TWO_PASS_BOX" />
</effect>
</PasswordField>
<Button fx:id="loginButton" layoutX="177.0" layoutY="247.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="65.0" style="-fx-background-color: #74AFAD#74AFAD #74AFAD#74AFAD;" text="Login" />
<Button fx:id="signUp" onAction="#signUp" layoutX="75.0" layoutY="247.0" mnemonicParsing="false" style="-fx-background-color: #74AFAD#74AFAD #74AFAD#74AFAD;" text="Sign up" />
<Button fx:id="signUpAdmin" layoutX="242.0" layoutY="8.0" mnemonicParsing="false" style="-fx-background-color: #74AFAD#74AFAD #74AFAD#74AFAD;" text="Admin" />
</children>
</AnchorPane>
<Line endX="100.0" fill="#ff2323" layoutX="360.0" layoutY="190.0" rotate="90.0" startX="-190.0" stroke="#e43030" strokeLineJoin="ROUND" strokeMiterLimit="0.0" />
<Text fill="#d9853b" layoutX="21.0" layoutY="202.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Welcome to Quiz app" wrappingWidth="284.21875">
<font>
<Font size="27.0" />
</font>
</Text>
</children>
</AnchorPane>
<AnchorPane />
</children>
</AnchorPane>
Ok, I managed to fix that.
The problem was in the Controller class. It tried to load FXML with
FXMLLoader.load(getClass().getResource("FXMLRegister.fxml"));
where it should be
FXMLLoader.load(getClass().getResource("FxmlRegister.fxml"));
Apparently IntelliJ did not catch that error so it was working while running by IntelliJ.
Currently i have TabPane with 3 active tabs. I have to manually switch between them, which isn't ideal. What i would like to do is replace the TabPane all together and have one scene inside the stage, which would then switch to next scene (From Tab1, to Tab2, to Tab3) upon press of a button.
It is important to maintain the set label text functionality.
Tab1
Tab2
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("../view/Main.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MainController.java
package controller;
import javafx.fxml.FXML;
import controller.tab.Tab1Controller;
import controller.tab.Tab2Controller;
import controller.tab.Tab3Controller;
public class MainController {
#FXML Tab1Controller tab1Controller;
#FXML Tab2Controller tab2Controller;
#FXML Tab3Controller tab3Controller;
public void initialize() {
tab1Controller.init(this);
tab2Controller.init(this);
tab3Controller.init(this);
}
public void setTab2LabelText(String text) {
tab3Controller.lbl3.setText(text);
tab2Controller.lbl2.setText(text);
}
}
Tab1Controller.java
package controller.tab;
import controller.MainController;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import java.io.IOException;
public class Tab1Controller {
private MainController main;
#FXML public Label lbl1;
#FXML private Button btn1Send;
#FXML private void btn1SendClicked(ActionEvent event) throws IOException {
main.setTab2LabelText("abc");
}
public void init(MainController mainController) {
main = mainController;
}
}
Tab2Controller.java
package controller.tab;
import controller.MainController;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Tab2Controller {
private MainController main;
#FXML public Label lbl2;
public void init(MainController mainController) {
main = mainController;
}
}
Tab3Controller.java
package controller.tab;
import controller.MainController;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Tab3Controller {
private MainController main;
#FXML public Label lbl3;
public void init(MainController mainController) {
main = mainController;
}
}
Main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="432.0" prefWidth="443.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.MainController">
<children>
<TabPane prefHeight="299.0" prefWidth="309.0" tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<tabs>
<Tab closable="false" text="Tab 1">
<content>
<fx:include fx:id="tab1" source="tab/Tab1.fxml" />
</content></Tab>
<Tab closable="false" text="Tab 2">
<content>
<fx:include fx:id="tab2" source="tab/Tab2.fxml" />
</content></Tab>
<Tab closable="false" text="Tab 3">
<content>
<fx:include fx:id="tab3" source="tab/Tab3.fxml" />
</content></Tab>
</tabs>
</TabPane>
</children>
</AnchorPane>
Tab2.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="206.0" prefWidth="226.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.tab.Tab1Controller">
<children>
<Button fx:id="btn1Send" layoutX="42.0" layoutY="74.0" mnemonicParsing="false" onAction="#btn1SendClicked" prefHeight="58.0" prefWidth="142.0" text="Send to Tab2 & Tab3" />
</children>
</AnchorPane>
Tab2.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="206.0" prefWidth="226.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.tab.Tab2Controller">
<children>
<Label fx:id="lbl2" alignment="CENTER" layoutX="37.0" layoutY="46.0" prefHeight="17.0" prefWidth="152.0" text="Default Tab2 text" />
</children>
</AnchorPane>
Tab3.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="206.0" prefWidth="226.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.tab.Tab3Controller">
<children>
<Label fx:id="lbl3" alignment="CENTER" layoutX="37.0" layoutY="46.0" prefHeight="17.0" prefWidth="152.0" text="Default Tab3 text" />
</children>
</AnchorPane>
Here is a example
fxml
create 3 panes, with its own button and label
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="380.0" prefWidth="387.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ask.FXMLDocumentController">
<children>
<Pane fx:id="p3" prefHeight="380.0" prefWidth="387.0" visible="false">
<children>
<Label layoutX="184.0" layoutY="181.0" text="p3" />
<Button fx:id="p3previous" layoutX="152.0" layoutY="225.0" mnemonicParsing="false" text="previous" />
</children>
</Pane>
<Pane fx:id="p2" prefHeight="380.0" prefWidth="387.0" visible="false">
<children>
<Label layoutX="184.0" layoutY="181.0" text="p2" />
<Button fx:id="p2previous" layoutX="78.0" layoutY="255.0" mnemonicParsing="false" text="previous" />
<Button fx:id="p2next" layoutX="239.0" layoutY="255.0" mnemonicParsing="false" text="next" />
</children>
</Pane>
<Pane fx:id="p1" prefHeight="380.0" prefWidth="387.0">
<children>
<Button fx:id="p1next" layoutX="167.0" layoutY="210.0" mnemonicParsing="false" text="next" />
<Label layoutX="184.0" layoutY="181.0" text="p1" />
</children>
</Pane>
</children>
</AnchorPane>
controller
add button action event, use setVisible(boolean) to control which pane should show.
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
public class FXMLDocumentController implements Initializable {
#FXML Pane p1;
#FXML Pane p2;
#FXML Pane p3;
#FXML Button p1next;
#FXML Button p2next;
#FXML Button p2previous;
#FXML Button p3previous;
public void initialize(URL url, ResourceBundle rb)
{
p1next.setOnAction(e->{ p1.setVisible(false); p2.setVisible(true); });
p2next.setOnAction(e->{ p2.setVisible(false); p3.setVisible(true); });
p2previous.setOnAction(e->{ p2.setVisible(false); p1.setVisible(true); });
p3previous.setOnAction(e->{ p3.setVisible(false); p2.setVisible(true); });
}
}
You do not need to add all the content you create in a fxml file to the scene. the <fx:define> tag can be used to create Node that are not part of the object scene (yet). Use a suitable Parent that allows you to proper display the content.
Example:
<StackPane fx:id="container" prefHeight="432.0" prefWidth="443.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.MainController">
<children>
<fx:include fx:id="tab1" source="tab/Tab1.fxml" />
<fx:define>
<fx:include fx:id="tab2" source="tab/Tab2.fxml" />
<fx:include fx:id="tab3" source="tab/Tab3.fxml" />
</fx:define>
</children>
</StackPane>
public class MainController {
#FXML private Tab1Controller tab1Controller;
#FXML private Tab2Controller tab2Controller;
#FXML private Tab3Controller tab3Controller;
#FXML private Node tab1;
#FXML private Node tab2;
#FXML private Node tab3;
#FXML private StackPane container;
public void initialize() {
tab1Controller.init(this);
tab2Controller.init(this);
tab3Controller.init(this);
}
public void setTab2LabelText(String text) {
tab3Controller.lbl3.setText(text);
tab2Controller.lbl2.setText(text);
}
public void toTab2() {
container.getChildren().setAll(tab2);
}
}
I'm adding tabs dynamically from fxml files by clicking a button. Is there a way to check to see if that tab is already opened in TabPane in JavaFX and switch to that Tab instead of adding the same Tab to TabPane.
Here is my controller class:-
package application;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.AnchorPane;
public class MainController implements Initializable{
#FXML
public TabPane myTabPane ;
public AnchorPane myAnchorPane;
#FXML
public Button btnTab1 = new Button();
#FXML
public Button btnTab2 = new Button();
#FXML
public Button btnTab3 = new Button();
#Override
public void initialize(URL location, ResourceBundle resources) {
btnTab1.setOnAction(e -> {
//System.out.println("Clicked");
try {
Tab myNewTab = FXMLLoader.load(this.getClass().getResource("MyTestTab.fxml"));
myTabPane.getTabs().add(myNewTab);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
});
btnTab2.setOnAction(e -> {
try {
Tab myNewTab = FXMLLoader.load(this.getClass().getResource("MyTestTab2.fxml"));
myTabPane.getTabs().add(myNewTab);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
});
}
}
Here is my Main FXML File:-
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<top>
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<items>
<Button fx:id="btnTab1" mnemonicParsing="false" text="Tab1" />
<Button fx:id="btnTab2" mnemonicParsing="false" text="Tab2" />
<Button fx:id="btnTab3" mnemonicParsing="false" text="Tab3" />
</items>
</ToolBar>
</top>
<center>
<SplitPane dividerPositions="0.29797979797979796" prefHeight="160.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<items>
<AnchorPane fx:id="myTabAnchPane" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<TabPane fx:id="myTabPane" layoutX="-12.0" layoutY="34.0" prefHeight="358.0" prefWidth="175.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children></AnchorPane>
<AnchorPane fx:id="myAnchorPane" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
</items>
</SplitPane>
</center>
</BorderPane>
And here is my FXML File for one of the Tab (Tab2):-
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<Tab fx:id="tab2" text="My Profile" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
<content>
<VBox prefHeight="200.0" prefWidth="100.0">
<children>
<HBox spacing="10.0">
<children>
<Label text="Bank Statement File" />
<TextField prefHeight="25.0" prefWidth="288.0" />
<Button mnemonicParsing="false" text="Browse" />
</children>
<VBox.margin>
<Insets left="20.0" top="20.0" />
</VBox.margin>
</HBox>
<HBox VBox.vgrow="ALWAYS">
<children>
<Pane prefHeight="200.0" prefWidth="135.0" />
<TextArea HBox.hgrow="ALWAYS" />
<Pane prefHeight="200.0" prefWidth="200.0" />
</children>
<padding>
<Insets top="50.0" />
</padding>
</HBox>
<Pane prefHeight="64.0" prefWidth="600.0" />
</children>
</VBox>
</content></Tab>
Just have the controller track which FXML files you have opened (I refactored the code slightly to get rid of all the repetition).
As an aside, never, ever initialize #FXML-annotated fields. I.e. never do #FXML private Button btnTab1 = new Button();.
package application;
import java.io.IOException;
import java.net.URL;
import java.util.Map ;
import java.util.HashMap ;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.AnchorPane;
public class MainController implements Initializable{
private Map<String, Tab> openTabs = new HashMap<>();
#FXML
private TabPane myTabPane ;
#FXML
private AnchorPane myAnchorPane;
#FXML
private Button btnTab1 ;
#FXML
private Button btnTab2 ;
#FXML
private Button btnTab3 ;
#Override
public void initialize(URL location, ResourceBundle resources) {
btnTab1.setOnAction(e -> openTab("MyTestTab.fxml"));
btnTab2.setOnAction(e -> openTab("MyTestTab2.fxml"));
}
private void openTab(String fxmlFile) {
if (openTabs.containsKey(fxmlFile)) {
myTabPane.getSelectionModel().select(openTabs.get(fxmlFile));
} else {
try {
Tab myNewTab = FXMLLoader.load(this.getClass().getResource(fxmlFile));
myTabPane.getTabs().add(myNewTab);
openTabs.put(fxmlFile, myNewTab);
myNewTab.setOnClosed(e -> openTabs.remove(fxmlFile));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
My application functions as intended, but there are display issues when you run it. When it runs, the java window pops up with the correct window size, but the controls within the window are huge for some reason. The only way I've been able to work around this and get them to display at their normal size is if I drag the window over to one of my other monitors, drop it (at which point the oversized controls shift around a bit), and then drag it back to the laptop monitor. Then the controls shrink back and display properly.
If I drag it back over to one of my other monitors, however, the controls blow up again.
I'm using IntelliJ 14 and Scene Builder 2.0 running on MacOSX 10.10.3.
FXML File:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="332.0" prefWidth="404.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="408.0" minWidth="10.0" prefWidth="408.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button id="btnTest" contentDisplay="CENTER" mnemonicParsing="false" onAction="#foo" prefHeight="74.0" prefWidth="275.0" text="Generate Encryption Key" GridPane.halignment="CENTER">
<GridPane.margin>
<Insets />
</GridPane.margin>
</Button>
<Label fx:id="lblTop" alignment="TOP_CENTER" contentDisplay="TOP" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
<GridPane.margin>
<Insets top="35.0" />
</GridPane.margin>
</Label>
<TextField fx:id="txtKey" alignment="CENTER" editable="false" GridPane.rowIndex="1" />
</children>
</GridPane>
Controller:
package sample;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import java.beans.EventHandler;
import java.util.ResourceBundle;
import java.util.Random;
import java.awt.*;
import java.net.URL;
public class Controller implements Initializable{
#FXML
private TextField txtKey;
#FXML
private Label lblTop;
#FXML
private Button btnButton;
Random rnd = new Random();
private char[] pool = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9'};
#Override
public void initialize(URL fxmlFileLocation, ResourceBundle resources){
assert btnButton != null : "fx:id=\"txtKey\" was not injected: check your FXML file 'sample.fxml'.";
}
public void foo (ActionEvent actionEvent){
String key = "";
for (int i = 0; i < 32; i++){
key = key + pool[rnd.nextInt(36)];
}
lblTop.setText("Your Encryption Key is:");
txtKey.setText(key);
}
}
Main:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Encryption Key Generator");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
UPADATE:
This problem seems to be an IntelliJ issue. I decided to try to build my application and after I ran the JAR, it worked perfectly. The application's logic functioned and the aforementioned display problems weren't occurring anymore.
It does do a little resizing jitter when moving between monitors, but it never displays the elements incorrectly.