如何确定我的javafx应用程序所需的fxml文件、css文件、图像和其他资源的正确路径?

polhcujo  于 2021-06-30  发布在  Java
关注(0)|答案(1)|浏览(254)

我的javafx应用程序需要能够找到fxml文件来加载它们 FXMLLoader ,以及样式表(css文件)和图像。当我尝试加载这些时,经常会出现错误,或者我尝试加载的项在运行时根本无法加载。
对于fxml文件,我看到的错误消息包括

Caused by: java.lang.NullPointerException: location is not set

对于图像,堆栈跟踪包括

Caused by: java.lang.IllegalArgumentException: Invalid URL: Invalid URL or resource not found

如何为这些资源找出正确的资源路径?

lx0bsm1f

lx0bsm1f1#

答案的简短版本:

使用 getClass().getResource(...) 或者 SomeOtherClass.class.getResource(...) 创建 URL 到资源
传递绝对路径(带前导 / )或相对路径(没有前导 / )到 getResource(...) 方法。路径是包含资源的包,带有 . 替换为 / .
不要使用 .. 在资源路径中。如果应用程序被绑定为jar文件,那么这将不起作用。如果资源不在同一个包或类的子包中,请使用绝对路径。
对于fxml文件,传递 URL 直接到 FXMLLoader .
有关图像和样式表,请致电 toExternalForm()URL 生成 String 传给 Image 或者 ImageView 构造函数,或添加到 stylesheets 列表。
要解决问题,请检查buid文件夹(或jar文件)的内容,而不是源文件夹。

完整答案

目录

回答的范围
在运行时加载资源
javafx使用url加载资源
资源名称规则
使用创建资源url getClass().getResource(...) 组织代码和资源
maven(及类似)标准布局
故障排除

回答的范围

请注意,这个答案只涉及加载作为应用程序一部分并与之绑定的资源(例如fxml文件、图像和样式表)。因此,例如,加载用户从运行应用程序的计算机上的文件系统中选择的图像将需要不同的技术,这里没有介绍这些技术。

在运行时加载资源

关于加载资源,首先要了解的是,它们当然是在运行时加载的。通常,在开发过程中,应用程序是从文件系统运行的:也就是说,运行应用程序所需的类文件和资源是文件系统上的单个文件。但是,一旦构建了应用程序,它通常是从jar文件执行的。在本例中,资源(如fxml文件、样式表和图像)不再是文件系统中的单个文件,而是jar文件中的条目。因此:
代码不能使用 File , FileInputStream ,或 file: 加载资源的URL

javafx使用url加载资源

javafx使用url加载fxml、图像和css样式表。
这个 FXMLLoader 显式期望 java.net.URL 要传递给它的对象(或
static FXMLLoader.load(...) 方法,到 FXMLLoader 构造函数,或 setLocation() 方法)。
两者 Image 以及 Scene.getStylesheets().add(...) 期望 String 表示URL的。如果在没有方案的情况下传递url,则它们将相对于类路径进行解释。这些字符串可以从 URL 通过打电话 toExternalForm()URL .
为资源创建正确url的推荐机制是 Class.getResource(...) ,在适当的 Class 示例。这样的类示例可以通过调用 getClass() (给出当前对象的类),或 ClassName.class . 这个 Class.getResource(...) 方法需要 String 表示资源名称。

资源名称规则

资源名称是 / -分开的路径名。每个组件表示一个包或子包名称组件。
资源名称区分大小写。
资源名称中的各个组件必须是有效的java标识符
最后一点有一个重要的后果: . 以及 .. 不是有效的java标识符,因此不能在资源名称中使用。
当应用程序从文件系统运行时,它们实际上可能会起作用,尽管这实际上更多地是实现 getResource() . 当应用程序绑定为jar文件时,它们将失败。
类似地,如果您运行的操作系统不区分仅大小写不同的文件名,那么在从文件系统运行时,在资源名中使用错误的大小写可能会起作用,但在从jar文件运行时会失败。
以前导字符开头的资源名称 / 是绝对的:换句话说,它们是相对于类路径来解释的。不带前导的资源名称 / 是相对于 getResource() 被叫来了。
一个小小的变化就是 getClass().getClassLoader().getResource(...) . 提供给的路径 ClassLoader.getResource(...) 总是绝对的,即它相对于类路径。

使用getclass().getresource()创建资源url

要创建资源url,请使用 someClass.getResource(...) . 通常情况下, someClass 表示当前对象的类,使用 getClass() . 然而,正如下一节所描述的,情况并非如此。
如果资源与当前类在同一个包中,或在该类的子包中,请使用资源的相对路径:

// FXML file in the same package as the current class:
 URL fxmlURL = getClass().getResource("MyFile.fxml");
 Parent root = FXMLLoader.load(fxmlURL);

 // FXML file in a subpackage called `fxml`:
 URL fxmlURL2 = getClass().getResource("fxml/MyFile.fxml");
 Parent root2 = FXMLLoader.load(fxmlURL2);

 // Similarly for images:
 URL imageURL = getClass().getResource("myimages/image.png");
 Image image = new Image(imageURL.toExternalForm());

如果资源所在的包不是当前类的子包,请使用绝对路径。例如,如果当前类在包中 org.jamesd.examples.view ,我们需要加载一个css文件 style.css 哪个在包裹里 org.jamesd.examples.css ,我们必须使用绝对路径:

