我什么时候可以访问 FXML 属性?
When can I access a FXML property?
几天来我一直在尝试为 JavaFX 创建自定义组件,但 运行 遇到了问题。我的目的是创建一个带有自定义字段的组件,该组件的用户稍后可以对其进行编辑。
到目前为止一切顺利,该字段已经存在并且可以编辑。字段值是我的自定义字段,可以编辑。
奖励问题:1) 如何删除用户代理样式表字段? 2)该字段只接受正整数。我如何让它接受负整数?
问题是当我试图从我的代码中访问这个值时。这是组件的 .jar 和 .fxml
的代码
Simple.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 中访问值字段,但我无法启动我的应用程序:
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$launchApplication2(LauncherImpl.java:182)
at com.sun.javafx.application.LauncherImpl$$Lambda/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$launchApplication19(LauncherImpl.java:863)
at com.sun.javafx.application.LauncherImpl$$Lambda/198061478.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait2(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl$$Lambda/186276003.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null0(PlatformImpl.java:295)
at com.sun.javafx.application.PlatformImpl$$Lambda/1595566805.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater1(PlatformImpl.java:294)
at com.sun.javafx.application.PlatformImpl$$Lambda/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$null5(WinApplication.java:101)
at com.sun.glass.ui.win.WinApplication$$Lambda/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
方法中使用)?
而不是:
System.out.println( "Init: " + valueProperty.getValue() );
尝试:
System.out.println( "Init: " + getValue() );
如果注释 @NamedArg
. (The API docs give no information: see also 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 是否可以完全使用此功能。
James_D 展示了解决这个问题的方法,但它需要一个小星期,我 post 在这里供参考。
我必须更改两个构造函数才能使其正常工作。删除零参数构造函数并更改另一个:
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 文件中不存在 "value" 字段,则参数将为空,我们可以手动将值设置为 DEFAULT_VALUE.
几天来我一直在尝试为 JavaFX 创建自定义组件,但 运行 遇到了问题。我的目的是创建一个带有自定义字段的组件,该组件的用户稍后可以对其进行编辑。
到目前为止一切顺利,该字段已经存在并且可以编辑。字段值是我的自定义字段,可以编辑。
奖励问题:1) 如何删除用户代理样式表字段? 2)该字段只接受正整数。我如何让它接受负整数?
问题是当我试图从我的代码中访问这个值时。这是组件的 .jar 和 .fxml
的代码Simple.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 中访问值字段,但我无法启动我的应用程序:
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$launchApplication2(LauncherImpl.java:182)
at com.sun.javafx.application.LauncherImpl$$Lambda/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$launchApplication19(LauncherImpl.java:863)
at com.sun.javafx.application.LauncherImpl$$Lambda/198061478.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait2(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl$$Lambda/186276003.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null0(PlatformImpl.java:295)
at com.sun.javafx.application.PlatformImpl$$Lambda/1595566805.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater1(PlatformImpl.java:294)
at com.sun.javafx.application.PlatformImpl$$Lambda/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$null5(WinApplication.java:101)
at com.sun.glass.ui.win.WinApplication$$Lambda/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
方法中使用)?
而不是:
System.out.println( "Init: " + valueProperty.getValue() );
尝试:
System.out.println( "Init: " + getValue() );
如果注释 @NamedArg
. (The API docs give no information: see also 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 是否可以完全使用此功能。
James_D 展示了解决这个问题的方法,但它需要一个小星期,我 post 在这里供参考。
我必须更改两个构造函数才能使其正常工作。删除零参数构造函数并更改另一个:
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 文件中不存在 "value" 字段,则参数将为空,我们可以手动将值设置为 DEFAULT_VALUE.