JavaFX FXML:无法绑定到静态属性

时间:2014-12-04 09:23:51

标签: javafx fxml

我正在尝试为多种屏幕分辨率(也是高分辨率)创建一个JavaFX应用程序。

我想在我的FXML文件(JavaFX项目)中使用变量/计算。 这只适用于我只在prefWidth,prefHeight等中进行计算的情况。当尝试在(例如)AnchorPane.topAnchor中进行计算时,它会给我以下错误。

-- exec-maven-plugin:1.2.1:exec (default-cli) @ SecureChat ---
dec 04, 2014 10:13:50 AM securechat.helpers.CustomStage initialize
SEVERE: null
javafx.fxml.LoadException: Cannot bind to static property.
file:/D:/private_java/SecureChat/target/SecureChat-1.0-SNAPSHOT.jar!/fxml/main.fxml:24

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2591)
    at javafx.fxml.FXMLLoader.access$100(FXMLLoader.java:104)
    at javafx.fxml.FXMLLoader$Element.processPropertyAttribute(FXMLLoader.java:291)
    at javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:771)
    at javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2817)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2526)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2435)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2403)
    at securechat.helpers.CustomStage.initialize(CustomStage.java:111)
    at securechat.helpers.CustomStage.<init>(CustomStage.java:104)
    at securechat.MainApp.start(MainApp.java:40)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(LauncherImpl.java:821)
    at com.sun.javafx.application.LauncherImpl$$Lambda$51/967161415.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(PlatformImpl.java:323)
    at com.sun.javafx.application.PlatformImpl$$Lambda$45/584634336.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$164(PlatformImpl.java:292)
    at com.sun.javafx.application.PlatformImpl$$Lambda$47/1040960283.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(PlatformImpl.java:291)
    at com.sun.javafx.application.PlatformImpl$$Lambda$46/501263526.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$141(WinApplication.java:102)
    at com.sun.glass.ui.win.WinApplication$$Lambda$37/96639997.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)

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:483)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:363)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:303)
    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:483)
    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:875)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$147(LauncherImpl.java:157)
    at com.sun.javafx.application.LauncherImpl$$Lambda$48/815033865.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException: Root cannot be null
    at javafx.scene.Scene.<init>(Scene.java:313)
    at javafx.scene.Scene.<init>(Scene.java:181)
    at securechat.helpers.CustomStage.initialize(CustomStage.java:120)
    at securechat.helpers.CustomStage.<init>(CustomStage.java:104)
    at securechat.MainApp.start(MainApp.java:40)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(LauncherImpl.java:821)
    at com.sun.javafx.application.LauncherImpl$$Lambda$51/967161415.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(PlatformImpl.java:323)
    at com.sun.javafx.application.PlatformImpl$$Lambda$45/584634336.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$164(PlatformImpl.java:292)
    at com.sun.javafx.application.PlatformImpl$$Lambda$47/1040960283.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(PlatformImpl.java:291)
    at com.sun.javafx.application.PlatformImpl$$Lambda$46/501263526.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$141(WinApplication.java:102)
    at com.sun.glass.ui.win.WinApplication$$Lambda$37/96639997.run(Unknown Source)
    ... 1 more
Exception running application securechat.MainApp
------------------------------------------------------------------------
BUILD FAILURE
------------------------------------------------------------------------
Total time: 9.363s
Finished at: Thu Dec 04 10:13:51 CET 2014
Final Memory: 19M/265M
------------------------------------------------------------------------
Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.2.1:exec (default-cli) on project SecureChat: Command execution failed. Process exited with an error: 1 (Exit value: 1) -> [Help 1]

To see the full stack trace of the errors, re-run Maven with the -e switch.
Re-run Maven using the -X switch to enable full debug logging.