URL cssURL = getClass().getResource("/org/jamesd/examples/css/style.css");
 scene.getStylesheets().add(cssURL.toExternalForm());

值得再次强调的是,在这个例子中 "../css/style.css" 不包含有效的java资源名称,如果将应用程序绑定为jar文件,则将不起作用。

组织代码和资源

我建议将代码和资源组织到由它们关联的ui部分确定的包中。eclipse中的以下源代码布局给出了此组织的示例:

使用此结构,每个资源在同一个包中都有一个类,因此很容易为任何资源生成正确的url:

FXMLLoader editorLoader = new FXMLLoader(EditorController.class.getResource("Editor.fxml"));
Parent editor = editorLoader.load();
FXMLLoader sidebarLoader = new FXMLLoader(SidebarController.class.getResource("Sidebar.fxml"));
Parent sidebar = sidebarLoader.load();

ImageView logo = new ImageView();
logo.setImage(newImage(SidebarController.class.getResource("logo.png").toExternalForm()));

mainScene.getStylesheets().add(App.class.getResource("style.css").toExternalForm());

例如,如果您有一个只有资源而没有类的包,那么 images Package 在下面的布局中

您甚至可以考虑创建一个“marker接口”,仅用于查找资源名称:

package org.jamesd.examples.sample.images ;
public interface ImageLocation { }

现在,您可以轻松找到这些资源:

Image clubs = new Image(ImageLocation.class.getResource("clubs.png").toExternalForm());

从类的子包加载资源也相当简单。给定以下布局:

我们可以在 App 分类如下:

package org.jamesd.examples.resourcedemo;

import java.net.URL;

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

public class App extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {        

        URL fxmlResource = getClass().getResource("fxml/MainView.fxml");

        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(fxmlResource);
        Parent root = loader.load();
        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("style/main-style.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

}

要加载不在从中加载资源的类的同一包或子包中的资源,需要使用绝对路径:

URL fxmlResource = getClass().getResource("/org/jamesd/examples/resourcedemo/fxml/MainView.fxml");

maven(及类似)标准布局

maven和其他依赖关系管理和构建工具推荐一种源文件夹布局,其中资源与java源文件分开。上一个示例的maven布局版本如下所示:

重要的是要了解如何构建它来组装应用程序: *.java 源文件夹中的文件 src/main/java 被编译成类文件,这些类文件被部署到构建文件夹或jar文件中。
资源文件夹中的资源 src/main/resources 复制到构建文件夹或jar文件。
在本例中,由于资源位于与定义了源代码的包的子包相对应的文件夹中,因此生成的构建(在maven中,默认情况下是在 target/classes )由单个结构组成。
请注意,两者 src/main/java 以及 src/main/resources 被认为是构建中相应结构的根,因此只有它们的内容而不是文件夹本身才是构建的一部分。换句话说,没有 resources 文件夹在运行时可用。构建结构如下面的“故障排除”部分所示。
注意,本例中的ide(eclipse)显示 src/main/java 源文件夹与 src/main/resources 文件夹;在第一种情况下,它显示包,但对于资源文件夹,它显示文件夹。确保您知道是否正在创建包(其名称是 . -分隔)或文件夹(其名称不得包含 . ,或任何其他在java标识符中无效的字符)。

故障排除

如果出现意外错误,请首先检查以下各项:
确保您的资源没有使用无效的名称。这包括使用 . 或者 .. 在资源路径中。
确保在预期的位置使用相对路径,在预期的位置使用绝对路径。为了 Class.getResource(...) 如果路径有一个前导,那么它是绝对的 / ,反之亦然。为了 ClassLoader.getResource(...) ,路径总是绝对的。
请记住,绝对路径是相对于类路径定义的。通常,类路径的根是ide中所有源文件夹和资源文件夹的联合。
如果所有这些看起来都是正确的,但仍然看到错误,请检查build或deployment文件夹。此文件夹的确切位置将因ide和生成工具而异。如果您使用的是maven,那么默认情况下是 target/classes . 其他构建工具和IDE将部署到名为 bin , classes , build ,或 out .
通常,ide不会显示build文件夹,因此您可能需要使用系统文件资源管理器检查它。
上面maven示例的组合源代码和构建结构如下

如果您正在生成一个jar文件,某些ide可能允许您在树视图中展开jar文件以检查其内容。您还可以使用检查命令行中的内容 jar tf file.jar :

$ jar -tf resource-demo-0.0.1-SNAPSHOT.jar 
META-INF/
META-INF/MANIFEST.MF
org/
org/jamesd/
org/jamesd/examples/
org/jamesd/examples/resourcedemo/
org/jamesd/examples/resourcedemo/images/
org/jamesd/examples/resourcedemo/style/
org/jamesd/examples/resourcedemo/fxml/
org/jamesd/examples/resourcedemo/images/so-logo.png
org/jamesd/examples/resourcedemo/style/main-style.css
org/jamesd/examples/resourcedemo/Controller.class
org/jamesd/examples/resourcedemo/fxml/MainView.fxml
org/jamesd/examples/resourcedemo/App.class
module-info.class
META-INF/maven/
META-INF/maven/org.jamesd.examples/
META-INF/maven/org.jamesd.examples/resource-demo/
META-INF/maven/org.jamesd.examples/resource-demo/pom.xml
META-INF/maven/org.jamesd.examples/resource-demo/pom.properties
$

如果资源没有被部署,或者部署到意外的位置,请检查构建工具或ide的配置。

相关问题