GOOGLE ADS

пятница, 6 мая 2022 г.

Как определить правильный путь для файлов FXML, файлов CSS, изображений и других ресурсов, необходимых моему приложению JavaFX?

Мое приложение 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

Как определить правильный путь к этим ресурсам?


Решение проблемы

Краткая версия ответа:


  • Используйте getClass().getResource(...)или SomeOtherClass.class.getResource(...)для создания URLресурса

  • Передайте методу абсолютный путь (с начальным /) или относительный путь (без начального /) getResource(...). Путь — это пакет, содержащий ресурс, с .заменой на /.

  • Не используйте .. в пути к ресурсу. Если и когда приложение упаковано в виде файла jar, это не сработает. Если ресурс не находится в том же пакете или в подпакете класса, используйте абсолютный путь.

  • Для файлов FXML передайте URLнепосредственно в файл FXMLLoader.

  • Для изображений и таблиц стилей вызовите toExternalForm()метод URLдля создания Stringдля передачи в конструктор Imageили ImageViewили для добавления в stylesheetsсписок.

  • Чтобы устранить неполадки, изучите содержимое папки сборки (или файла jar), а не исходной папки.

  • Полный ответ


    Содержание


    1. Объем этого ответа

    2. Ресурсы загружаются во время выполнения

    3. JavaFX использует URL-адреса для загрузки ресурсов

    4. Правила для имен ресурсов

    5. Создание URL-адреса ресурса сgetClass().getResource(...)

    6. Организация кода и ресурсов

    7. Стандартные макеты Maven (и подобные)

    8. Поиск проблемы



    Объем этого ответа

    Обратите внимание, что этот ответ касается только загрузки ресурсов (например, файлов FXML, изображений и таблиц стилей), которые являются частью приложения и связаны с ним. Так, например, для загрузки изображений, которые пользователь выбирает из файловой системы на машине, на которой работает приложение, потребуются другие методы, которые здесь не рассматриваются.


    Ресурсы загружаются во время выполнения

    Первое, что нужно понять о загрузке ресурсов, это то, что они, конечно же, загружаются во время выполнения. Обычно во время разработки приложение запускается из файловой системы: то есть файлы классов и ресурсы, необходимые для его запуска, представляют собой отдельные файлы в файловой системе. Однако, как только приложение создано, оно обычно запускается из jar-файла. В этом случае такие ресурсы, как файлы FXML, таблицы стилей и изображения, больше не являются отдельными файлами в файловой системе, а являются записями в файле jar. Поэтому:


    Код не может использовать File, FileInputStreamили file:URL-адреса для загрузки ресурса



    JavaFX использует URL-адреса для загрузки ресурсов

    JavaFX загружает FXML, изображения и таблицы стилей CSS, используя URL-адреса.

    Явно FXMLLoaderожидает, что java.net.URLобъект будет передан ему (либо static FXMLLoader.load(...)методу, FXMLLoaderлибо конструктору, либо setLocation()методу).

    Оба Imageи Scene.getStylesheets().add(...)ожидают String, которые представляют URL-адреса. Если URL-адреса передаются без схемы, они интерпретируются относительно пути к классам. Эти строки можно создать из URLa надежным способом, вызвав toExternalForm()метод URL.

    Рекомендуемый механизм для создания правильного URL-адреса для ресурса — использовать Class.getResource(...), который вызывается для соответствующего Classэкземпляра. Такой экземпляр класса можно получить вызовом getClass()(который дает класс текущего объекта) или ClassName.class. Метод Class.getResource(...)принимает Stringпредставление имени ресурса.


    Правила для имен ресурсов

  • Имена ресурсов представляют собой /пути, разделенные -. Каждый компонент представляет собой компонент имени пакета или подпакета.

  • Имена ресурсов чувствительны к регистру.

  • Отдельные компоненты в имени ресурса должны быть действительными идентификаторами Java.

  • Последний пункт имеет важное следствие:


    .и ..не являются действительными идентификаторами Java, поэтому их нельзя использовать в именах ресурсов.


    На самом деле они могут работать, когда приложение запускается из файловой системы, хотя на самом деле это скорее случайность реализации getResource(). Они потерпят неудачу, если приложение упаковано в виде jar-файла.

    Точно так же, если вы работаете в операционной системе, которая не различает имена файлов, отличающиеся только регистром, то использование неправильного регистра в имени ресурса может работать при запуске из файловой системы, но не будет работать при запуске из jar-файла.

    Имена ресурсов, начинающиеся с начального, /являются абсолютными: другими словами, они интерпретируются относительно пути к классам. Имена ресурсов без начального /интерпретируются относительно класса, для которого они getResource()были вызваны.

    Небольшой вариант — использовать getClass().getClassLoader().getResource(...). Указанный путь ClassLoader.getResource(...) не должен начинаться с a /и всегда является абсолютным, т. е. относительным по отношению к пути к классам. Также следует отметить, что в модульных приложениях доступ к использованию ресурсов ClassLoader.getResource()при некоторых обстоятельствах подчиняется правилам строгой инкапсуляции, а кроме того, пакет, содержащий ресурс, должен открываться безоговорочно. Подробности смотрите в документации.


    Создание URL-адреса ресурса сgetClass().getResource()

    Чтобы создать 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.



  • Организация кода и ресурсов

    Я рекомендую организовывать ваш код и ресурсы в пакеты, определяемые той частью пользовательского интерфейса, с которой они связаны. Следующий макет исходного кода в Eclipse дает пример такой организации:

    введите описание изображения здесь

    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

    введите описание изображения здесь

    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:

    введите описание изображения здесь

    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:

    введите описание изображения здесь

    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

    введите описание изображения здесь

    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.

  • Учебник по кодированию Eden хорош тем, что он всеобъемлющий. В дополнение к освещению информации о поиске из кода Java, которая находится в этом вопросе. Учебник Eden охватывает такие темы, как поиск ресурсов, которые закодированы как URL-адреса в CSS, или ссылки на ресурсы в FXML с использованием @спецификатора или fx:includeэлемента (которые в настоящее время не рассматриваются напрямую в этом ответе).

    Комментариев нет:

    Отправить комментарий

    Laravel Datatable addColumn returns ID of one record only

    Я пытаюсь использовать Yajra Datatable для интеграции DataTable на свой веб-сайт. Я смог отобразить таблицу, но столкнулся с проблемой. В по...