JavaFX 8 - 每个选项卡带有单独的 FXML 和控制器的选项卡和选项卡

Posted

技术标签:

【中文标题】JavaFX 8 - 每个选项卡带有单独的 FXML 和控制器的选项卡和选项卡【英文标题】:JavaFX 8 - Tabpanes and tabs with separate FXML and controllers for each tab 【发布时间】:2017-01-02 23:51:03 【问题描述】:

我希望得到一些关于在选项卡窗格中为每个选项卡设置 fx:include 语句的答案。我已经轻松地设法让内容显示出来,但是无论我如何构造它,相关控制器类的引用方法只会给我一个空指针引用异常。包含的 FXML 布局的控制器既没有构造函数也没有 initialize() 方法,是否需要它们?我尝试了一些不同的方法,但总是遇到相同的异常。

我所做的只是在选项卡窗格中添加一个更改侦听器,当按下选项卡时,我想使用从全局可访问的数组列表中获取的一些值填充一些文本字段。注意:arraylist 不是问题,使用主控制器执行此操作可以正常工作。

我将很快添加一个代码示例,但现在不能。如果您需要更多信息,请告诉我,否则我将在今天晚些时候发布代码。

*编辑,这是我的代码示例,取自 *** 上的另一个线程。 JavaFX TabPane - One controller for each tab

TestApp.java:

public class TestApp extends Application 
    @Override
    public void start(Stage primaryStage) throws Exception 
        Scene scene = new Scene(new StackPane());

        FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/MainTestWindow.fxml"));
        scene.setRoot(loader.load());
        MainTestController controller = loader.getController();
        controller.init();

        primaryStage.setScene(scene);
        primaryStage.show();
    

    public static void main(String[] args) 
        launch(args);
    

主控制器,我想引用子控制器。

public class MainTestController 

    @FXML private TabPane tabPane;
    // Inject tab content.
    @FXML private Tab fooTabPage;
    // Inject controller
    @FXML private FooTabController fooTabPageController;

    // Inject tab content.
    @FXML private Tab barTabPage;
    // Inject controller
    @FXML private BarTabController barTabPageController;

    public void init() 
        tabPane.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends Tab> observable,
                                                                        Tab oldValue, Tab newValue) -> 
            if (newValue == barTabPage) 
                System.out.println("Bar Tab page");
                barTabPageController.handleButton();
             else if (newValue == fooTabPage) 
                System.out.println("Foo Tab page");
                fooTabPageController.handleButton();
            
        );
    

主视图的.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.Tab?>

<TabPane fx:id="tabPane" fx:controller="controller.MainTestController" xmlns="http://javafx.com/javafx/8.0.40"
         xmlns:fx="http://www.w3.org/2001/XInclude">
    <tabs>
        <Tab fx:id="fooTabPage" text="FooTab">
            <fx:include source="fooTabPage.fxml"/>
        </Tab>
        <Tab fx:id="barTabPage" text="BarTab">
            <fx:include source="barTabPage.fxml"/>
        </Tab>
    </tabs>
</TabPane>

FooTab:

public class FooTabController 
    @FXML private Label lblText;

    public void handleButton() 
        lblText.setText("Byebye!");
    

FooTab 的 .fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>

<?import javafx.scene.control.Button?>
<VBox xmlns:fx="http://www.w3.org/2001/XInclude" fx:controller="controller.FooTabController">
    <Label fx:id="lblText" text="Helllo"/>
    <Button fx:id="btnFooTab" onAction="#handleButton" text="Change text"/>
</VBox>

条形标签:

public class BarTabController 
    @FXML private Label lblText;

    public void handleButton() 
        lblText.setText("Byebye!");
    

BarTab 的 .fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>

<?import javafx.scene.control.Button?>
<VBox xmlns:fx="http://www.w3.org/2001/XInclude" fx:controller="controller.BarTabController">
    <Label fx:id="lblText" text="Helllo" />
    <Button fx:id="btnBarTab" onAction="#handleButton" text="Change text"/>
</VBox>

上面的 FooTab 和 BarTab 的 onAction 都适用于它们各自的按钮。当这个方法(handleButton)是来自主控制器的引用时,那是我得到一个异常的时候。

【问题讨论】:

“无论我如何构造它,关联控制器类的引用方法只会给我一个空指针引用异常”。你能edit你的问题来展示你的尝试吗? 我已经更新了我目前拥有的测试代码,请指教 【参考方案1】:

要为包含的 FMXL 文件注入控制器,您需要 &lt;fx:include&gt; 元素上的 fx:id 属性。控制器将被注入到"Controller" 附加到fx:id 值的字段中。

如果您也想注入实际的Tab,则需要一个单独的fx:id

所以:

<tabs>
    <Tab fx:id="fooTab" text="FooTab">
        <fx:include fx:id="fooTabPage" source="fooTabPage.fxml"/>
    </Tab>
    <Tab fx:id="barTab" text="BarTab">
        <fx:include fx:id="barTabPage" source="barTabPage.fxml"/>
    </Tab>
