簡體   English   中英

定義控制器后,加載多個FXML文件失敗

[英]Loading multiple FXML-files fails when controllers are defined

我知道這個問題被問過很多次,但是我找不到適合我的解決方案(實際上,我什至看不到自己在做什么錯)。

基本思想是在需要時加載GUI組件。 因此,我用各種FXML文件構造了GUI,並實現了控制器類。 FXML文件和類都存儲在相同的程序包中,但這是每個組件的程序包。 只要我未在FXML文件(fx:controller)中定義控制器類,就將每個FXML文件加載並添加到GUI。 如果定義,我將得到一個LoadException。

為了更好地理解,這里是我的代碼(簡體):

Main.java:

package application;

import application.a.ControllerA;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Main extends Application
{
    // Button aus MainLayout.fxml
    @FXML
    private Button button;

    @Override
    public void start(Stage primaryStage)
    {
        try
        {
            BorderPane root = new BorderPane();

            Parent contentMain = FXMLLoader.load(getClass().getResource("MainLayout.fxml"));
            ControllerA contentA = new ControllerA(root);

            root.setTop(contentA.getContent());
            root.setCenter(contentMain);

            Scene scene = new Scene(root, 400, 400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

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

    // Event-Handler für den Button -> funktioniert!
    @FXML
    public void buttonClicked(ActionEvent e)
    {
        if (!button.getText().equals("NEW"))
        {
            button.setText("NEW");
        }
        else
        {
            button.setText("OLD");
        }
    }
}

此類也是以下布局的控制器(到目前為止,它可以正常工作):

MainLayout.fxml:

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.GridPane?>

<Pane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Main">
   <children>
      <Button fx:id="button" mnemonicParsing="false" onAction="#buttonClicked" text="Button" />
   </children>
</Pane>

application的子包(稱為a)中,您將找到以下內容:

ControllerA.java:

package application.a;

import java.net.URL;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;

public class ControllerA
{
    private Parent content;

    @FXML
    private Button buttonA;

    public ControllerA(BorderPane root)
    {
        String sceneFile = "A.fxml";
        URL url = null;
        try
        {
            url = getClass().getResource(sceneFile);
            content = FXMLLoader.load(url);
        }
        catch (Exception ex)
        {
                // TODO Auto-generated catch block
                e.printStackTrace();
        }
    }

    public Parent getContent()
    {
        return content;
    }

    @FXML
    public void clickedA(ActionEvent e)
    {
        buttonA.setText("Clicked already");
    }
}

A.fxml:

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.GridPane?>

<Pane 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="application.a.ControllerA">
   <children>
      <Button fx:id="buttonA" layoutX="274.0" layoutY="188.0" mnemonicParsing="false" onAction="#clickedA" text="A" />
   </children>
</Pane>

這就是所有出錯的地方:

javafx.fxml.LoadException: 
/Z:/BachelorArbeit/Projektdateien/Entwicklung/EclipseWorkspace/Sandbox/bin/application/a/A.fxml:8

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.access$700(FXMLLoader.java:103)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:932)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
    at application.a.ControllerA.<init>(ControllerA.java:26)
    at application.Main.start(Main.java:35)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(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$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.InstantiationException: application.a.ControllerA
    at java.lang.Class.newInstance(Class.java:427)
    at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:927)
    ... 23 more
Caused by: java.lang.NoSuchMethodException: application.a.ControllerA.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.newInstance(Class.java:412)
    ... 25 more

我試圖擺弄路徑字符串像

  • “./a/A.fxml”
  • “/application/a/A.fxml”
  • “A.fxml”
  • “一個/ A.fxml”
  • ...

但沒有任何效果。 如果有人可以阻止我解決這個問題,我會很放心。

我將向您展示一個最小的工作示例項目,並建議您調整其結構。

文件夾結構如下

在此處輸入圖片說明

這是主類包dynamic.content.javafx;。

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public final class Main extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));
        Scene scene = new Scene(root, 300, 200);

        stage.setTitle("FXML Welcome");
        stage.setScene(scene);
        stage.show();
    }

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

主控制器

package dynamic.content.javafx;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;

public final class MainController implements Initializable{

    @FXML
    private Button btnSwitch;

    @FXML
    private AnchorPane contentPane;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // TODO Auto-generated method stub
    }

    private boolean swtch = false;
    @FXML
    public void handleContentSwitch() throws IOException{
        Parent contentNode = null;

        if(swtch){
            System.out.println("loading content A");
            contentNode = FXMLLoader.load(getClass().getResource("./content/content_a.fxml"));          
        }else{
            System.out.println("loading content B");
            contentNode = FXMLLoader.load(getClass().getResource("./content/content_b.fxml"));
        }
        contentPane.getChildren().clear();
        contentPane.getChildren().add(contentNode);

        swtch = !swtch;
    }

}

主要的FXML

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>


<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dynamic.content.javafx.MainController">
   <children>
      <Button fx:id="btnSwitch" onAction="#handleContentSwitch" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" text="Switch Content" />
      <AnchorPane fx:id="contentPane" prefHeight="150.0" prefWidth="300.0" />
   </children>
</VBox>

假設我們擁有內容A和B,我們需要分別創建一個控制器和一個FXML。

package dynamic.content.javafx.content;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class ContentAController implements Initializable{

    @FXML
    private Label labl;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        labl.setText("Content A");
    }

}

現在對應的FXML

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dynamic.content.javafx.content.ContentAController">
   <children>
      <Label fx:id="labl" alignment="CENTER" contentDisplay="CENTER" text="Label" textAlignment="CENTER" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
   </children>
</AnchorPane>

這是第二個控制器

package dynamic.content.javafx.content;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class ContentBController implements Initializable{

    @FXML
    private Label labl;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        labl.setText("Content B");
    }

}

和第二個FXML

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>


<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dynamic.content.javafx.content.ContentBController">
   <children>
      <Label fx:id="labl" alignment="CENTER" contentDisplay="CENTER" text="Label" textAlignment="CENTER" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
   </children>
</AnchorPane>

如果您知道按下按鈕的內容將動態加載

如果您有任何疑問,請告訴我:-)

您正在混合使用FXML和控制器的兩種不同方式。 如果您的FXML文件具有fx:controller="SomeClass" ,則FXMLLoader將實例化該類並將該實例用作控制器:換句話說,它使FXML創建控制器。

另一方面,控制器的構造函數將加載FXML,因此,您還需要控制器創建在FXML中定義的UI。

發生異常的原因是,當FXMLLoader遇到fx:controller屬性時,它將調用指定類的無參數構造函數 :即,在這種情況下,它將嘗試調用new ControllerA() 由於沒有這樣的構造函數,因此會出現異常:

java.lang.NoSuchMethodException: application.a.ControllerA.<init>()

目前還不清楚控制器的構造函數使用BorderPane參數的目的是什么,因為您從未使用過它。 但是,即使您在此處具有適當的構造函數,也將引起另一個問題:加載FXML將調用控制器的構造函數,加載FXML,這將調用控制器的構造函數, StackOverflowException :您將得到StackOverflowException 如果要從控制器的構造函數加載FXML:

  1. 從FXML文件中刪除fx:controller屬性
  2. FXMLLoader上的控制器明確設置為當前控制器實例:

     public ControllerA(BorderPane root) { String sceneFile = "A.fxml"; URL url = null; try { url = getClass().getResource(sceneFile); FXMLLoader loader = new FXMLLoader(url); loader.setController(this); content = loader.load(); } catch (Exception ex) { // TODO Auto-generated catch block e.printStackTrace(); } } 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM