繁体   English   中英

带有java.time.Duration的构造方法的自定义JavaFX组件

[英]Custom JavaFX Component with Constructor That Takes a java.time.Duration

我正在尝试在FXML中创建Spinner<Duration>对象。 我创建了一个DurationSpinnerValueFactory ,它扩展了SpinnerValueFactory<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的代码时,它正在尝试在Duration上调用静态valueOf函数。 例如,对于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 API 据我所知,绝对没有关于这种方法如何工作的文档,因此您可能需要依靠检查FXMLLoader代码和JavaFX运行时可能附带的一些示例构建器来支持在内部使用内置JavaFX控件。 FXML。

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

感谢@jewelsea向我指出FXMLLoader代码。 在检查了代码之后,很明显没有办法向FXML加载过程添加自定义解析器。 但是,任何未知类型T的默认操作是使用签名public static T valueOf(String value)反射性地调用静态方法。 因此,我只是为java.time.Duration创建了以下包装器类:

public class DurationWrapper {

    private final Duration myValue;

    private DurationWrapper(final Duration value) {
        myValue = value;
    }

    public static DurationWrapper valueOf(final String value) {
        final Duration duration = Duration.parse(value);
        final DurationWrapper result = new DurationWrapper(duration);
        return result;
    }

    public Duration getValue() {
        return myValue;
    }

}

然后,我修改了DurationSpinnerValueFactory构造函数:

public class DurationSpinnerValueFactory extends SpinnerValueFactory<Duration> {

    public DurationSpinnerValueFactory(
            @NamedArg("max") final DurationWrapper max) {
        this(max.getValue());
    }

    public DurationSpinnerValueFactory(final Duration max) {
        ...
    }

}

我仍然需要验证FXMLLoader.load(...)保证来选择需要的构造DurationWrapper在一个需要Duration ,但它正在开发Windows 7企业版SP1 64位运行JRE 8u77 64。 如果不是,那么我将使采用Duration对象的构造函数成为非公共对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM