SceneBuilder cannot create instance of custom component with given set of properties - java

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!

Related

View Injection in FXML

#Override
public void start(Stage stage) throws Exception {
BorderPane root = FXMLLoader.load(getClass().getClassLoader().getResource("mainView.fxml"));
When i run this it doesnt show the injected views
Im building a new application with JavaFX for the main page im using 3 views build by 3 fxml files
each view has its controller. for the main page i want to inject the three fxml files in a mainView.fxml via fx:include the mainView.fxml has also a controller how can i do that?
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>
<fx:root alignment="CENTER_LEFT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="93.0" prefWidth="600.0" style="-fx-min-width: 800; -fx-min-height: 100; -fx-spacing: 30;" type="javafx.scene.layout.HBox" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label fx:id="title" alignment="CENTER" prefHeight="91.0" prefWidth="179.0" style="-fx-label-padding: 20; -fx-line-spacing: 20;" text="News" textFill="#00a4f2">
<font>
<Font name="Arial Black" size="36.0" />
</font>
<opaqueInsets>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</opaqueInsets></Label>
<Button fx:id="refresh" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" style="-fx-alignment: CENTER; -fx-background-color: #66a6ff;" text="Button" textAlignment="CENTER">
<opaqueInsets>
<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
</opaqueInsets></Button>
<Button fx:id="stat" alignment="CENTER" mnemonicParsing="false" style="-fx-background-color: #feada6;" text="Button">
<opaqueInsets>
<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
</opaqueInsets></Button>
</children>
</fx:root>
This is per example the topView.fxml
package ch.bfh.spacenews;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class TopBarController extends HBox {
#FXML
public Label title;
#FXML
public Button refresh;
#FXML
public Button stat;
TopBarController(){
FXMLLoader load = new FXMLLoader(getClass().getClassLoader().getResource("topView.fxml"));
load.setRoot(this);
load.setController(this);
try {
System.out.println("TopBarController");
load.load();
}catch(IOException e) {
e.printStackTrace();
}
}
}
This is the corresponding controller of the topView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<BorderPane fx:controller="ch.bfh.spacenews.mainController" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<top>
<fx:include fx:id="topBar" source="topView.fxml"/>
</top>
<center>
<fx:include fx:id="article" source="sample.fxml"/>
</center>
<right>
<fx:include fx:id="seacrh" source="searchView.fxml"/>
</right>
</BorderPane>
This is where i want to inject the topView.fxml
package ch.bfh.spacenews;
import javafx.fxml.FXML;
public class mainController {
#FXML
TopBarController topBarController;
#FXML
ArticleController articleController;
#FXML
SearchController searchController;
#FXML
public void initialize() {
}
}
And this is the Controller of the mainView.fxml where i want to inject the topView.fxml
In the FXML custom component pattern the controller classes also serve as the wrapper for the view, by subclassing an appropriate Node subclass. This means you can just instantiate them directly in the FXML via the FXMLLoader. For example, the <TopBarController> element instructs the FXMLLoader to instantiate the TopBarController by calling its no-argument constructor. That constructor, as per your code, loads topView.fxml, etc. There is no need to use <fx:include>, which is an instruction for the FXMLLoader to load another FXML file, since you already have code in your TopBarController to do that.
So your main view FXML file should look like:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import ch.bfh.spacenews.TopBarController?>
<!-- other imports... -->
<BorderPane fx:controller="ch.bfh.spacenews.MainController" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<top>
<TopBarController fx:id="topBar" />
</top>
<center>
<ArticleController fx:id="article" />
</center>
<right>
<SearchController fx:id="search" />
</right>
</BorderPane>
And the corresponding controller is
package ch.bfh.spacenews;
import javafx.fxml.FXML;
public class MainController {
#FXML
TopBarController topBar;
#FXML
ArticleController article;
#FXML
SearchController search;
#FXML
public void initialize() {
}
}
Note that it's really critical to follow standard naming conventions here. The FXMLLoader explicitly relies on the case of an element to determine if it's referring to a class name or a property name.
Main class : Launch.java
public class Launch extends Application {
#Override
public void start(Stage stage) throws Exception {
Controller.show(stage);
}
public static void main(String[] args) {
launch(args);
}
}
You can add view from controller :
public class Controller {
public BorderPane mainPane; // fx:id of your pane
public static Stage current;
public static void show(Stage stage) {
this.current = stage;
Scene scene = null;
BorderPane pane = null;
try {
scene = new Scene(FXMLLoader.load(getClass().getResource("mainView.fxml")));
pane = (BorderPane) scene.lookup("#mainPane");
// Add .fxml view to mainView
pane.setCenter(FXMLLoader.load(getClass().getResource("sample.fxml")));
pane.setTop(FXMLLoader.load(getClass().getResource("topView.fxml")));
pane.setRight(FXMLLoader.load(getClass().getResource("searchView.fxml")));
} catch (IOException e) {
e.printStackTrace();
}
stage.setTitle("MainView");
stage.setScene(scene);
stage.show();
}
}

JavaFX: Remove a child from Vbox

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);
}
}

