How to Reference Javafx Fxml Files in Resource Folder

How to reference javafx fxml files in resource folder?

Generic Resource Lookup Information

This answer discusses FXML location lookup, which is really just a subset of the generic resource lookup task in Java. The resource location is passed by the calling program as input to the FXMLLoader, so the resource lookup itself is part of your calling application code and not the FXMLLoader.

For thorough coverage of generic resource information (including recommended troubleshooting steps) for Java/JavaFX applications, please refer to:

  • How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?

Also useful, is the Eden coding resource guide:

  • Where to put resource files in JavaFX

Example usage

FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/main.fxml"));
Parent content = loader.load();

Location resolution options

  1. Put all of your fxml in the src/main/resources directory.

    loader.setLocation(getClass().getResource("/main.fxml"));
  2. Put all of your fxml in a src/main/resources/fxml directory.

    loader.setLocation(getClass().getResource("/fxml/main.fxml"));
  3. Place fxml in a corresponding resource directory; e.g. src/main/resources/com/mycompany/myapp.

    loader.setLocation(getClass().getResource("main.fxml")); 

The last option assumes that the class from which you are loading the fxml is in the same relative location in the corresponding Java source hierarchy. For example, you might invoke the last load command from source com/mycompany/myapp/Main.java.

FXMLLoader usage recommendations

  1. Instantiate an FXMLLoader via new FXMLLoader() rather than using
    the static methods on the FXMLLoader.

    • The static methods
      become confusing when you want to get values (like instantiated
      controllers) out of a loader.
  2. Set the location on the instantiated FXMLLoader and call
    load() rather than loading from a stream using
    load(stream).

    • Setting a URL based location on the loader allows for resolution of
      relative resources loaded in fxml and css files. Relative
      resources do not resolve for a stream based constructor.
  3. To derive a location based upon a class, use
    getClass().getResource(), as it is URL based, rather than
    getClass().getResourceAsStream() which is stream based.

IDE and Build Settings

Ensure that your IDE or build tool is copying the fxml files from the resource directory to the build output directory. For understanding Intellij settings for this, see: How to convert a normal java project in intellij into a JavaFx project.

A note on Java Jigsaw modular applications

See:

  • How to access resource using class loader in Java 9

Specifically, don't write:

ComboBoxStyling.class.getClassLoader()
.getResource("/css/styleclass.css");

Instead write:

ComboBoxStyling.class
.getResource("/css/styleclass.css");

That is, get the resource from the class directly rather, NOT from the class loader. If you get resources from a class loader rather than the class, then there are additional restrictions on lookup based upon the module configuration. These restrictions can be hard to understand, see the documentation for the Class and ClassLoader getResource methods for information if needed.

How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?

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 build folder (or jar file), not your source folder.


Full Answer

Contents

  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


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.


Resources are loaded at runtime

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:

Code cannot use File, FileInputStream, or file: URLs to load a resource


JavaFX uses URLs to load resources

JavaFX loads FXML, Images, and CSS stylesheets using URLs.

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).

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.

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.


Rules for resource names

  • 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

The last point has an important consequence:

. and .. are not valid Java identifiers, so they cannot be used in resource names.

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.

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.

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.

A slight variation on this is to use getClass().getClassLoader().getResource(...). The path supplied to ClassLoader.getResource(...) must not begin with a / and is always absolute, i.e. it is relative to the classpath. It should also be noted that in modular applications, access to resources using ClassLoader.getResource() is, under some circumstances, subject to rules of strong encapsulation, and additionally the package containing the resource must be opened unconditionally. See the documentation for details.


Creating a resource URL with getClass().getResource()

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());
  • 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());

    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.


Organizing code and resources

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:

Sample Image

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());

If you have a package with only resources and no classes, for example, the images package in the layout below

Sample Image

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:

Sample Image

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

Sample Image

It is important to understand how this is built to assemble the application:

  • *.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.

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.

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.

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.


Troubleshooting

If you get errors you do not expect, first check the following:

  • 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, and must not start with a /.
  • 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.

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.

Often, your IDE will not show the build folder, so you may need to check it with the system file explorer.

