【问题标题】:When can I access a FXML property?我什么时候可以访问 FXML 属性?
【发布时间】:2015-11-25 12:09:14
【问题描述】:

几天来,我一直在为 JavaFX 创建自定义组件,但遇到了问题。我的目的是创建一个带有自定义字段的组件,以后可以由组件的用户进行编辑。

到目前为止一切顺利,该字段在那里并且可以编辑。字段值是我的自定义字段,可以编辑。

额外问题:1) 我如何摆脱用户代理样式表字段? 2) 该字段只接受正整数。如何让它接受负整数?

问题是当我尝试从我的代码中访问此值时。这是组件的 .jar 和 .fxml 的代码

简单的.jar

public class Simple extends AnchorPane implements Initializable{

    public Simple(){
        //Loads the FXML sheet
        FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Simple.fxml") );
        fxmlLoader.setRoot(this); 
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }

        System.out.println( "Constructor: " + valueProperty.getValue() );
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        System.out.println( "Init: " + valueProperty.getValue() );
    }


    @FXML protected void printValue(ActionEvent ae){
        System.out.println( "Button: " + valueProperty.getValue() );
    }

    //Editor field for the  value (helps scene builder to render it)
    IntegerProperty valueProperty;
    public void setValue(Integer value){
        valueProperty().setValue(value);
    }
    public Integer getValue(){
        return valueProperty == null ? -10 :  valueProperty.get();
    }
    public final IntegerProperty valueProperty() {
        if( valueProperty == null )
            valueProperty = new SimpleIntegerProperty(-10);
        return valueProperty;
    }
}

Simple.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>

<fx:root type="AnchorPane" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">

      <Button mnemonicParsing="false" onAction="#printValue" text="Button" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />

</fx:root>

这些组件已被导出为名为 gikkWidgets 的外部库,然后导入到另一个项目中。正如我上面所说,可以在 Scene Builder 中访问 value 字段,但我无法启动我的应用程序:

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(LauncherImpl.java:182)
    at com.sun.javafx.application.LauncherImpl$$Lambda$50/1645995473.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javafx.fxml.LoadException: 
/D:/Project/gikkWidgets/bin/main/Test.fxml:12

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2605)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2583)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3218)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3179)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3152)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3128)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3108)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3101)
    at main.Test.start(Test.java:15)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
    at com.sun.javafx.application.LauncherImpl$$Lambda$53/198061478.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl$$Lambda$45/186276003.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
    at com.sun.javafx.application.PlatformImpl$$Lambda$48/1595566805.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
    at com.sun.javafx.application.PlatformImpl$$Lambda$47/237061348.run(Unknown Source)
    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$145(WinApplication.java:101)
    at com.sun.glass.ui.win.WinApplication$$Lambda$36/2117255219.run(Unknown Source)
    ... 1 more
Caused by: java.lang.RuntimeException: javafx.fxml.LoadException: 
/D:/Project/gikkWidgets/bin/com/gikk/javafx/simple/Simple.fxml

    at com.gikk.javafx.simple.Simple.<init>(Simple.java:26)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at java.lang.Class.newInstance(Class.java:442)
    at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.constructValue(FXMLLoader.java:1005)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:742)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2711)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2531)
    ... 22 more
Caused by: javafx.fxml.LoadException: 
/D:/Goats_Project/gikkWidgets/bin/com/gikk/javafx/simple/Simple.fxml

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2605)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2583)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2413)
    at com.gikk.javafx.simple.Simple.<init>(Simple.java:24)
    ... 32 more
Caused by: java.lang.NullPointerException
    at com.gikk.javafx.simple.Simple.initialize(Simple.java:34)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
    ... 35 more
Exception running application main.Test

我猜它源于创建属性时的某种时间问题。如果您注释掉 Simple.jar 第 29 行和第 34 行中的代码(构造函数 sysout 和初始化 sysout),那么它可以工作并且按钮显示正确的值。但是,在我正在处理的实际组件中,我需要在构建时访问用户所需的值。这有可能吗?


编辑

Puce 的回答解决了最初的问题,我试图访问一个未初始化的变量。

