[英]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”字符串是以下类型的字符串表示形式:
但是,在DurationSpinnerValueFactory中使用的Duration可能应该是以下类型:
当您查看FXMLLoader
的代码时,它正在尝试在Duration
上调用静态valueOf
函数。 例如,对于javafx.util.Duration
:
当您查看该功能的文档时,它指出有效的输入应为:
语法为“ [数字] [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.