我正在构建一个包含大量场景的大型JavaFX应用程序,现在正尝试使用Maven部署应用程序。
Maven期待资源,例如FXML文件,放在src/main/resources
下。这与我将新场景加载到主场景容器中的方式发生冲突。
我正在使用jewelsea的小框架来加载新内容:Small JavaFX framework for swapping in and out child panes in a main FXML container.
到目前为止,这种方法运作良好。但是,现在我正在尝试从src/main/resources
获取资源(FXML文件),我正在运行NullPointerExceptions。
我可以使用下面的方法加载我的MAIN
视图,但所有其他视图都会抛出NPE。
MAIN
场景加载如下:
FXMLLoader loader = new FXMLLoader();
Pane mainPane = loader.load(getClass().getResource(ViewNavigator.MAIN));
MainController mainController = loader.getController();
ViewNavigator.setMainController(mainController);
ViewNavigator.loadVista(ViewNavigator.CONFIGURE);
记下最后一行。我使用函数loadVista
来注入CONFIGURE
视图。此函数在ViewNavigator
类中声明:
static void loadVista(String fxml) {
try {
mainController.setVista(
FXMLLoader.load(
ViewNavigator.class.getClass().getResource(
fxml
)
)
);
} catch (IOException e) {
e.printStackTrace();
}
}
正如您所看到的,完全相同的方法用于获取CONFIGURE
场景,但这次我遇到了NPE:
Caused by: java.lang.NullPointerException: Location is required.
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3246)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3210)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3129)
at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:3122)
at com.demant.controller.ViewNavigator.loadVista(ViewNavigator.java:33)
at com.demant.controller.MainController.configureView(MainController.java:99)
... 58 more
在ViewNavigator
内,MAIN
和CONFIGURE
视图的网址声明如下:
static final String MAIN = "MainControllerView.fxml";
static final String CONFIGURE = "ConfigureView.fxml";
我希望显然可以将我的资源文件放在src/main/resources
下,并将该包标记为 resources root 。
希望有人能指出我正确的方向。
谢谢。
答案 0 :(得分:1)
FXML文件是在运行时加载的,因此从某种意义上说,源代码层次结构的组织完全无关紧要;唯一重要的是类路径的内容。当然,在这种情况下,您的IDE通过Maven确定如何编译源层次结构中的源代码和资源,并将其部署到类路径层次结构中。
为了使这项工作成功,您需要了解两件事:第一,在不同源文件夹中定义的各种资源的类路径中的最终位置,第二,Class.getResource(resourceName)
如何解决指定的resourceName
来查找资源。
对于第一个问题,Maven喜欢将需要编译为类文件的Java源文件与其他"静态"资源。 (这在Web应用程序中是有意义的,在这些应用程序中,这些应用程序通常部署到不同的位置;在桌面应用程序中它不太合理;但这有点偏离主题。)
默认情况下,Java源代码位于文件夹src/main/java
中。编译该文件夹中的源代码,并将生成的类文件部署到构建文件夹(默认情况下为target/classes
)。关于Java包的通常规则适用,因此如果在名为Main
的包中有一个类org.example
,则源代码将位于src/main/java/org/example/Main.java
中,编译后的代码将位于{{1}中}}。
默认情况下,非Java资源位于target/classes/org/example/Main.class
。此文件夹中的文件被部署(即复制)到同一个构建文件夹。因此,如果你有一个FXML文件,比如src/main/resources
,并且你希望它最终放在包Sample.fxml
中,你可以将它放在org.example
中。构建项目时,它将被复制到src/main/resources/org/example/Sample.fxml
。
对于第二个问题,target/classes/org/example/Sample.fxml
实例具有Class
方法,该方法将返回表示资源的URL。 (因此,如果从文件系统中的类运行,它将返回带有getResource(resourceName)
方案的URL;如果从jar文件运行,它将返回带有file:
方案的URL等等。我喜欢将jar:
视为含义"在找到当前类的同一个地方找到资源"。如果您的资源名称是绝对,意味着它以前导Class.getResource(...)
开头,那么将从类路径的根搜索资源。如果资源名称是 relative ,即它不以/
开头,则从当前类的包中搜索资源。请注意,资源名称的组件(/
之间的部分)必须是有效的Java标识符。特别是,这意味着您不能假设您正在从文件系统加载资源(通常您不是),并且包含/
和.
的名称无效。因此,您只能搜索当前类的包,该包的子包或使用绝对资源名称。
这是一个简单的例子。假设我们正在使用联系信息制作通常的演示JavaFX程序。我们需要(至少)一个主要类,我将调用..
,一个FXML文件,ContactApp
和一个控制器类Contacts.fxml
。我将主类ContactController
放在名为ContactApp
的包中,将控制器类放在子包org.jamesd.fxtest
中。
我喜欢将FXML文件放在与其相应控制器类相同的包中(这样做的好处很快就会显现),因此org.jamesd.fxtest.contacts
也会进入Contacts.fxml
。
以下是项目布局的屏幕截图:
要确认这些是按预期部署的,请使用系统文件浏览器(Finder,Windows资源管理器等)查看" build"文件夹,默认为org.jamesd.fxtest.contacts
:
正如您所见,正如预期的那样,FXML文件和控制器类已部署到包target/classes
,主类已部署到org.jamesd.fxtest.contacts
。
要从org.jamesd.fxtest
类加载FXML文件,我可以指定绝对资源名称(同样,这意味着相对于类路径):
ContactApp
或者,我可以使用相对资源名称。由于当前类位于URL location = getClass().getResource("/org/jamesd/fxtest/contacts/Contacts.fxml");
且我想要的资源位于org.jamesd.fxtest
,因此以下内容将起作用:
org.jamesd.fxtest.contacts
我更喜欢的方法是将FXML文件放在与其相应控制器相同的包中。这样做的好处是我可以使用控制器类来加载资源。这里的一个优点是重构事物要容易得多,因为代码不太依赖于FMXL文件的实际位置(任何IDE都可以轻松处理重构导入)。这是使用此方法的整个URL location = getClass().getResource("contacts/Contacts.fxml");
课程。
ContactApp
因此,在您的示例中,我建议将您的FXML文件放在与package org.jamesd.fxtest;
import java.net.URL;
import org.jamesd.fxtest.contacts.ContactController;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class ContactApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
URL location = ContactController.class.getResource("Contacts.fxml");
FXMLLoader loader = new FXMLLoader(location);
Parent root = loader.load() ;
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
相同的包中:这意味着如果ViewNavigator
位于包ViewNavigator
中,那么FXML文件就会出现在com.demant.controller
。然后只需使用src/main/resources/com/demant/controller
即可。如果它不起作用,那么您可以在系统文件浏览器中打开ViewNavigator.class.getResource("ConfigureView.fxml")
来诊断问题。
最后,请注意您想要
target/classes
不
ViewNavigator.class.getResource(...)
在后一段代码中,ViewNavigator.class.getClass().getResource(...)
解析为ViewNavigator.class
的实例,因此Class<ViewNavigator>
解析为ViewNavigator.class.getClass()
的实例。由于Class<Class<?>>
位于Class
,您将在类路径中搜索java.lang
,这显然不存在(并且不应该存在)。
答案 1 :(得分:0)
您在Class.class
上使用ViewNavigator.class
。 (Class
计算包含ViewNavigator
类信息的getClass()
对象,getResource
返回此对象的类型。)/java/lang/ResourceName
因此正在寻找路径ViewNavigator
中的资源。
您需要使用mainController.setVista(
FXMLLoader.load(
ViewNavigator.class.getResource(
fxml
)
)
);
类来解析资源:
static final String MAIN = "/com/demant/controller/MainControllerView.fxml";
static final String CONFIGURE = "/com/demant/controller/ConfigureView.fxml";
或使用绝对路径:
import urllib2
from bs4 import BeautifulSoup
import sys
import StringIO
import re
search_term = 'automobile'
patent_list = []
for i in range(100): #for the first 100 pages of results
web_page = 'https://www.lens.org/lens/search?q=' + str(search_term) + '&sat=P&l=en&st=true&p=' + str(i) + '&n=100'
page = urllib2.urlopen(web_page)
soup = BeautifulSoup(page,'html.parser')
for aref in soup.findAll("a",href=True):
if re.findall('/lens/patent',aref['href']):
link = aref['href']
split_link = link.split('/')
if len(split_link) == 4:
patent_list.append(split_link[-1])
print '\n'.join(set(patent_list))
答案 2 :(得分:-2)
在fxml文件中,您需要指定要使用的控制器
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import java.util.ArrayList?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.MyController">
<Label fx:id="label1"/>
<Label fx:id="label2"/>
</VBox>