Краткая версия ответа:
- Используйте
getClass().getResource(...)
или SomeOtherClass.class.getResource(...)
для создания URL
для ресурса - Передайте либо абсолютный путь (с ведущим
/
) или относительный путь (без указания /
) к методу getResource(...)
. Путь - это пакет , содержащий ресурс, с .
, замененным на /
. - Не используйте
..
в пути к ресурсу. Если и когда приложение упаковано в JAR-файл, это не будет работать. Если ресурс находится не в том же пакете или в подпакете класса, используйте абсолютный путь. - Для файлов F XML передайте
URL
непосредственно в FXMLLoader
. - Для изображений и таблиц стилей вызовите
toExternalForm()
в URL
, чтобы сгенерировать String
для передачи конструктору Image
или ImageView
или для добавления в список stylesheets
.
Полный ответ
Содержание
- Объем этого ответа
- Ресурсы загружаются во время выполнения
- JavaFX использует URL-адреса для загрузки ресурсов
- Правила для имен ресурсов
- Создание URL ресурса с
getClass().getResource(...)
- Организация кода и ресурсов
- Стандартные макеты Maven (и аналогичные)
- Устранение неполадок
Сфера действия этого ответа
Обратите внимание, что этот ответ * только 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 дает пример этой организации:
Используя эту структуру, каждый ресурс имеет класс в одном и том же пакете, поэтому он Легко создать правильный 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
в макете ниже
Вы даже можете подумать о создании "интерфейса маркера" исключительно для целей поиска имен ресурсов:
package org.jamesd.examples.sample.images ;
public interface ImageLocation { }
, который теперь позволяет найти эти ресурсы легко:
Image clubs = new Image(ImageLocation.class.getResource("clubs.png").toExternalForm());
Загрузка ресурсов из подпакета класса также достаточно проста. Учитывая следующую компоновку:
мы можем загрузить ресурсы в классе 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 из предыдущего примера выглядит следующим образом:
Важно понимать, как это построено для сборки приложения:
* Файлы 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:
Если вы создаете 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 .