簡體   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