如何确定JavaFX应用程序所需的FXML文件,CSS文件,图像和其他资源的正确路径? [英] How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?

查看:121
本文介绍了如何确定JavaFX应用程序所需的FXML文件,CSS文件,图像和其他资源的正确路径?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的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,以传递到ImageImageView构造函数,或添加到stylesheets列表中.
  • 要进行故障排除,请检查您的 buid 文件夹(或jar文件)的内容,而不是您的 source 文件夹的内容.
  • Short version of answer:

    • Use getClass().getResource(...) or SomeOtherClass.class.getResource(...) to create a URL to the resource
    • Pass either an absolute path (with a leading /) or a relative path (without a leading /) to the getResource(...) 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 the FXMLLoader.
    • For images and stylesheets, call toExternalForm() on the URL to generate the String to pass to the Image or ImageView constructor, or to add to the stylesheets list.
    • To troubleshoot, examine the content of your buid folder (or jar file), not your source folder.
      1. 此答案的范围
      2. 资源在运行时加载
      3. JavaFX使用URL加载资源
      4. 资源名称规则
      5. 使用getClass().getResource(...)
      6. 创建资源URL
      7. 组织代码和资源
      8. Maven(和类似的)标准布局
      9. 故障排除
      1. Scope of this answer
      2. Resources are loaded at runtime
      3. JavaFX uses URLs to load resources
      4. Rules for resource names
      5. Creating a resource URL with getClass().getResource(...)
      6. Organizing code and resources
      7. Maven (and similar) standard layouts
      8. Troubleshooting

      该答案的范围

      请注意,此答案仅 处理作为应用程序一部分并与之捆绑在一起的加载资源(例如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:

      代码不能使用FileFileInputStreamfile: URL加载资源

      Code cannot use File, FileInputStream, or file: 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).

      ImageScene.getStylesheets().add(...)都期望String代表URL.如果URL是在没有方案的情况下传递的,则它们是相对于类路径进行解释的.可以通过在URL上调用toExternalForm()来以健壮的方式从URL创建这些字符串.

      Both Image and Scene.getStylesheets().add(...) expect Strings 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标识符
      • 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

      最后一点很重要:

      ...不是有效的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.

      • 如果资源与当前类位于同一包中,或者位于该类的子包中,请使用资源的相对路径:

      • 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:

       // 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 文件夹src/main/java中的
      • *.java文件被编译为类文件,这些文件将部署到build文件夹或jar文件中.
      • resource 文件夹src/main/resources中的资源复制到构建文件夹或jar文件.
      • *.java files in the source folder src/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/javasrc/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. For ClassLoader.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将部署到名为binclassesbuildout的文件夹.

      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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