Я ни в коем случае не эксперт по JavaFX, но я смог придумать это решение.
Это MCVE, поэтому вы можете полностью воссоздать этот пример приложения и запустить его, чтобы увидеть его в действии..
В этом примере используется AnchorPane
для многократно используемого узла FXML, но вы можете изменить его для использования Tab
.Я считаю, что повторное использование самого контента немного проще.
Я использую простой Interface
для определения наших контроллеров вкладок;это позволяет нам написать один метод, который устанавливает содержимое и отдельный контроллер для каждого Tab
.
Main.java:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
try {
// Load the main layout
FXMLLoader loader = new FXMLLoader(getClass().getResource("MainLayout.fxml"));
// Show the Stage
primaryStage.setWidth(700);
primaryStage.setHeight(500);
primaryStage.setScene(new Scene(loader.load()));
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FXML документы:
MainLayout.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/9.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.reusableTabs.MainController">
<VBox alignment="TOP_CENTER" prefHeight="400.0" prefWidth="600.0" spacing="10.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
<Label style="-fx-font-size: 150%; -fx-font-weight: bold;" text="Reusable Tab Content Sample"/>
<Separator prefWidth="200.0"/>
<TabPane fx:id="tabPane" prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE"
VBox.vgrow="ALWAYS">
<Tab fx:id="tab1" text="Tab #1"/>
<Tab fx:id="tab2" text="Tab #2"/>
<Tab fx:id="tab3" text="Tab #3"/>
</TabPane>
</VBox>
</AnchorPane>
TabContent.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
prefWidth="600.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
<VBox alignment="CENTER" prefHeight="400.0" prefWidth="600.0" spacing="20.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<Label text="Look - reusable Tab contents!"/>
<Button fx:id="btnTestController" mnemonicParsing="false" text="Test Controller"/>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
</padding>
</VBox>
</AnchorPane>
Контроллеры:
MainController.java:
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import java.io.IOException;
public class MainController {
@FXML
private TabPane tabPane;
@FXML
private Tab tab1;
@FXML
private Tab tab2;
@FXML
private Tab tab3;
@FXML
private void initialize() {
// Our TabPane has 3 Tabs. Let's populate them with content, reusing our TabContent.fxml file
setTabContent(tab1, new Tab1Controller());
setTabContent(tab2, new Tab2Controller());
setTabContent(tab3, new Tab3Controller());
}
/**
* Sets the content of Tab to the TabContent.fxml document
*
* @param tab The Tab whose content we wish to set
* @param controller The controller for this particular Tab's content
*/
private void setTabContent(Tab tab, TabController controller) {
// Load our TabContent.fxml file and set it as the content of the Tab that was passed to this method
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("TabContent.fxml"));
// Set the controller for this specific tab
loader.setController(controller);
// Set the content of the passed Tab to this FXML content
tab.setContent(loader.load());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Tab1Controller:
import javafx.fxml.FXML;
import javafx.scene.control.Button;
public class Tab1Controller implements TabController {
@FXML
private Button btnTestController;
@FXML
private void initialize() {
// Set our button to print out which controller is being used
btnTestController.setOnAction(event -> System.out.println("Hello, from Controller #1!"));
}
}
Просто дублируйте этот контроллер еще два раза для Tab2Controller.java
и `Tab3Controller.java
Результат:
При нажатии на кнопку Test Controller
будет напечатано Hello, from Controller #2!
, подтверждая, что содержимое каждой вкладкиуправляется собственным контроллером.