我正在尝试在FXML中创建一个Spinner<Duration>对象。我创建了扩展DurationSpinnerValueFactorySpinnerValueFactory<Duration>。我已经为DurationSpinnerValueFactory定义了一个默认的构造函数,以及一个采用最大允许值的构造函数。构造函数定义如下:

import java.time.Duration;
...

public class DurationSpinnerValueFactory extends SpinnerValueFactory<Duration> {

    public DurationSpinnerValueFactory() {
        this(null);
    }

    public DurationSpinnerValueFactory(@NamedArg("max") final Duration max) {
        ...
    }

}


在FXML中,以下各项按预期工作(调用默认构造函数):

...
<?import myNamespace.DurationSpinnerValueFactory?>
...
<Spinner fx:id="mySpinner">
  <valueFactory>
    <DurationSpinnerValueFactory />
  </valueFactory>
</Spinner>
...


但是,当我尝试为max属性添加值,从而更改被调用的构造函数时,出现错误。下面说明了FXML的更改:

<DurationSpinnerValueFactory max="PT10M" />


我得到的错误是:

javafx.fxml.LoadException:
unknown path:23

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2425)
    ...
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.RuntimeException: java.lang.IllegalArgumentException: Unable to coerce PT10M to class java.time.Duration.
    at com.sun.javafx.fxml.builder.ProxyBuilder.createObjectFromDefaultConstructor(ProxyBuilder.java:340)
    at com.sun.javafx.fxml.builder.ProxyBuilder.build(ProxyBuilder.java:223)
    at javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:763)
    at javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2823)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2532)
    ... 18 more
Caused by: java.lang.IllegalArgumentException: Unable to coerce PT10M to class java.time.Duration.
    at com.sun.javafx.fxml.BeanAdapter.coerce(BeanAdapter.java:496)
    at com.sun.javafx.fxml.builder.ProxyBuilder$Setter.invoke(ProxyBuilder.java:533)
    at com.sun.javafx.fxml.builder.ProxyBuilder.createObjectFromDefaultConstructor(ProxyBuilder.java:338)
    ... 22 more


我知道我可以将构造函数中max的类型更改为String,然后解析构造函数中的值。但是,我宁愿避免这样做,因为我知道max的类型应为Duration。有没有办法让FXMLLoader.load(...)能够从FXML内部解析Duration对象?

最佳答案



尝试使用“ 10m”代替“ PT10M”作为Duration的字符串表示形式,并始终使用javafx.util.Duration表示与主要与服务JavaFX GUI的代码相关联的所有持续时间相关类型。

解释性背景信息

您对持续时间类型和格式感到困惑。

您提供的“ PT10M”字符串是以下类型的字符串表示形式:

java.time.Duration

但是,在DurationSpinnerValueFactory中使用的Duration可能应该是以下类型:

javafx.util.Duration

当您查看FXMLLoader的代码时,正在尝试在valueOf上调用静态Duration函数。例如,对于javafx.util.Duration

javafx.util.Duration.valueOf

查看该函数的文档时,它指出有效输入应为:


  语法为“ [数字] [ms | s | m | h]”。


显然,这不是您所使用的。相反,您使用的是java.time.Duration.parse()格式。

编写以下语句并运行它会引发异常:

javafx.util.Duration.valueOf("PT10M");

Exception in thread "main" java.lang.NumberFormatException: empty String
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
    at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    at java.lang.Double.parseDouble(Double.java:538)
    at javafx.util.Duration.valueOf(Duration.java:85)


但是,如果您仅按以下方式进行编码,则可以正常执行:

javafx.util.Duration.valueOf("10m");



  我认为可以通过某种方式告诉FXMLLoader如何从FXML中的文本解析自定义类型。


是的,您可以执行此操作,尽管该文件尚未正式记录下来,但我自己也没有尝试过。通过查看如何处理javafx.util.Duration,可以提供实现静态valueOf函数的包装类型或java.time.Duration的子类。

作为另一种选择,我认为可以将构建器类与某些类类型相关联,以允许它们在FXMLLoader的上下文中进行解析和解析。我没有研究过构建器类型的解决方案,但是它可能依赖于反射或FXMLLoader中公开的某些API。参见,例如,FXMLLoader APIs that take builder factories as parameters。据我所知,绝对没有关于这种方法如何工作的文档,因此您可能需要依靠检查FXMLLoader代码和JavaFX运行时可能附带的一些示例构建器来支持在内部使用内置JavaFX控件。 FXML。

我对您的建议是更改自定义组件以使其与javafx.util.Duration而不是与java.time.Duration一起使用,这样就不必开始编写其他代码来改进不同的工期类型,而且您也不必结束弄乱了自己和其他任何试图使用该控件的人。

10-07 19:42
查看更多