The combined source and build structure for the Maven example above is

Sample Image

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
$

If the resources are not being deployed, or are being deployed to an unexpected location, check the configuration of your build tool or IDE.

Example image loading troubleshooting code

This code is deliberately more verbose than is strictly necessarily to facilitate adding additional debugging information for the image loading process. It also uses System.out rather than a logger for easier portability.

String resourcePathString = "/img/wumpus.png";
Image image = loadImage(resourcePathString);

// ...

private Image loadImage(String resourcePathString) {
System.out.println("Attempting to load an image from the resourcePath: " + resourcePathString);
URL resource = HelloApplication.class.getResource(resourcePathString);
if (resource == null) {
System.out.println("Resource does not exist: " + resourcePathString);

return null;
}

String path = resource.toExternalForm();
System.out.println("Image path: " + path);

Image image = new Image(path);
System.out.println("Image load error? " + image.isError());
System.out.println("Image load exception? " + image.getException());

if (!image.isError()) {
System.out.println("Successfully loaded an image from " + resourcePathString);
}

return image;
}

External Tutorial Reference

A useful external tutorial for resource location is Eden coding's tutorial:

  • Where to put resource files in JavaFX.

The nice thing about the Eden coding tutorial is that it is comprehensive. In addition to covering the information on lookups from Java code which is in this question. The Eden tutorial covers topics such as locating resources that are encoded as urls in CSS, or resource references in FXML using an @ specifier or fx:include element (which are topics currently not directly covered in this answer).

Using a resource folder to load images on JavaFX project using IntelIiJ

Quick Solution for finding images

You see how you load the stylesheet in the preceding line by providing the string "/sample/desing.css", do the same thing for your image, e.g. new Image("/sample/nfc.png").

That assumes that the image is in the same location as your css. If it were somewhere else, e.g. under /images/ngc.png, then you would use that path. It also assumes that your build system is configured to copy the source resource files to your project output directory.

Finding images when your project is based on a build tool (e.g. maven)

If you are using a build tool such as Maven or Gradle (recommended for any medium to large size project), it will have a standard structure where your java classes are in src/main/java and your resources, such as css and images are in src/main/resources. It is automatically configured to copy the resources into your application artifact.

Finding images using IntelliJ or another IDE

You note that you are using IntelliJ, that tool (and any other major Java IDE) is intelligent enough to recognize that a project built with a build tool such as maven has a standard structure and will automatically configure an imported maven project in Idea to understand that structure, so you don't have to do anything additional.

If you are not using a recommended tool such as maven, then you can configure the project manually to copy over resources (as @egimaben defined in his answer). Manual configuration for copying resources in other IDEs will differ, so you will need to research how the IDE handles project resources to work out how to do that for such projects.

Example code to find images

So for example, if your image is in this location within your project source: src/main/resources/images/ngc.png, you can access it in your project via:

Image image = new Image("/images/nfc.png");

If you don't need a reference to the image, you can just directly reference the location from your ImageView constructor:

ImageView imageView = new ImageView("/images/nfc.png");

Relevant documentation

Read the image doc to see how images are found. Specifically, these lines show how to load images from resources on the class path:

// The image is located in default package of the classpath
Image image1 = new Image("/flower.png", true);

// The image is located in my.res package of the classpath
Image image2 = new Image("my/res/flower.png", 100, 150, false, false);

