【问题标题】:How to get the controller of an included FXML?如何获取包含的 FXML 的控制器?
【发布时间】:2020-01-01 10:59:15
【问题描述】:

我围绕 JavaFXML 和 scenebuilder 构建了一个简单的两个选项卡应用程序。这些选项卡目前什么都不做,因为我在尝试加载它们时无法通过空指针异常。

java 和 fxml 文件排列在 Netbeans 项目中,如下所示:

Main Application:应用主类MainApp.java设置场景,声明如下:

package tabpane;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;


public class MainApp extends Application {

  private static Stage primaryStage;
  private AnchorPane rootLayout;  


  public MainApp() {}

  @Override
  public void start(Stage primaryStage) {
    primaryStage.initStyle(StageStyle.UNDECORATED);
    MainApp.primaryStage = primaryStage;
    MainApp.primaryStage.setTitle("Account Names");
    initRootLayout();
  }

  public void initRootLayout() {
    try {
      FXMLLoader loader = new FXMLLoader();
      loader.setLocation(MainApp.class.getResource("view/MainView.fxml"));
      rootLayout = (AnchorPane) loader.load(); 
      Scene scene = new Scene(rootLayout);
      primaryStage.setScene(scene);
      primaryStage.show();

    } catch (IOException e) {
        Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, e);
        System.out.println("MainApp: IOException in method: initRootLayout" + e.getMessage());
        System.exit(0);
    }
  }

  public Stage getPrimaryStage() {
    return primaryStage;
  }

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

MainController 类为应用程序声明两个选项卡,输入选项卡和帐户选项卡,以及用于关闭程序的退出按钮。这是主视图。 fxml 文件:

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.MainController">
   <children>
      <TabPane fx:id="tabPane" prefHeight="345.0" prefWidth="600.0" style="-fx-background-color: blue;" tabClosingPolicy="UNAVAILABLE">
        <tabs>
          <Tab fx:id="inputTab" closable="false" text="Input">
            <content>
                <fx:include source="InputView.fxml" />
            </content>
          </Tab>
          <Tab fx:id="detailTab" closable="false" text="Detail">
            <content>
               <fx:include source="DetailView.fxml" />
            </content>
          </Tab>
        </tabs>
      </TabPane>
      <Button fx:id="exitBtn" layoutX="529.0" layoutY="355.0" mnemonicParsing="false" onAction="#handleExitBtn" text="Exit" />
   </children>
</AnchorPane>

这是 MainController.java 中包含的主视图的 java 控制器类:

package tabpane1.view;

import java.net.URL;
import java.util.ResourceBundle;
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;

public class MainController implements Initializable    {
    @FXML public TabPane tabPane;
    @FXML public Tab inputTab;
    @FXML public Tab accountTab;
    @FXML private Button exitBtn;

    public DetailController detailController;      
    public InputController inputController;

    @Override
    public void initialize(URL location, ResourceBundle resources) { 
      detailController = new FXMLLoader(getClass().getResource("DetailView.fxml")).getController(); 
//      System.out.println(detailController.getClass());
      inputController = new FXMLLoader(getClass().getResource("InputView.fxml")).getController(); 
//      System.out.println(inputController.getClass());
    }

    @FXML private void handleExitBtn() {
        System.exit(0);
    }
}

这两个 println 语句只是试图获取对 detailController 和 inputController 这两个选项卡的引用来做一些事情。然而,当这些被取消注释并运行时,会输出以下转储:

MainApp: IOException in method: initRootLayout
SEVERE: null
file:/G:/J2EE/TabPane_1/dist/run1314937364/TabPane_1.jar!/tabpane1/view/MainView.fxml
javafx.fxml.LoadException: 

file:/G:/J2EE/TabPane_1/dist/run1314937364/TabPane_1.jar!/tabpane1/view/MainView.fxml

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
    at tabpane1.MainApp.initRootLayout(MainApp.java:43)
    at tabpane1.MainApp.start(MainApp.java:33)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
    at tabpane1.view.MainController.initialize(MainController.java:24)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
    ... 13 more

