Replace TabPane with separate scenes (JAVAFX) - java

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

Related

Focus lost on textfields inside custom menu item

I have a button which display a menu containing only one custom menu item. This menu item contains a textfield and a button. Here is my code:
helloApplication.java:
package com.example.demo;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
helloController.java:
package com.example.demo;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class HelloController {
#FXML
private Label welcomeText;
#FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
}
}
hello-view.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CustomMenuItem?>
<?import javafx.scene.control.MenuButton?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/18" fx:controller="com.example.demo.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
<children>
<MenuButton mnemonicParsing="false" text="MenuButton">
<items>
<CustomMenuItem hideOnClick="false" mnemonicParsing="false" text="Unspecified Action">
<content>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<TextField />
<Button mnemonicParsing="false" text="Button" />
</children>
</HBox>
</content>
</CustomMenuItem>
</items>
</MenuButton>
</children>
</VBox>
This menu I want this menu item to be unselectable, just a pane poping to display the textfield. It is already not hidden when clicked. The problem is that when i enter some text in the textfield, and move the mouse cursor, the focus in the text field is lost because the menu item takes the focus:
How can I prevent the menu item to react and let the text field keep the focus ? Is a custom menu item a good solution if I want only one menu item containing components ?

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

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>

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

How to create tabs dynamically in JavaFX using FXML?

How do you create a new tab using JavaFX/FXML? I've created a tabpane in my FXML but I want to click a button that causes a new tab to come up.
Here is my FXML:
<?import javafx.scene.effect.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="New..." onAction="#btnNew"/>
<MenuItem mnemonicParsing="false" text="Save..." />
<MenuItem mnemonicParsing="false" text="Save As..." />
<MenuItem mnemonicParsing="false" text="Open..." />
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About Blank" onAction="#btnAbout"/>
</items>
</Menu>
</menus>
</MenuBar>
</top>
<center>
<TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER" fx:id="tabPane">
<tabs>
<Tab text="Untitled Tab 1">
<content>
<AnchorPane>
<children>
<TextFlow maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" />
</children>
</AnchorPane>
</content>
</Tab>
</tabs>
</TabPane>
Here is my code that I'm trying right now in my controller:
package sample;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML
private Button btnAbout;
#FXML
private Button btnNew;
#FXML
private TabPane tabPane;
//tab array
int intTabs = 0;
Tab[] openTabs;
#FXML
private void btnNew(ActionEvent event){
try{
intTabs++;
openTabs = new Tab[intTabs];
//System.out.println(openTabs.length);
tabAdder(intTabs, openTabs);
}catch(Exception e){
e.printStackTrace();
}
}
private TabPane tabAdder(int tabNum, Tab[] tabs){
//System.out.println(tabNum);
tabPane.getTabs().add(tabs[tabNum]);
return tabPane;
}
#FXML
private void btnAbout(ActionEvent event){
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Blank: Information");
alert.setHeaderText(null);
alert.setContentText("This is Blank, a simple and soon to be elegant cross platform text editor.");
alert.showAndWait();
}
public void initialize(URL url, ResourceBundle rb){
//tabPane.getTabs().add
}
}
I'm not really sure what the problem is I've been going through a bunch of resources and I know how to add a regular tab.
I know that if I make a tab like this:
Tab fapTab = new Tab();
I can add it like this:
tabPane.getTabs().add(fapTab);
But I want to create them dynamically because I don't know how many tabs my user's would want. So my approach was to create an array of tabs that gets bigger whenever a user clicks "new" and then have a function that adds that new tab to the gui. It's not working though and I've tried several different approaches, adapting people's code to my own.
So my question is does anyone know what I'm doing wrong?
Here are some of those sources:
javafx open a new fxml file in new tab dynamically
http://javafx-albert-af77.blogspot.com/2012/06/tabs-fxml.html
https://www.youtube.com/watch?v=NhoiSMk3f5U
You have already given the answer: just create a new tab and add it to the tab pane:
Tab tab = new Tab();
tabPane.getTabs().add(tab);
Complete example:
AddTabsExample.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1"
fx:controller="AddTabsController">
<center>
<TabPane fx:id="tabPane" />
</center>
<bottom>
<HBox alignment="center">
<Button text="Add tab" onAction="#addTab" />
<Button text="List tabs" onAction="#listTabs" />
</HBox>
</bottom>
</BorderPane>
AddTabsController.java:
import javafx.fxml.FXML;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
public class AddTabsController {
#FXML
private TabPane tabPane ;
public void initialize() {
tabPane.getTabs().add(new Tab("Tab 1"));
}
#FXML
private void addTab() {
int numTabs = tabPane.getTabs().size();
Tab tab = new Tab("Tab "+(numTabs+1));
tabPane.getTabs().add(tab);
}
#FXML
private void listTabs() {
tabPane.getTabs().forEach(tab -> System.out.println(tab.getText()));
System.out.println();
}
}
Application (AddTabsExample.java):
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class AddTabsExample extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = FXMLLoader.load(getClass().getResource("AddTabsExample.fxml"));
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Use this instead. You have to override initialize() method:
#Override
public void initialize(URL location, ResourceBundle resources) {
tabPane.getTabs().add(new Tab("Tab 1"));
}
Not this:
public void initialize() {
tabPane.getTabs().add(new Tab("Tab 1"));
}

Categories