您可以在FXML中将变量传递给Insets吗?

问题描述 投票:0回答:1

我正在做一些工作,要求我根据标量值对元素具有不同的值。我的工作是使用名为getScalar()的方法(该方法返回一个double值)在名为Sizer的类中得出标量值。下面是我现在拥有的部分FXML。

<VBox id="mainWindow" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller">
   <fx:define>
      <Sizer fx:id="SCALAR" fx:factory="getScalar"/>
   </fx:define>
   <children>
     <HBox alignment="CENTER_LEFT">
        <children>
           <Label fx:id="titleLabel" text="Label" HBox.hgrow="ALWAYS" />
           <HBox fx:id="titleBar" HBox.hgrow="ALWAYS" />
        </children>
        <padding>
           <!-- This part DOESN'T work at runtime -->
           <Insets left="${SCALAR * 10.0}" right="${SCALAR * 10.0}" top="${SCALAR * 5.0}" />
        </padding>
     </HBox>
     <!-- This part DOES work at runtime -->
     <Separator prefWidth="${SCALAR * 200.0}" />
  </children>
</Vbox>

在此代码中,如果我取出Insets引用并将这些值设置为10.0、5.0等,则它将在运行时起作用,并出现我的对话框。使用SCALAR的'prefWidth'设置似乎工作正常,但是当我添加对Insets值的引用时,它会抛出错误并指向带有Insets的行。

javafx.fxml.LoadException: Cannot bind to untyped object.

at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2621)
at javafx.fxml.FXMLLoader.access$100(FXMLLoader.java:105)
at javafx.fxml.FXMLLoader$Element.processPropertyAttribute(FXMLLoader.java:306)
at javafx.fxml.FXMLLoader$Element.processInstancePropertyAttributes(FXMLLoader.java:242)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:757)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2722)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2435)

我的理解

您可以通过定义块创建这些定义的对象,然后通过表达式绑定和变量解析在其他属性中引用它们,但是以某种方式,我无法使用Insets进行此操作。我发现的唯一一件事是,“ prefWidth”具有一个带双精度设置的二传手,而“左”或具有Insets的其他位置则具有getter。我以为也许绑定到一个似乎没有设置器的对象可能会有问题。

任何帮助都将不胜感激,因为我对FXML并不了解,而且我来自JavaFX世界,这比这容易得多。

编辑下面是Sizer类的内容

public static Toolkit getToolKit() {
    return Toolkit.getDefaultToolkit();
}
public static Dimension getScreenSize() {
    return getToolKit().getScreenSize();
}
public static int getPercentOfHeight(double percent) {
    return (int)(getScreenSize().getHeight() * percent / 100.0);
}
public static int getPercentOfWidth(double percent) {
    return (int)(getScreenSize().getWidth() * percent / 100.0);
}
public static double getScaleFactor() {
    if ((int) getScreenSize().getHeight() > 1800) return 2.0;    //4K
    else if ((int)getScreenSize().getHeight() > 1260) return 1.33; //1440p
    else if ((int)getScreenSize().getHeight() > 900) return 1.0;     //1080p
    else return 0.667;                                   //720p
}
java fxml
1个回答
2
投票

${}语法用于创建expression binding,然后将其用于将Property绑定到生成的ObservableValue。您的代码的问题是Insets不会将其状态显示为Property实例。最重要的是,Insets类是不可变的,这意味着状态是通过构造函数之一设置的,并且两个构造函数都仅接受double参数。您无法传递表达式绑定的结果,因为doubleFXMLLoader在创建Insets实例时不会“巧妙地”提取当前值。

据我了解,您想将Insets的每一边乘以单个double值,而这只需要发生一次(即,您不需要随着时间的推移更新值)。在这种情况下,一种解决方案是将Insets子类化,并为标量参数的每个构造函数“添加参数”。

package com.example;

import javafx.beans.NamedArg;
import javafx.geometry.Insets;

public class ScaledInsets extends Insets {

    public ScaledInsets(@NamedArg(value = "scale", defaultValue = "1") double scale,
                        @NamedArg("top") double top, @NamedArg("right") right,
                        @NamedArg("bottom") double bottom, @NamedArg("left") double left) {
        super(scale * top, scale * right, scale * bottom, scale * left);
    }

    public ScaledInsets(@NamedArg(value = "scale", defaultValue = "1") double scale,
                        @NamedArg("topRightBottomLeft") double topRightBottomLeft) {
        super(scale * topRightBottomLeft);
    }

}

允许您在FXML文件中使用以下内容:

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

<?import com.example.ScaledInsets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?>

<StackPane xmlns="http://javafx.com/javafx/" xmlns:fx="http://javafx.com/fxml">
    <fx:define>
        <Sizer fx:id="SCALAR" fx:factory="getScaleFactor"/>
    </fx:define>
    <padding>
        <ScaledInsets scale="$SCALAR" top="5" left="10" right="10"/>
    </padding>
    <Label text="Hello, World!"/>
</StackPane>

注意,FXML也支持scripting

<fx:script>标记允许调用者将脚本代码导入FXML文件或将脚本嵌入FXML文件中。可以使用任何JVM脚本语言,包括JavaScript,Groovy和Clojure等。脚本代码通常用于直接在标记或关联的源文件中定义事件处理程序,因为与使用静态类型的语言(例如Java)相比,事件处理程序通常可以使用更为宽松的脚本语言来更简洁地编写。

我以前从未在FXML中使用脚本,并且目前不知道如何在示例中使用它。可以这么说,您可以弄乱它,但是我不知道脚本编写是否是可行的解决方案。


与您的问题无关,但您似乎正在使用AWT类来获取屏幕尺寸。 JavaFX为此提供了自己的API:javafx.stage.Screen。您可以使用:

javafx.stage.Screen

获取主屏幕的尺寸。要获取整个屏幕的尺寸,而不仅仅是视觉区域,请使用Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds(); 而不是getBounds()

© www.soinside.com 2019 - 2024. All rights reserved.