</tabs>

@FXML private TabPane tabPane;
// Inject tab content.
@FXML private Tab fooTab;
// Inject controller
@FXML private FooTabController fooTabPageController;

// Inject tab content.
@FXML private Tab barTab;
// Inject controller
@FXML private BarTabController barTabPageController;

【讨论】:

非常感谢,它立刻就发挥了作用!【参考方案2】:

结论:示例模板“注入子控制器” - 上面的例子对我最终理解注入机制很有帮助。谢谢。 - 我重新编写了代码,使其更加透明和清晰 - 下面的代码是完整的和工作的

Tab1fooController.java

public class TabPaneRootController 

    @FXML private TabPane tabPane;

    //###################################Inject part#############################################
    // Inject tab content
    @FXML private Tab tab1_foo; //from TabPaneRootView.fxml: <Tab fx:id="tab1_foo" ...>
    @FXML private Tab tab2_bar; //from TabPaneRootView.fxml: <Tab fx:id="tab2_bar" ...>

    // Inject tab controller
    @FXML private Tab1fooController xxx_tab1foo_xxxController;//TabPaneRootView.fxml_include_fx:id="xxx_tab1foo_xxx" + "Controller"
    @FXML private Tab2barController xxx_tab2bar_xxxController;//TabPaneRootView.fxml_include_fx:id="xxx_tab2bar_xxx" + "Controller"
   //###########################################################################################

        public void init() 
               tabPane.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends Tab> observable,
                                                                    Tab oldValue, Tab newValue) ->                                                                                                     
            if (newValue == tab2_bar) 
                System.out.println("- 2.Tab bar -");
                System.out.println("xxx_tab2bar_xxxController=" + xxx_tab2bar_xxxController); //if =null => inject problem 
                xxx_tab2bar_xxxController.handleTab2ButtonBar();
             else if (newValue == tab1_foo) 
                System.out.println("- 1.Tab foo -");
                System.out.println("xxx_tab1foo_xxxController=" + xxx_tab1foo_xxxController); //if =null => inject problem
                xxx_tab1foo_xxxController.handleTab1ButtonFoo();
             else 
                System.out.println("- another Tab -");
            
        );
    

tabPaneRootView.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Tab?
<?import javafx.scene.control.TabPane?>

<TabPane fx:id="tabPane" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1"  fx:controller="controller.TabPaneRootController">
<tabs>
    <Tab fx:id="tab1_foo" text="myTab1foo">
        <fx:include fx:id="xxx_tab1foo_xxx" source="tab1fooView.fxml" />
    </Tab>
    <Tab fx:id="tab2_bar" text="myTab2bar">
        <fx:include fx:id="xxx_tab2bar_xxx" source="tab2barView.fxml" />
    </Tab>
</tabs>

Tab1fooController.java

public class Tab1fooController 
    @FXML private Label tab1_label_showText;

    public void handleTab1ButtonFoo() 
        if( tab1_label_showText.getText().equals("tab1 aaa") ) 
            tab1_label_showText.setText("tab1 iii");
         else 
            tab1_label_showText.setText("tab1 aaa");
        
    

tab1fooView.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.Tab1fooController">
    <Label fx:id="tab1_label_showText" text="Tab1_start" />
    <Button fx:id="tab1_button_foo" onAction="#handleTab1ButtonFoo" text="tab1_button_foo" />
</VBox>

Tab2barController.java

public class Tab2barController 
    @FXML private Label tab2_label_showText;

    public void handleTab2ButtonBar()              
        if( tab2_label_showText.getText().equals("tab2 bbb") ) 
            tab2_label_showText.setText("tab2 jjj");
         else 
            tab2_label_showText.setText("tab2 bbb");
        
    

tab2barView.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111" fx:controller="controller.Tab2barController">
    <Label fx:id="tab2_label_showText" text="Tab2_start" />
    <Button fx:id="tab2_button_bar" onAction="#handleTab2ButtonBar" text="tab2_button_bar" />
</VBox>

TestApp.java

public class TestApp extends Application 
    @Override
    public void start(Stage primaryStage) throws Exception 
        Scene scene = new Scene(new StackPane());

        FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/tabPaneRootView.fxml"));
        scene.setRoot(loader.load());
        TabPaneRootController controller = loader.getController();
        controller.init();

        primaryStage.setScene(scene);
        primaryStage.setTitle("Inject TabController");
        primaryStage.show();
    

    public static void main(String[] args) 
        launch(args);
    

- Directory structure- Example started

【讨论】:

以上是关于JavaFX 8 - 每个选项卡带有单独的 FXML 和控制器的选项卡和选项卡的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX TabPane:如何设置选定的选项卡

带有 StackPane 和自定义控件的 Javafx TabPane

使用 Fragments 为 Android 中的每个选项卡单独返回堆栈

每个选项卡都有单独的控制器/视图?

更改 JavaFX 选项卡顺序时出现 NullPointerException

如何向JavaFX选项卡添加多个选项卡关闭事件处理程序?