但是,应用他建议的修复(即,将 valueProperty.getValue() 更改为简单的 getValue() 会产生以下输出:

Constructor: -10
Init: -10
Button: 5

即,直到初始化之后才设置属性值。是否有可能以某种方式更早地读取属性值,以便在构造函数中使用它(或至少在initialize 方法中)?

【问题讨论】:

    标签: java javafx scenebuilder


    【解决方案1】:

    James_D 展示了一种解决此问题的方法,但它需要一小段时间,我在此发布以供参考。

    我必须更改两个构造函数才能使其工作。删除零参数构造函数并更改另一个:

    public Simple(@NamedArg("value") Integer value){
        //Loads the FXML sheet
        FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Simple.fxml") );
        fxmlLoader.setRoot(this); 
        fxmlLoader.setController(this);
    
        if(value != null )
            setValue(value);
        else
            setValue(DEFAULT_VALUE);
    
        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    
    
    
        System.out.println( "Constructor: " + getValue() );
    }
    

    此实现有效。如果 .fxml 文件中不存在“值”字段,则参数将为空,我们可以手动将值设置为 DEFAULT_VALUE。

    【讨论】:

    • 这里请注意:您已将属性设置为延迟初始化(即,如果您使用默认值并且从不调用 valueProperty(),则不会创建 IntegerProperty 实例)。但是,通过从构造函数调用setValue(...),您可以确保始终强制创建属性实例。因此,如果你使用这个构造函数,你应该摆脱惰性初始化;或者保持惰性初始化并省略构造函数中的else 子句(如果未调用setValue(...)getValue() 将返回DEFAULT_VALUE)。
    • 明白了。我太兴奋了,以至于我完全忘记了这一点。 :-)
    【解决方案2】:

    如果你注解@NamedArg,你可以将属性值作为参数提供给构造函数。 (API 文档未提供任何信息:另请参阅 here。)

    所以(包括对您的属性实现的轻微更改):

    public class Simple extends AnchorPane implements Initializable{
    
        private static final int DEFAULT_VALUE = -10 ;
    
        public Simple(@NamedArg("value") int value){
            //Loads the FXML sheet
            FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Simple.fxml") );
            fxmlLoader.setRoot(this); 
            fxmlLoader.setController(this);
    
            // set value, avoiding property creation if default...
            if (value != DEFAULT_VALUE) {
                setValue(value);
            }
    
            try {
                fxmlLoader.load();
            } catch (IOException exception) {
                throw new RuntimeException(exception);
            }
    
            System.out.println( "Constructor: " + getValue() );
        }
    
        @Override
        public void initialize(URL location, ResourceBundle resources) {
            System.out.println( "Init: " + getValue() );
        }
    
    
        @FXML protected void printValue(ActionEvent ae){
            System.out.println( "Button: " + getValue() );
        }
    
        //Editor field for the  value (helps scene builder to render it)
        IntegerProperty valueProperty;
        public final void setValue(Integer value){
            valueProperty().setValue(value);
        }
        public final Integer getValue(){
            return valueProperty == null ? DEFAULT_VALUE : valueProperty.get();
        }
        public final IntegerProperty valueProperty() {
            if( valueProperty == null )
                valueProperty = new SimpleIntegerProperty(DEFAULT_VALUE);
            return valueProperty;
        }
    }
    

    这是 JavaFX 8 及更高版本的功能:我不确定 SceneBuilder 是否可以完全使用它。

    【讨论】:

    • 您向我展示了方法:-DI 必须做一些小的更改,但可以修复:显然,0-args 构造函数是优先级的,即使字段“值”存在于.fxml。但是,如果不存在 0-args 构造函数,它总是选择那个。下一个问题,如果在构造函数被触发时没有“values”字段,它会将“int value”初始化为0。通过将其更改为整数来解决这个问题。这样,我们可以检查它是否为空 ^^
    • NamedArg 注释上还有一个defaultValue 属性。我还没有尝试过,而且正如评论的那样,根本没有任何文档。但是您可以尝试使用 @NamedArg(value="value", defaultValue="-10") int value 之类的参数作为参数。更新了删除默认构造函数的答案:感谢您指出这一点。
    • 令人印象深刻,defaultValue 参数同样有效,至少对于整数。感谢您的发现,我为此苦苦思索了好几个小时。
    【解决方案3】:

    代替:

     System.out.println( "Init: " + valueProperty.getValue() );
    

    试试:

     System.out.println( "Init: " + getValue() );
    

    【讨论】:

    • 哎呀,真是个错误。这修复了崩溃。但是,在构造函数和 init 中打印的值都是 -10(默认值),而不是分配的值。但我想这应该是另一个问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-09
    • 1970-01-01
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多