JavaFX:自定义组件仅在可运行的JAR

时间:2019-07-02 00:59:05

标签: java javafx fxml

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

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

<?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

1 个答案:

答案 0 :(得分:2)

@Zephyr指出了撰写本文时我如何错过StackTrace上的“ Caused By:”语句。它清楚表明ImageView由于URL格式错误而无法实例化。

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

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

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