当 printlns 被注释掉时,结果如预期的那样,即标签窗格正确显示:

如前所述,细节和输入控制器是空的,但为了完整起见,它们如下:

InputView.fxml:

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

<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.InputController">

</AnchorPane>

控制器 InputController.java:

package tabpane1.view;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;

public class InputController implements Initializable   {

  @Override
  public void initialize(URL location, ResourceBundle resources) {}  
}

DetailView.fxml:

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

<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: transparent;" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tabpane1.view.DetailController" />

控制器DetailController.java:

package tabpane1.view;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;

public class InputController implements Initializable   {

  @Override
  public void initialize(URL location, ResourceBundle resources) {}  
}

【问题讨论】:

    标签: java javafx controller fxml


    【解决方案1】:

    this article 中描述了对嵌入式FXML 控制器的引用。

    你只需要给包含的资源一个fx:id

       <fx:include fx:id="IncludedView" source="InputView.fxml" />
    

    MainController 中获取对IncludedView 的引用:

    @FXML private Parent IncludedView;
    

    除了嵌入元素的变量名之外,只需附加单词 Controller 即可获取对其控制器的引用:

    @FXML private InputController IncludedViewController;
    

    就是这样。

    测试它:

    import java.net.URL;
    import java.util.ResourceBundle;
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.Parent;
    
    public class MainController implements Initializable    {
    
        @FXML private Parent IncludedView;
        @FXML private InputController IncludedViewController; // $IncludedView;+Controller
    
        @Override
        public void initialize(URL location, ResourceBundle resources) {
    
            System.out.println(IncludedView.getClass());
            System.out.println(IncludedViewController.getClass()    );
        }
    
        @FXML private void handleExitBtn() {
            System.exit(0);
        }
    }
    

    为了未来的读者,MRE


    import java.io.IOException;
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Scene;
    import javafx.scene.layout.Pane;
    import javafx.stage.Stage;
    
    public class GetEmbeddedController extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            try {
                Pane root = (Pane) FXMLLoader.load(getClass().getResource("MainView.fxml"));
                primaryStage.setScene(new Scene(root));
                primaryStage.show();
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            launch(null);
        }
    }
    

    MainView.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.layout.AnchorPane?>
    
    <AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1" 
      xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
       <children>  
          <fx:include fx:id="includedView" source="IncludedView.fxml" />         
          <Button layoutX="401.0" layoutY="354.0" onAction="#handleTestBtn" text="Test Included Controller" />
       </children>
    </AnchorPane>
    

    MainController.java

    import javafx.fxml.FXML;
    import javafx.scene.Parent;
    
    public class MainController{
    
        @FXML private Parent includedView; //not used in this demo
        /*
         * Get a reference to IncludedView controller simply by appending
         * the word Controller in addition to the variable name of the embedded
         * element:$IncludedView;+Controller
         */
        @FXML private IncludedViewController includedViewController;
    
        @FXML private void handleTestBtn() {
            includedViewController.showAlert();
        }
    }
    

    IncludedView.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.layout.AnchorPane?>
    <?import javafx.scene.text.Text?>
    
    <AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1" 
    xmlns:fx="http://javafx.com/fxml/1" fx:controller="IncludedViewController">
       <children>
          <Text layoutX="277.0" layoutY="196.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Included Fxml" />
       </children>
    </AnchorPane>
    

    IncludedViewController.java

    import javafx.scene.control.Alert;
    import javafx.scene.control.Alert.AlertType;
    
    public class IncludedViewController{
    
        void showAlert(){
            Alert alert = new Alert(AlertType.INFORMATION);
            alert.setContentText("Alert invoked by IncludedViewController ");
            alert.show();
        }
    }
    

    【讨论】:

    • 感谢@c0der 的解决方案,它运行良好。我省略了对嵌入式视图控制器的引用。您向我推荐的这篇文章非常有用,填补了我对 JavaFXML 理解的巨大空白。再次感谢。
    猜你喜欢
    • 2015-05-29
    • 1970-01-01
    • 1970-01-01
    • 2014-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多