JavaFX:自定义组件仅在 Runnable JAR 中抛出 LoadException

标签 java javafx fxml

我正在尝试从我正在处理的 JavaFX 项目生成一个可执行 jar 文件,该文件在通过 Eclipse IDE 执行时运行正常。 但是,当从生成的 jar 文件执行时,当 FXML 尝试加载自定义组件(扩展 JFX 组件 BorderPane 的类)时,会发生运行时错误>)。 怎么了?

这是 FXML 文件,其中使用了扩展 BorderPane 的自定义类 CardPane

<?xml version="1.0" encoding="UTF-8"?>

<?import com.tsi.ui.CardPane?>
<?import java.lang.*?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<?import com.tsi.ui.CardPane?>

<BorderPane fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="400.0" style="-fx-background-color: #454449;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <center>
      <GridPane fx:id="gridPane" opacity="0.99" prefHeight="386.0" prefWidth="380.0" BorderPane.alignment="CENTER">
        <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
           <!-- 
              The below command (referenced in the stack trace as line 40, 
              which here is not precise, since I've deleted some useless 
              stuff for the post propose) is where the error occurs
            -->
            <CardPane fx:id="pane00" prefHeight="200.0" prefWidth="200.0" />
            <CardPane fx:id="pane01" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" />
            <CardPane fx:id="pane02" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" />
            <CardPane fx:id="pane10" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1" />
            <CardPane fx:id="pane11" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
            <CardPane fx:id="pane12" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="1" />
            <CardPane fx:id="pane20" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="2" />
            <CardPane fx:id="pane21" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
            <CardPane fx:id="pane22" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="2" />
         </children>

      </GridPane>
   </center>

<!-- I will show only the interesting point -->
...

</BorderPane>

CardPane 只是用作扩展 BorderPane 的类,其布局位置已填充了一些其他节点。在此代码中,我们使用其中一些填充 GridPane 单元格的内容。

以下几行显示了它的定义方式:

public class CardPane extends BorderPane {

    private Card card;
    private Label cardName;
    private Label cardValue;
    private ImageView valueIcon;
    private HBox hBox;
    private Node armaSprite;

    public CardPane() {
        hBox = new HBox();
        cardName = new Label();
        cardValue = new Label();
        valueIcon = new ImageView(new Image(Sprite.CAMINHO + File.separator + "CoracaoIcon.png", 27, 22, false, false));
    }

老实说,我不知道这种方法(扩展 BorderPane 类,以这种方式制作我自己的组件)是否是一个好的实践。如果没有,如果有人能让我知道应该避免什么,我会很高兴。 但最重要的是,要点是,在 Eclipse 中一切正常,但在导出项目时却不然。为什么?

这是 Windows 控制台上打印的异常。同样,仅当使用 java.exe -jar '.\Dungeon Cards.jar' 执行 jar 时才会发生这种情况。从来没有在 IDE 上。

javafx.fxml.LoadException:
file:/C:/Users/ramon/Desktop/Dungeon%20Cards.jar!/com/tsi/ui/fxml/game.fxml:40

        at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
        at com.tsi.app.Jogo.<init>(Jogo.java:40)
        at com.tsi.app.DungeonCards.iniciarJogo(DungeonCards.java:138)
        at com.tsi.ui.Instrucoes.esconder(Instrucoes.java:72)
        at com.tsi.ui.Instrucoes$2.handle(Instrucoes.java:52)
        at com.tsi.ui.Instrucoes$2.handle(Instrucoes.java:1)
        at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
        at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
        at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
        at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
        at javafx.event.Event.fireEvent(Event.java:198)
        at javafx.scene.Scene$KeyHandler.process(Scene.java:3964)
        at javafx.scene.Scene$KeyHandler.access$1800(Scene.java:3910)
        at javafx.scene.Scene.impl_processKeyEvent(Scene.java:2040)
        at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2501)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:217)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:149)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$352(GlassViewEventHandler.java:248)
        at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:247)
        at com.sun.glass.ui.View.handleKeyEvent(View.java:546)
        at com.sun.glass.ui.View.notifyKey(View.java:966)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
        at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: Invalid URL: Invalid URL or resource not found
        at javafx.scene.image.Image.validateUrl(Image.java:1118)
        at javafx.scene.image.Image.<init>(Image.java:659)
        at com.tsi.ui.CardPane.<init>(CardPane.java:42)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at java.lang.Class.newInstance(Unknown Source)
        at sun.reflect.misc.ReflectUtil.newInstance(Unknown Source)
        at javafx.fxml.FXMLLoader$InstanceDeclarationElement.constructValue(FXMLLoader.java:1009)
        at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:746)
        at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
        ... 39 more
Caused by: java.lang.IllegalArgumentException: Invalid URL or resource not found
        at javafx.scene.image.Image.validateUrl(Image.java:1110)
        ... 51 more

最佳答案

@Zephyr指出我在写这篇文章时如何错过了 StackTrace 上的“原因:”声明。它清楚地表明由于 URL 格式错误,无法实例化 ImageView

诀窍是我有以下常量指向图像资源所在的根路径:

public static final String CAMINHO = "/com/tsi/sprites"; 

URL 被定义为 CAMINHO + File.separator + [image_file].png

结果:“/com/tsi/sprites\[image_file].png”

构造函数串联中使用的 File.separator 保留 Windows 文件分隔模式(反斜杠),而常量值 CAMINHO使用普通斜杠(Linux 模式)。 不知何故,Eclipse 可以抽象出错误的组合,但在它之外运行时,Windows 却不能。

为了解决这个问题,我只是将 File.separator 更改为 "/"

正如this article中所述,在使用资源时,应避免使用 File.separator:

[...]仅在处理文件以及向用户显示路径时才使用系统特定的文件分隔符。对于所有其他情况,请使用正斜杠。

关于JavaFX:自定义组件仅在 Runnable JAR 中抛出 LoadException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56844078/

相关文章:

JavaFX切换BorderPane的中心: Buttons only work once

css - 文本对齐对齐但向右

java - eclipse helios中代码的识别

java - jpa mysql 表不存在 autoGenerate 不起作用

java - 有没有办法使用 Hibernate session.get() 获取查询?

java - 如何将 2D 网格缩放到 Canvas 的大小?

java - onActivityResult 未在 MainActivity.Java 中调用

JavaFX 在自定义编辑单元中请求焦点

JavaFX 按钮不随 Windows 字体大小设置缩放

layout - JavaFX:即使在调整窗口大小时也将所有组件保持在中心