For more information about the errors and possible solutions, please read the following articles:
[Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

当我在Main.fxml中删除AnchorPane.leftAnchor =“$ {dpi.value * 200}”(在最后一个AnchorPane中)时,它确实有效。

我正在使用以下代码段。

Main.fxml

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import securechat.helpers.Scaling?>

<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="securechat.controllers.MainController">
    <children>
        <fx:define>
            <Scaling fx:id="dpi"></Scaling>
        </fx:define>
        <AnchorPane fx:id="chatsAnchorPane" maxWidth="${dpi.value*200}" minWidth="${dpi.value*200}" prefWidth="${dpi.value*200}" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
            <children>
                <Button fx:id="openNewChatWindowButton" onAction="#openNewChatDialog" text="%button.openNewChatDialog" prefHeight="${dpi.value*40}" maxHeight="${dpi.value*40}" minHeight="${dpi.value*40}" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
                <ListView fx:id="chatsListView" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="40.0" />
            </children>
        </AnchorPane>
        <AnchorPane fx:id="chatAnchorPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="${dpi.value*200}" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
            <children>
                <ListView fx:id="chatMessagesListView" AnchorPane.bottomAnchor="40.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
                <TextField fx:id="chatMessageTextField" onAction="#sendChatMessage" promptText="%textfield.chatMessagePrompt" maxHeight="${dpi.value*40}" minHeight="${dpi.value*40}" prefHeight="${dpi.value*40}"  AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" />
            </children>
        </AnchorPane>
    </children>
</AnchorPane>

Scaling.java

package securechat.helpers;

import java.awt.GraphicsEnvironment;

/**
 *
 */
public final class Scaling {

    public double value;

    public Scaling() {
        value = getDefaultScaling();
    }

    private double getDefaultScaling() {
        int width = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().width;
        return width / 1920;
    }

    public double getValue() {
        return value;
    }

}

2 个答案:

答案 0 :(得分:3)

经过一些调试后,我发现在FXMLLoader课程中有几个LinkedList<Attribute>列表。一个用于实例属性属性,如prefHeight="400.0",一个用于静态属性属性,如AnchorPane.bottomAnchor="0.0"

如果您使用prefHeight="400.0"之类的常规值或prefWidth="${dpi.value*200}"之类的绑定表达式,则会使用此方法processPropertyAttribute()处理这些案例。

对于绑定,有三个初步检查:

    @SuppressWarnings("unchecked")
    public void processPropertyAttribute(Attribute attribute) throws IOException {
        String value = attribute.value;
        if (isBindingExpression(value)) {
            // Resolve the expression
            Expression expression;

            if (attribute.sourceType != null) {
                throw constructLoadException("Cannot bind to static property.");
            }

            if (!isTyped()) {
                throw constructLoadException("Cannot bind to untyped object.");
            }

            if (this.value instanceof Builder) {
                throw constructLoadException("Cannot bind to builder property.");
            }
            // Evaluate the expression
            ...
       } else {
            processValue(attribute.sourceType, attribute.name, value);
       }

虽然prefWidth="${dpi.value*200}"通过了这些检查,但相反,AnchorPane.leftAnchor="${dpi.value*200}"您会发现它有一个sourceType:

attribute.sourceType = (java.lang.Class) class javafx.scene.layout.AnchorPane

所以设计会发生这种情况:

if (attribute.sourceType != null) {
     throw constructLoadException("Cannot bind to static property.");
}

双向绑定也是不可能的:

if (isBidirectionalBindingExpression(value)) {
    throw constructLoadException(new UnsupportedOperationException("This feature is not currently enabled."));
}

如果你看docs

  

表示静态属性的属性与静态属性元素的处理方式类似,并使用类似的语法。   除了更简洁的静态属性属性(如实例属性属性,支持位置,资源和变量解析运算符)之外,唯一的限制是无法创建绑定到静态属性的表达式。

你需要找到另一种方式......

修改

基于提供的FXML,我建议的一种避免静态属性的方法是使用HBoxVBox,通过绑定使用所需的min / pref / max大小,包装控件。 / p>

这样的事情:

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import securechat.helpers.Scaling?>

<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="securechat.controllers.MainController">
    <children>
        <fx:define>
            <Scaling fx:id="dpi"></Scaling>
        </fx:define>
        <HBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"  AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <children>
            <VBox fx:id="chatsVBox" maxWidth="${dpi.value*200}" minWidth="${dpi.value*200}" prefWidth="${dpi.value*200}" HBox.hgrow="NEVER">
               <children>
                      <Button fx:id="openNewChatWindowButton" onAction="#openNewChatDialog" text="%button.openNewChatDialog" maxHeight="${dpi.value*40}" minHeight="${dpi.value*40}" prefHeight="${dpi.value*40}" prefWidth="${dpi.value*200}" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" VBox.vgrow="NEVER" />
                      <ListView fx:id="chatsListView" VBox.vgrow="ALWAYS" />
               </children>
            </VBox>
            <VBox fx:id="chatVBox" HBox.hgrow="ALWAYS">
               <children>
                      <ListView fx:id="chatMessagesListView"  VBox.vgrow="ALWAYS" />
                      <TextField fx:id="chatMessageTextField" onAction="#sendChatMessage" maxHeight="${dpi.value*40}" minHeight="${dpi.value*40}"  prefHeight="${dpi.value*40}" promptText="%textfield.chatMessagePrompt" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" VBox.vgrow="NEVER" />
               </children>
            </VBox>
         </children>
      </HBox>
    </children>
</AnchorPane>

答案 1 :(得分:1)

您指出的主要信息是,无法在静态属性中使用表达式,并且永远不会使用变量。

表达式用$ {}编写,必须绑定到属性,“变量”只用$ var写入,并通过getter访问。

我结束了这个可怕的解决方法:AnchorPane.leftAnchor="$dpi.value200"

在Scaling类中实现尽可能多的方法:

public final class Scaling {

    public double value;

    public Scaling() {
        value = getDefaultScaling();
    }

    private double getDefaultScaling() {
        int width =  GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().width;
        return width / 1920;
    }

    public double getValue() {
        return value;
    }

    public double getValue100() {
        return value*100;
    }
    public double getValue200() {
        return value*200;
    }
    public double getValue300() {
        return value*300;
    }
    /* etc.*/ 

}

另外,变量分析中存在一种错误,不要将其称为“200Value”(或者像我所说的那样称为“200dp”)。如果变量以数字字符开头,它将停止评估。