Note on explicitly using the file: protocol (don't do it)

To load from the class or module path, don't explicitly use the file: protocol, your files could be in a jar or elsewhere such as on a remote network, so the protocol needed to access them could change (a file in jar needs to be accessed using the jar: protocol, not the file: protocol). When you don't specify a protocol in your Image constructor, then the system will default to use the class loader, which already knows what protocol to use to find resources on your class path.

Troubleshooting

Place the following line in your code where you are trying to load the image:

System.out.println(getClass().getResource("<imagepath>"))

Where is the "<imagepath>" is exactly the same string you passed to the image constructer. If the call outputs null, then the image is probably not on your class path at the expected location.

If your app is packaged as a jar, run jar tvf <yourjar>.jar, replacing <yourjar> with the name of your jar file and it will list everything in the jar file. Most likely the image won't be at the location in the jar file you are trying to retrieve it from, so you will need to move it around or fix your build system to get it in the right place.

Image has error and exception properties, if it fails to load, you can query the exception and it may provide you with some more info on what went wrong, e.g.:

Image image = new Image("/images/nfc.png");
if (image.isError()) {
System.out.println(image.getException());
}

If you use an image constructor with background loading, then the error could occur asynchronously, so you would need to monitor changes in the error property to know if an error occurred:

Image image = new Image("/images/nfc.png");
if (image.isError()) {
System.out.println(image.getException());
} else {
image.errorProperty().addListener((observable, oldValue, newValue) -> {
if (image.isError()) {
System.out.println(image.getException());
}
});
}

JavaFX Image from resources folder

Resources

The Class#getResource(String) and related API are used for locating resources relative to the class path and/or module path. When using Class to get a resource you can pass an absolute name or a relative name. An absolute name will locate the resource relative to the root of the class path/module path; an absolute name starts with a /. A relative name will locate the resource relative to the location of the Class; a relative name does not start with a leading /.

In a typical Maven/Gradle project structure, the src/main/java and src/main/resources are roots of the class path/module path. This means all resource names are relative to those directories. It's slightly more complicated than that because the files under those directories are moved to the target/build directory and it's that location that's put on the class path/module path, but for all intents and purposes consider the source directories as the root. There's a reason a get-resource API exists in the first place, to provide an application-location-independent way of obtaining resources.


Issues in Your Code

From your question I gather your project structure looks something like:

<project-dir>
|--src/
|--main/
|--java/
|--resources/
|--images/
|--image.png

And you're calling your method with an Object and a resource name of image.png. The problem here is that, since you're passing a relative name, the resource is located relative to the Class of the passed Object (i.e. context). I doubt your class is located in a package named images which means the resource will not be found relative to said class. You need to pass an absolute name: /images/image.png.

The other problem is your use of URL#getPath(). The URL you obtain from Class#getResource(String) will, if the resource were to be found, look something like this:

file:/path/to/gradle/project/build/resources/main/images/image.png

But the result of URL#getPath() will give you:

/path/to/gradle/project/build/resources/main/images/image.png

This causes a problem due to the way Image works. From the documentation:

All URLs supported by URL can be passed to the constructor. If the passed string is not a valid URL, but a path instead, the Image is searched on the classpath in that case.

Notice the second sentence. If the passed URL does not have a scheme then it's interpreted as a resource name and the Image will locate the image file relative to the classpath. In other words, since you're passing the value of URL#getPath() to the Image constructor it searches for the resource image.png in the package path.to.gradle.project.build.resources.main.images. That package does not exist. You should be passing the URL as-is to the Image constructor via URL#toString() or URL#toExternalForm().


Solution

If you're going to use the URL returned by Class#getResource(String) to load the Image then no matter what you need to use URL#toString() or URL#toExternalForm() instead of URL#getPath().

public static Image createImage(Object context, String resourceName) {
URL _url = context.getClass().getResource(resourceName);
return new Image(_url.toExternalForm());
}

Then you have at least two options:

  1. Pass the absolute resource name (i.e. "/images/image.png") to your #createImage(Object,String) method since the image.png resource is not in the same package as the passed Object (i.e. context).

  2. Move the resource to the same package as the class of the passed in Object (i.e. context). For instance, if the context object's class is com.foo.MyObject then place the resource under src/main/resources/com/foo and it will be in the same package as MyObject. This will allow you to continue passing the relative resource name.

Of course, as noted by the documentation of Image you can pass a scheme-less URL and it's interpreted as a resource name. In other words, you could do:

Image image = new Image("images/image.png");

And that should work. A note of caution, however: When using modules the above will only work if the resource-containing package is opens unconditionally or if the module itself is open.

Load fxml file located in src maven folder

Java does not make distinction between resources and sources. As long as your fxml file is visible in classpath (packaged into *.jar appropriately) you can reach it the same way. Given that you use maven, this is the configuration you need.



Related Topics



Leave a reply



Submit