我正在尝试在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
个对象?
答案 0 :(得分:1)
<强>解决方案强>
尝试使用&#34; 10m&#34;而不是&#34; PT10M&#34;作为持续时间的字符串表示形式,并始终使用javafx.util.Duration
来表示与主要用于服务JavaFX GUI的代码相关联的所有持续时间相关类型。
解释性背景资料
您对持续时间类型和格式感到困惑。
&#34; PT10M&#34;您提供的字符串是以下类型的字符串表示形式:
但是,您的DurationSpinnerValueFactory中使用的持续时间应该是以下类型:
FXMLLoader
在查看代码时正在尝试调用valueOf
上的静态Duration
函数。例如,对于javafx.util.Duration
:
当您查看该函数的文档时,它指出有效输入应为:
语法为&#34; [number] [ms | s | m | h]&#34;。
显然,这不是你正在使用的。相反,您使用的是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,然后您不必开始编写其他代码来改进不同的持续时间类型,此外,你不会混淆自己和其他试图使用控件的人。
答案 1 :(得分:1)
感谢@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 Enterprise SP1 x64运行JRE 8u77 x64。如果没有,那么我将只使得Duration
对象非公开的构造函数。