Creating PieChart, meaby Array declaration don't work

I'm trying to make a program for generating a PieChart. At the first Window
you can write a name (X) and the percentage (Y). When you press Insert, this information will be written in two arrays one for X (XI) and the second for Y (YI).
When you press stop, the program writes this information in an observable list.
But it wasn't work, I think the problem is at the declaration of the arrays. But don't know the specific problem can anyone help me?
MainClass.java :
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainClass extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(FXMLLoader.load(getClass().getResource("FirstPage.fxml"))));
primaryStage.show();}}
Controller.java :
package application;
import java.io.IOException;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class Controller{
#FXML
private TextField XInput;
#FXML
private TextField YInput;
#FXML
private PieChart PieChart;
public String[] XI; // Array for X
public int [] YI; // Array for Y
public int atm = -1;
#FXML
void GetValue(ActionEvent event) { // Write in Arrays
atm++;
XI[atm] = XInput.getText();
YI[atm] = Integer.parseInt(YInput.getText());
}
#FXML
void Stop(ActionEvent event) throws IOException {
// Load new Stage
Stage primaryStage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("SecondPage").openStream());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
// Create ObservableList
ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList();
int i = 0;
while(i == atm){
pieChartData.add(new PieChart.Data(XI[i], YI[i]));
i++;
}
// Set PieChart
PieChart.setData(pieChartData);
}
}
FirstPage.fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="410.0" prefWidth="592.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<children>
<Button layoutX="108.0" layoutY="235.0" mnemonicParsing="false" onAction="#GetValue" prefHeight="40.0" prefWidth="376.0" style="-fx-background-color: #FFF; -fx-border-color: #000;" text="Insert" />
<TextField fx:id="YInput" alignment="CENTER" layoutX="108.0" layoutY="154.0" prefHeight="72.0" prefWidth="376.0" promptText="Y" style="-fx-background-color: #FFF; -fx-border-color: #000;">
<font>
<Font size="30.0" />
</font>
</TextField>
<TextField fx:id="XInput" alignment="CENTER" layoutX="108.0" layoutY="74.0" prefHeight="72.0" prefWidth="376.0" promptText="X" style="-fx-background-color: #FFF; -fx-border-color: #000;">
<font>
<Font size="30.0" />
</font>
</TextField>
<Button layoutX="108.0" layoutY="282.0" mnemonicParsing="false" onAction="#Stop" prefHeight="40.0" prefWidth="376.0" style="-fx-background-color: #FFF; -fx-border-color: #000;" text="Stop" />
</children>
</AnchorPane>
SecondPage :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.chart.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="571.0" prefWidth="744.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<PieChart fx:id="PieChart" layoutY="7.0" prefHeight="564.0" prefWidth="744.0" />
</children>
</AnchorPane>

Buttons do not work in executable JAR

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.

How to check if a tab is already opened in TabPane in JavaFX

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();
}
}
}
}

Categories