如何确定JavaFX应用程序所需的FXML文件,CSS文件,图像和其他资源的正确路径? [英] How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?
问题描述
我的JavaFX应用程序需要能够找到FXML文件以用FXMLLoader
加载它们,以及样式表(CSS文件)和图像.当我尝试加载这些项目时,经常会出错,或者我尝试加载的项目根本无法在运行时加载.
My JavaFX application needs to be able to find the FXML files to load them with the FXMLLoader
, as well as stylesheets (CSS files) and images. When I try to load these, I often get errors, or the item I'm trying to load simply doesn't load at runtime.
对于FXML文件,我看到的错误消息包括
For FXML files, the error message I see includes
Caused by: java.lang.NullPointerException: location is not set
对于图像,堆栈跟踪包括
For images, the stack trace includes
Caused by: java.lang.IllegalArgumentException: Invalid URL: Invalid URL or resource not found
如何找出这些资源的正确资源路径?
How do I figure out the correct resource path for these resources?
推荐答案
答案的简短版本:
- 使用
getClass().getResource(...)
或SomeOtherClass.class.getResource(...)
创建资源的URL
- 将绝对路径(带有前导
/
)或相对路径(没有前导/
)传递给getResource(...)
方法.路径是包含资源的 package ,其中.
替换为/
. - 请勿在资源路径中使用
..
.如果并且当应用程序捆绑为jar文件时,这将无法工作.如果资源不在该类的同一包或子包中,请使用绝对路径. - 对于FXML文件,将
URL
直接传递到FXMLLoader
. - 对于图像和样式表,请在
URL
上调用toExternalForm()
生成String
,以传递到Image
或ImageView
构造函数,或添加到stylesheets
列表中. - 要进行故障排除,请检查您的 buid 文件夹(或jar文件)的内容,而不是您的 source 文件夹的内容.
- Use
getClass().getResource(...)
orSomeOtherClass.class.getResource(...)
to create aURL
to the resource - Pass either an absolute path (with a leading
/
) or a relative path (without a leading/
) to thegetResource(...)
method. The path is the package containing the resource, with.
replaced with/
. - Do not use
..
in the resource path. If and when the application is bundled as a jar file, this will not work. If the resource is not in the same package or in a subpackage of the class, use an absolute path. - For FXML files, pass the
URL
directly to theFXMLLoader
. - For images and stylesheets, call
toExternalForm()
on theURL
to generate theString
to pass to theImage
orImageView
constructor, or to add to thestylesheets
list. - To troubleshoot, examine the content of your buid folder (or jar file), not your source folder.
- 此答案的范围
- 资源在运行时加载
- JavaFX使用URL加载资源
- 资源名称规则
- 使用
getClass().getResource(...)
创建资源URL
- 组织代码和资源
- Maven(和类似的)标准布局
- 故障排除
- Scope of this answer
- Resources are loaded at runtime
- JavaFX uses URLs to load resources
- Rules for resource names
- Creating a resource URL with
getClass().getResource(...)
- Organizing code and resources
- Maven (and similar) standard layouts
- Troubleshooting
- 资源名称是用
/
分隔的路径名.每个组件代表一个包或子包名称组件. - 资源名称区分大小写.
- 资源名称中的各个组件必须是有效的Java标识符
- Resource names are
/
-separated path names. Each component represents a package or sub-package name component. - Resource names are case-sensitive.
- The individual components in the resource name must be valid Java identifiers
-
如果资源与当前类位于同一包中,或者位于该类的子包中,请使用资源的相对路径:
If the resource is in the same package as the current class, or in a subpackage of that class, use a relative path to the resource:
Short version of answer:
该答案的范围
请注意,此答案仅 处理作为应用程序一部分并与之捆绑在一起的加载资源(例如FXML文件,图像和样式表).因此,例如,在运行应用程序的计算机上加载用户从文件系统中选择的图像,将需要此处未介绍的其他技术.
Scope of this answer
Note that this answer only addresses loading resources (for example FXML files, images, and stylesheets) that are part of the application, and bundled with it. So, for example, loading images that the user chooses from the file system on the machine on which the application is running would require different techniques that are not covered here.
关于加载资源的第一件事是,它们当然是在运行时加载的.通常,在开发过程中,应用程序是从文件系统运行的;也就是说,运行该文件所需的类文件和资源是文件系统上的各个文件.但是,一旦构建了应用程序,通常就可以从jar文件中执行该应用程序.在这种情况下,诸如FXML文件,样式表和图像之类的资源不再是文件系统上的单个文件,而是jar文件中的条目.因此:
The first thing to understand about loading resources is that they, of course, are loaded at runtime. Typically, during development, an application is run from the file system: that is, the class files and resources required to run it are individual files on the file system. However, once the application is built, it is usually executed from a jar file. In this case, the resources such as FXML files, stylesheets, and images, are no longer individual files on the filesystem but are entries in the jar file. Therefore:
代码不能使用
File
,FileInputStream
或file:
URL加载资源
Code cannot use
File
,FileInputStream
, orfile:
URLs to load a resource
JavaFX使用URL加载资源
JavaFX使用URL加载FXML,图像和CSS样式表.
JavaFX uses URLs to load resources
JavaFX loads FXML, Images, and CSS stylesheets using URLs.
FXMLLoader
明确希望将java.net.URL
对象传递给它(传递给static
FXMLLoader.load(...)
方法,传递给FXMLLoader
构造函数或传递给setLocation()
方法).
The FXMLLoader
explicitly expects a java.net.URL
object to be passed to it (either to the static
FXMLLoader.load(...)
method, to the FXMLLoader
constructor, or to the setLocation()
method).
Image
和Scene.getStylesheets().add(...)
都期望String
代表URL.如果URL是在没有方案的情况下传递的,则它们是相对于类路径进行解释的.可以通过在URL
上调用toExternalForm()
来以健壮的方式从URL
创建这些字符串.
Both Image
and Scene.getStylesheets().add(...)
expect String
s that represent URLs. If URLs are passed without a scheme, they are interpreted relative to the classpath. These strings can be created from a URL
in a robust way by calling toExternalForm()
on the URL
.
为资源创建正确的URL的推荐机制是使用Class.getResource(...)
,它在适当的Class
实例上调用.可以通过调用getClass()
(给出当前对象的类)或ClassName.class
来获得此类实例. Class.getResource(...)
方法采用String
代表资源名称.
The recommended mechanism for creating the correct URL for a resource is to use Class.getResource(...)
, which is called on an appropriate Class
instance. Such a class instance can be obtained by calling getClass()
(which gives the class of the current object), or ClassName.class
. The Class.getResource(...)
method takes a String
representing the resource name.
最后一点很重要:
.
和..
不是有效的Java标识符,因此它们 不能用于资源名称 .
.
and..
are not valid Java identifiers, so they cannot be used in resource names.
当应用程序从文件系统运行时,这些实际上可能有效,尽管这确实是getResource()
实施的偶然事件.当应用程序打包为jar文件时,它们将失败.
These may actually work when the application is running from the filesystem, though this is really more of an accident of the implementation of getResource()
. They will fail when the application is bundled as a jar file.
类似地,如果您在不能区分仅大小写不同的文件名的操作系统上运行,则从文件系统运行时在资源名称中使用错误的大小写可能会起作用,但是从jar运行时会失败文件.
Similarly, if you are running on an operating system that does not distinguish between filenames that differ only by case, then using the wrong case in a resource name might work while running from the filesystem, but will fail when running from a jar file.
以前导/
开头的资源名称是绝对:换句话说,它们是相对于类路径进行解释的.没有前导/
的资源名称将相对于调用getResource()
的类进行解释.
Resource names beginning with a leading /
are absolute: in other words they are interpreted relative to the classpath. Resource names without a leading /
are interpreted relative to the class on which getResource()
was called.
对此稍作改动即可使用getClass().getClassLoader().getResource(...)
.提供给ClassLoader.getResource(...)
的路径始终是 绝对路径,即相对于类路径.
A slight variation on this is to use getClass().getClassLoader().getResource(...)
. The path supplied to ClassLoader.getResource(...)
is always absolute, i.e. it is relative to the classpath.
要创建资源URL,请使用someClass.getResource(...)
.通常,someClass
表示当前对象的类,并使用getClass()
获得.但是,不必如此,如下一节所述.
To create a resource URL, use someClass.getResource(...)
. Usually, someClass
represents the class of the current object, and is obtained using getClass()
. However, this doesn't have to be the case, as described in the next section.
// 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
包中,并且我们需要加载在org.jamesd.examples.css
包中的CSS文件style.css
,则必须使用绝对路径:
If the resource is in a package that is not a subpackage of the current class, use an absolute path. For example, if the current class is in the package org.jamesd.examples.view
, and we need to load a CSS file style.css
which is in the package org.jamesd.examples.css
, we have to use an absolute path:
URL cssURL = getClass().getResource("/org/jamesd/examples/css/style.css");
scene.getStylesheets().add(cssURL.toExternalForm());
在此示例中值得再次强调的是,路径"../css/style.css"
不包含有效的Java资源名称,如果应用程序捆绑为jar文件,则将不起作用.
It's worth re-emphasizing for this example that the path "../css/style.css"
does not contain valid Java resource names, and will not work if the application is bundled as a jar file.
我建议将代码和资源组织到由与它们相关联的UI部分确定的程序包中. Eclipse中的以下源代码布局提供了此组织的示例:
I recommend organizing your code and resources into packages determined by the part of the UI they are associated with. The following source layout in Eclipse gives an example of this organization:
使用这种结构,每个资源在同一包中都有一个类,因此很容易为任何资源生成正确的URL:
Using this structure, each resource has a class in the same package, so it is easy to generate the correct URL for any resource:
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
软件包
If you have a package with only resources and no classes, for example, the images
package in the layout below
您甚至可以考虑创建一个标记界面",仅出于查找资源名称的目的:
you can even consider creating a "marker interface" solely for the purposes of looking up the resource names:
package org.jamesd.examples.sample.images ;
public interface ImageLocation { }
现在,您可以轻松找到这些资源:
which now lets you find these resources easily:
Image clubs = new Image(ImageLocation.class.getResource("clubs.png").toExternalForm());
从类的子包加载资源也相当简单.给出以下布局:
Loading resources from a subpackage of a class is also reasonably straightforward. Given the following layout:
我们可以按以下方式在App
类中加载资源:
we can load resources in the App
class as follows:
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);
}
}
要加载与要从中加载资源的类不在同一个包或子包中的资源,您需要使用绝对路径:
To load resources which are not in the same package, or a subpackage, of the class from which you're loading them, you need to use the absolute path:
URL fxmlResource = getClass().getResource("/org/jamesd/examples/resourcedemo/fxml/MainView.fxml");
Maven(和类似的)标准布局
Maven和其他依赖项管理与构建工具建议使用 source 文件夹布局,其中资源与Java源文件分开.上一个示例的Maven布局版本如下所示:
Maven (and similar) standard layouts
Maven and other dependency management and build tools recommend a source folder layout in which resources are separated from Java source files. The Maven layout version of the previous example looks like:
重要的是要了解如何组装应用程序:
It is important to understand how this is built to assemble the application:
-
source 文件夹
-
*.java
文件被编译为类文件,这些文件将部署到build文件夹或jar文件中. - resource 文件夹
src/main/resources
中的资源复制到构建文件夹或jar文件.
src/main/java
中的*.java
files in the source foldersrc/main/java
are compiled to class files, which are deployed to the build folder or jar file.- Resources in the resource folder
src/main/resources
are copied to the build folder or jar file.
在此示例中,由于资源位于与定义了源代码的包的子包相对应的文件夹中,因此生成的内部版本(默认情况下在Maven中位于target/classes
中)由单个结构组成.
In this example, because the resources are in folders that correspond to subpackages of the packages where the source code is defined, the resulting build (which, by default with Maven, is in target/classes
) consists of a single structure.
请注意,src/main/java
和src/main/resources
均被视为构建中相应结构的根,因此,构建中仅包含它们的内容,而不是文件夹本身.换句话说,运行时没有resources
文件夹可用.该构建结构在下面的疑难解答"中显示.部分.
Note that both src/main/java
and src/main/resources
are considered the root for the corresponding structure in the build, so only their content, not the folders themselves, are part of the build. In other words, there is no resources
folder available at runtime. The build structure is shown below in the "troubleshooting" section.
请注意,在这种情况下(Eclipse),IDE显示的src/main/java
源文件夹与src/main/resources
文件夹不同;在第一种情况下,它显示 packages ,但是对于资源文件夹,它显示 folders .确保知道是否要在IDE中创建包(名称以.
分隔)或文件夹(名称必须不包含.
或Java标识符中无效的任何其他字符).
Notice that the IDE in this case (Eclipse) displays the src/main/java
source folder differently to the src/main/resources
folder; in the first case it displays packages, but for the resource folder it displays folders. Make sure you know if you are creating packages (whose names are .
-delimited) or folders (whose names must not contain .
, or any other character not valid in a Java identifier) in your IDE.
如果遇到意外错误,请首先检查以下内容:
If you get errors you do not expect, first check the following:
- 确保您没有对资源使用无效的名称.这包括在资源路径中使用
.
或..
. - 确保在预期的位置使用相对路径,在预期的位置使用绝对路径.对于
Class.getResource(...)
,如果路径具有前导/
,则该路径为绝对路径,否则为相对路径.对于ClassLoader.getResource(...)
,该路径始终是绝对路径. - 请记住,绝对路径是相对于 classpath 定义的.通常,类路径的根是IDE中所有源和资源文件夹的并集.
- Make sure you are not using invalid names for your resources. This includes using
.
or..
in the resource path. - Make sure you are using relative paths where expected, and absolute paths where expected. for
Class.getResource(...)
the path is absolute if it has a leading/
, and relative otherwise. ForClassLoader.getResource(...)
, the path is always absolute. - Remember that absolute paths are defined relative to the classpath. Typically the root of the classpath is the union of all source and resource folders in your IDE.
如果这一切似乎都正确,但您仍然看到错误,请检查 build 或部署文件夹.此文件夹的确切位置将因IDE和构建工具而异.如果使用的是Maven,默认情况下为target/classes
.其他构建工具和IDE将部署到名为bin
,classes
,build
或out
的文件夹.
If all this seems correct, and you still see errors, check the build or deployment folder. The exact location of this folder will vary by IDE and build tool. If you are using Maven, by default it is target/classes
. Other build tools and IDEs will deploy to folders named bin
, classes
, build
, or out
.
通常,您的IDE不会显示build文件夹,因此您可能需要使用系统文件浏览器进行检查.
Often, your IDE will not show the build folder, so you may need to check it with the system file explorer.
上面的Maven示例的组合的源代码和构建结构是
The combined source and build structure for the Maven example above is
如果要生成jar文件,则某些IDE可能会允许您在树形视图中展开jar文件以检查其内容.您也可以使用jar tf file.jar
在命令行中检查内容:
If you are generating a jar file, some IDEs may allow you to expand the jar file in a tree view to inspect its contents. You can also check the contents from the command line with 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的配置.
If the resources are not being deployed, or are being deployed to an unexpected location, check the configuration of your build tool or IDE.
这篇关于如何确定JavaFX应用程序所需的FXML文件,CSS文件,图像和其他资源的正确路径?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!