Как определить правильный путь для файлов F XML, CSS, изображений и других ресурсов, необходимых для моего приложения JavaFX? - PullRequest
3 голосов
/ 30 апреля 2020

Мое приложение JavaFX должно иметь возможность находить файлы F XML для загрузки их с FXMLLoader, а также таблицы стилей (CSS файлы) и изображения. Когда я пытаюсь загрузить их, я часто получаю ошибки, или элемент, который я пытаюсь загрузить, просто не загружается во время выполнения.

Для файлов F XML, сообщение об ошибке, которое я вижу, включает

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

Для изображений трассировка стека включает

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

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

1 Ответ

7 голосов
/ 30 апреля 2020

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

  • Используйте getClass().getResource(...) или SomeOtherClass.class.getResource(...) для создания URL для ресурса
  • Передайте либо абсолютный путь (с ведущим /) или относительный путь (без указания /) к методу getResource(...). Путь - это пакет , содержащий ресурс, с ., замененным на /.
  • Не используйте .. в пути к ресурсу. Если и когда приложение упаковано в JAR-файл, это не будет работать. Если ресурс находится не в том же пакете или в подпакете класса, используйте абсолютный путь.
  • Для файлов F XML передайте URL непосредственно в FXMLLoader.
  • Для изображений и таблиц стилей вызовите toExternalForm() в URL, чтобы сгенерировать String для передачи конструктору Image или ImageView или для добавления в список stylesheets.

Полный ответ

Содержание

  1. Объем этого ответа
  2. Ресурсы загружаются во время выполнения
  3. JavaFX использует URL-адреса для загрузки ресурсов
  4. Правила для имен ресурсов
  5. Создание URL ресурса с getClass().getResource(...)
  6. Организация кода и ресурсов
  7. Стандартные макеты Maven (и аналогичные)
  8. Устранение неполадок

Сфера действия этого ответа

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

Ресурсы загружаются в runtime

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

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

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

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

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.

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

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

Небольшое отклонение от этого заключается в использовании getClass().getClassLoader().getResource(...). Путь к 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 дает пример этой организации:

enter image description here

Используя эту структуру, каждый ресурс имеет класс в одном и том же пакете, поэтому он Легко создать правильный 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 в макете ниже

enter image description here

Вы даже можете подумать о создании "интерфейса маркера" исключительно для целей поиска имен ресурсов:

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

, который теперь позволяет найти эти ресурсы легко:

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

Загрузка ресурсов из подпакета класса также достаточно проста. Учитывая следующую компоновку:

enter image description here

мы можем загрузить ресурсы в классе 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 и другие инструменты управления зависимостями и сборки рекомендуют макет папки source , в котором ресурсы отделены от Java исходных файлов. Версия макета Maven из предыдущего примера выглядит следующим образом:

enter image description here

Важно понимать, как это построено для сборки приложения:

* Файлы 1240 * *.java в папке source src/main/java компилируются в файлы классов, которые развертываются в папке сборки или файле jar. Ресурсы в resource folder src/main/resources копируется в папку build или файл jar.

В этом примере, поскольку ресурсы находятся в папках, которые соответствуют подпакетам пакетов, в которых определен исходный код, в результате получается сборка (которая по умолчанию с Maven находится в target/classes) состоит из одной структуры.

Обратите внимание, что src/main/java и src/main/resources считаются root для соответствующей структуры в сборке, поэтому только их содержимое, а не сами папки, являются частью строить. Другими словами, нет папки resources, доступной во время выполнения. Структура сборки показана ниже в разделе «Устранение неполадок».

Обратите внимание, что в этом случае IDE (Eclipse) отображает исходную папку src/main/java по-другому, чем папка src/main/resources; в первом случае отображаются пакетов , а для папки ресурсов - папок . Убедитесь, что вы знаете, создаете ли вы пакеты (имена которых ограничены .) или папки (имена которых не должны содержать ., или любой другой символ, недопустимый в идентификаторе Java) в вашей среде IDE.

Устранение неполадок

Если вы получаете ошибки, которые не ожидаете, сначала проверьте следующее:

  • Убедитесь, что вы не используете недопустимые имена для Ресурсы. Это включает использование . или .. в пути к ресурсу.
  • Убедитесь, что вы используете относительные пути, где ожидается, и абсолютные пути, где ожидается. для Class.getResource(...) путь является абсолютным, если он имеет ведущий /, и относительный в противном случае. Для ClassLoader.getResource(...) путь всегда является абсолютным.
  • Помните, что абсолютные пути определены относительно classpath . Обычно root пути к классам представляет собой объединение всех папок источника и ресурсов в вашей IDE.

Если все это кажется правильным и вы все еще видите ошибки, проверьте build или папку развертывания. Точное местоположение этой папки зависит от среды разработки и инструмента сборки. Если вы используете Maven, по умолчанию это target/classes. Другие инструменты сборки и IDE будут развернуты в папках с именами bin, classes, build или out.

Часто в вашей среде IDE не отображается папка сборки, поэтому вам может потребоваться проверить ее с помощью системного обозревателя файлов.

Объединенная структура исходного кода и сборки для приведенного выше примера Maven:

enter image description here

Если вы создаете 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 .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...