【问题标题】:Javafx location is required (loading FXML file from another folder/package)需要 Javafx 位置(从另一个文件夹/包加载 FXML 文件)
【发布时间】:2015-02-01 14:45:51
【问题描述】:

我在将 Java 与 FXML 文件结合使用时遇到问题。

我现在搜索了几个小时,但找不到任何可以解决我问题的东西,所以我最后的希望是针对我的具体情况提出问题(我知道像 this one 和其他人这样的问题,但没有一个真正帮助我在这方面。

简单解释:我有一个 Eclipse Java 项目,我的(对这个问题很重要)类位于包 [项目名称]/src/measurements.gui 中。我的 FXML 文件位于 [Project Name]/resources 文件夹中。

加载 FXML 文件 ElementsProperties.java 的类如下所示:

import java.io.IOException;

import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;

public class ElementsProperties {

  public static void show(Window parent, String title) {

    ElementsProperties el = new ElementsProperties();
    BorderPane root = el.loadFXMLFile("resources/TestWindow.fxml");

    Stage dialog = new Stage();
    dialog.initOwner(parent);
    dialog.setTitle(title);
    dialog.initModality(Modality.WINDOW_MODAL);
    dialog.setScene(new Scene(root));
    dialog.show();
  }

  @SuppressWarnings({ "finally", "static-access" })
  private BorderPane loadFXMLFile(String filePath) {
    BorderPane borderPane = null;
    try {
      borderPane = new BorderPane();
      FXMLLoader fxmlLoader = new FXMLLoader();
      Parent content = fxmlLoader.load(getClass().getResource(filePath));
      borderPane.setCenter(content);
    }
    catch (IOException e) {
      e.printStackTrace();
      System.err.println("Couldn't find the specified file");
    }
    catch(Exception e){
      e.printStackTrace();
    }
    finally {
     return borderPane;
    }
}}

FXML 文件显示为带有以下简单行的对话框: ElementsProperties.show(parent, "TestWindow");

我的 FXML 文件(使用 JavaFXSceneBuilder 2.0 创建)如下所示: `

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

<?import javafx.scene.effect.*?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>

<VBox prefHeight="400.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
  <children>
    <MenuBar VBox.vgrow="NEVER">
      <menus>
        <Menu mnemonicParsing="false" text="File">
          <items>
            <MenuItem mnemonicParsing="false" text="New" />
            <MenuItem mnemonicParsing="false" text="Open…" />
            <Menu mnemonicParsing="false" text="Open Recent" />
            <SeparatorMenuItem mnemonicParsing="false" />
            <MenuItem mnemonicParsing="false" text="Close" />
            <MenuItem mnemonicParsing="false" text="Save" />
            <MenuItem mnemonicParsing="false" text="Save As…" />
            <MenuItem mnemonicParsing="false" text="Revert" />
            <SeparatorMenuItem mnemonicParsing="false" />
            <MenuItem mnemonicParsing="false" text="Preferences…" />
            <SeparatorMenuItem mnemonicParsing="false" />
            <MenuItem mnemonicParsing="false" text="Quit" />
          </items>
        </Menu>
        <Menu mnemonicParsing="false" text="Edit">
          <items>
            <MenuItem mnemonicParsing="false" text="Undo" />
            <MenuItem mnemonicParsing="false" text="Redo" />
            <SeparatorMenuItem mnemonicParsing="false" />
            <MenuItem mnemonicParsing="false" text="Cut" />
            <MenuItem mnemonicParsing="false" text="Copy" />
            <MenuItem mnemonicParsing="false" text="Paste" />
            <MenuItem mnemonicParsing="false" text="Delete" />
            <SeparatorMenuItem mnemonicParsing="false" />
            <MenuItem mnemonicParsing="false" text="Select All" />
            <MenuItem mnemonicParsing="false" text="Unselect All" />
          </items>
        </Menu>
        <Menu mnemonicParsing="false" text="Help">
          <items>
            <MenuItem mnemonicParsing="false" text="About MyHelloApp" />
          </items>
        </Menu>
      </menus>
    </MenuBar>
    <AnchorPane maxHeight="-1.0" maxWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS">
      <children>
        <Label alignment="CENTER" layoutX="155.0" layoutY="177.0" style="&#10;" text="Drag components from Library here…" textAlignment="CENTER" textFill="#9f9f9f" wrapText="false">
          <font>
            <Font size="18.0" />
          </font>
        </Label>
            <GridPane layoutX="-2.0" layoutY="-3.0" prefHeight="407.0" prefWidth="659.0">
              <columnConstraints>
                <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
              </columnConstraints>
              <rowConstraints>
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
              </rowConstraints>
               <children>
                  <HBox prefHeight="134.0" prefWidth="654.0" spacing="20.0" GridPane.rowIndex="1">
                     <children>
                        <Button mnemonicParsing="false" prefHeight="46.0" prefWidth="60.0" text="Click me" wrapText="true" />
                        <Button mnemonicParsing="false" prefHeight="47.0" prefWidth="73.0" text="No, click me" wrapText="true" />
                        <Button mnemonicParsing="false" prefHeight="46.0" prefWidth="120.0" text="Don't you dare click me" wrapText="true" />
                     </children>
                     <padding>
                        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
                     </padding>
                  </HBox>
                  <TextField promptText="Type something here..." GridPane.columnIndex="1" GridPane.rowIndex="1">
                     <GridPane.margin>
                        <Insets left="20.0" right="20.0" />
                     </GridPane.margin>
                  </TextField>
               </children>
            </GridPane>
      </children>
    </AnchorPane>
  </children>
</VBox>

这实际上就是所有需要的。如果我尝试运行程序并显示对话框,则会出现异常

java.lang.NullPointerException: Location is required.

给出。如果我将 FXML 文件移动到与主类相同的包中,即measurements.gui,并将 ELementsProperties.java 类的 show-method 中的 filePath 更改为"TestWindow.fxml",它一切正常,我看到创建的窗口在我的应用程序。但我想将 fxml 文件放在单独的资源文件夹中,以便于插入其他 fxml 文件。

我希望我能清楚地解释我的问题,你可以帮我解决这个问题。关于如何从与主类不同的包中加载 fxml 文件的任何想法? 顺便说一句,我已经尝试过的事情是:

  1. 将资源文件夹添加到类路径
  2. 将路径设置为“/resources/TestWindow.fxml”(使用正斜杠 一开始
  3. 使用getClass().getClassLoader().getResource(filePath)作为参数 对于 FXMLLoader 的加载方法

提前谢谢你。

【问题讨论】:

  • 我的回答可能会有所帮助:stackoverflow.com/questions/23975897/… ...如果没有,请提供一个完整的可验证示例,而不仅仅是您的问题的一部分。
  • 谢谢@Roland!该文件夹结构给了我所需的推动力。抱歉,如果我对我的问题不够清楚,我以为我已经写了关于这个问题需要知道的所有内容。

标签: java eclipse nullpointerexception javafx fxml


【解决方案1】:

感谢 Roland 提供链接。我尝试复制文件夹结构并提出以下内容(对于可能遇到相同问题的其他人):

我创建了一个测试项目 JavaFXTest。我在包 org.example.main 中有一个主类。包和往常一样,在项目的 src 文件夹中。

MyJavaFXDialog.java:

package org.example.main;

import java.io.IOException;

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

public class MyJavaFXDialog extends Application{

    public void start(Stage primaryStage) {

    MyJavaFXDialog javaFx = new MyJavaFXDialog();
    BorderPane root = javaFx.loadFXMLFile("/resources/TestWindow.fxml");

    Stage dialog = new Stage();
    dialog.initModality(Modality.WINDOW_MODAL);
    dialog.setScene(new Scene(root));
    dialog.show();
    }

    @SuppressWarnings({ "finally", "static-access" })
    private BorderPane loadFXMLFile(String filePath) {
    BorderPane borderPane = null;
    try {
        borderPane = new BorderPane();
        FXMLLoader fxmlLoader = new FXMLLoader();
        Parent content = fxmlLoader.load(getClass().getResource(filePath));
        borderPane.setCenter(content);
    }
    catch (IOException e) {
        e.printStackTrace();
        System.err.println("Couldn't find the specified file");
    }
    catch(Exception e){
        e.printStackTrace();
    }
    finally {
        return borderPane;
    }
    }

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

我在 src 文件夹中创建了一个普通文件夹“resources”。该资源文件夹仅包含我的 TestWindow.fxml 文件,该文件与我上面的问题完全相同。 现在,当我启动应用程序时,找到并显示 fxml 文件作为主窗口。

所以解决方案实际上是将资源文件夹移入 src 文件夹,这对于我的情况仍然是一个可行的解决方案。我只是不希望最终用户不得不在其他包的深处四处寻找以保存他自己的 fxml 文件。

再次感谢 Roland,为我指明了那个解决方案。

【讨论】:

  • 只是一个不相关的评论:FXMLLoader.load(URL)是一个静态方法;您没有在您创建的 fxmlLoader 实例上调用它(并且您从未实际使用过)。您应该删除fxmlLoader 实例并以通常的方式调用静态方法:Parent content = FXMLLoader.load(...);(或在fxmlLoader 实例上设置位置并调用无参数load() 方法)。
  • 实际上,我认为 FXMLLoader 中的静态加载方法已(计划)弃用。这就是我采用这种方法的原因。
  • 对此进行了讨论,但我不知道是否已决定(可能已经决定)。您的代码仍然调用静态方法。
  • 哦,你是对的……会改变的。感谢您的提醒。
  • FWIW current docs for version 9 不会将静态方法显示为已弃用(尽管这可能会改变 - 我希望如此)。
猜你喜欢
  • 2017-02-25
  • 2015-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-05
  • 1970-01-01
相关资源
最近更新 更多