【问题标题】:Can you pass through a variable to Insets in FXML?您可以将变量传递给 FXML 中的 Insets 吗?
【发布时间】:2020-02-02 23:55:45
【问题描述】:

我正在做一些工作,要求我根据标量值为元素设置不同的值。我的工作是在一个名为 Sizer 的类中使用一个名为 getScalar() 的方法来计算标量值,该方法返回一个双精度值。下面是我现在拥有的部分 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
}

【问题讨论】:

  • 能否请您展示Sizer 课程?
  • 查看我的回复:Bind Font Size in JavaFX?
  • @Slaw 我编辑了帖子以包含 Sizer 的内容
  • @jewelsea 我查看了这篇文章,它确实有一些有趣的想法,但对于我的问题,它只与 Insets 一起使用。我可以使用表达式绑定来使其他值正常工作,但即使我用具体值 left="${2.0 * 10.0}" 替换其中一个方向,它也不起作用。我只是不确定是否可以使用表达式绑定设置 Insets。

标签: java fxml


【解决方案1】:

${} 语法用于创建expression binding,而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

&lt;fx:script&gt; 标签允许调用者将脚本代码导入或嵌入 FXML 文件中。可以使用任何 JVM 脚本语言,包括 JavaScript、Groovy 和 Clojure 等。脚本代码通常用于直接在标记或相关源文件中定义事件处理程序,因为事件处理程序通常可以用更松散类型的脚本语言编写,而不是用静态类型的语言(如 Java)编写。

我以前从未在 FXML 中使用过脚本,目前不知道如何在示例中使用它。可以这么说,你可以乱用它,但我不知道脚本是否是一个可行的解决方案。


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

Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();

获取主屏幕的尺寸。要获取整个屏幕的尺寸,而不仅仅是可视区域,请使用 getBounds() 而不是 getVisualBounds()

【讨论】:

  • 太棒了!这似乎有效。还要感谢有关 JavaFX API 参考的信息。
猜你喜欢
  • 1970-01-01
  • 2015-10-19
  • 2023-03-20
  • 1970-01-01
  • 2017-09-17
  • 1970-01-01
  • 1970-01-01
  • 2016-04-16
  • 1970-01-01
相关资源
最近更新 更多