我正在做一些工作,要求我根据标量值对元素具有不同的值。我的工作是使用名为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
}
${}
语法用于创建expression binding,然后将其用于将Property
绑定到生成的ObservableValue
。您的代码的问题是Insets
不会将其状态显示为Property
实例。最重要的是,Insets
类是不可变的,这意味着状态是通过构造函数之一设置的,并且两个构造函数都仅接受double
参数。您无法传递表达式绑定的结果,因为double
和FXMLLoader
在创建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()
。