繁体   English   中英

JavaFX,通过Id查找节点

[英]JavaFX, finding node by Id

我最近遇到了问题,我需要从窗口中使用的节点获取特定节点(最好通过 id)。 我使用 FXMLLoader,所以第一个想法是搜索 FXMLLoader 返回的树结构。

root|  
    |                
    |--some_container |- child1   
    |                 |- child2
    |      
    |--other_container |-child3
    ...

进一步的研究让我在Scene类中进行了方法查找,但官方文档( https://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html#lookup-java.lang.String- )缺乏对这种方法如何工作的描述,并且在我看来是不准确的(我努力使事情发挥作用)。

正如这里提到的( How to find an element with an ID in JavaFX? )方法查找的调用必须方法show 之后,为什么?

第二个问题是为什么节点id前面必须有“#”(哈希符号)? 虽然官方文档指出它应该是“...CSS 选择器...”? (我在这里有点困惑,因为类 Node 只包含一个类似 id 的属性)

Edi1 - @kleopatra 的简单示例,尽管问题不是严格的代码,而是关于理解。

包含场景内容的示例 fxml 文件:

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

<?import javafx.scene.canvas.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.2" xmlns:fx="http://javafx.com/fxml/1" fx:controller="display.windows.ConsoleForBoard">
   <children>
      <SplitPane fx:id="mainSplitPane" dividerPositions="0.7" layoutX="277.0" layoutY="100.0" prefHeight="160.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <items>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="398.0" prefWidth="404.0">
               <children>
                  <SplitPane dividerPositions="0.8" layoutX="150.0" layoutY="53.0" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                    <items>
                      <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
                           <children>
                              <Canvas fx:id="drawBoard" height="313.0" layoutX="125.0" layoutY="43.0" width="413.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
                           </children>
                        </AnchorPane>
                      <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
                           <children>
                              <TextArea layoutX="107.0" layoutY="-86.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
                           </children>
                        </AnchorPane>
                    </items>
                  </SplitPane>
               </children>
            </AnchorPane>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
               <children>
                  <ToggleButton layoutX="35.0" layoutY="62.0" mnemonicParsing="false" text="ToggleButton" />
                  <TextField alignment="CENTER" layoutX="2.0" layoutY="14.0" promptText="Main Menu" text="Main Menu" />
               </children>
            </AnchorPane>
        </items>
      </SplitPane>
   </children>
</AnchorPane>

注意:画布有 fx:id="drawBoard"

创建此场景的示例类:

public class BoardWithMenu extends Application {
    Scene mainScene;
    AnchorPane root;
    FXMLLoader loader;

    public static void start() {launch("start");}
    @Override
    public void start(Stage stage) throws Exception {
        loader = new FXMLLoader(this.getClass().getResource("scene_definition.fxml"));
        root = loader.load();
        mainScene = new Scene(root, 500, 500);

        stage.setScene(mainScene);
        stage.setTitle("Space map");
        stage.show();

        Canvas board = (Canvas) mainScene.lookup("drawBoard");
        System.out.println(board.getId());
    }
}  

问题:

Line Canvas board = (Canvas) mainScene.lookup("#drawBoard"); 不能在stage.show();之前调用stage.show(); 它会产生空指针错误。 为什么?

你引用的评论:

如此处所述( 如何在 JavaFX中查找具有 ID 的元素? )方法查找的调用必须在方法显示之后?

作为一般性陈述是错误的。 是否可以在加载后立即查找节点取决于它是如何添加的:

  • 所有作为子节点注入的节点在加载程序之后都可以直接使用(fi "#mainSplitPane" 作为 root 的子节点)
  • 皮肤插入的所有节点仅在附加皮肤后才可用,即显示后(fi "#drawBoard" 注入到 splitPane 的项目)

这个答案是对kleopatra's answer 的补充。

除非您确实需要查找,否则使用 Java 变量引用而不是查找

我的建议是尽可能避免使用基于 CSS 的查找方法(支持 Java 中的直接类型引用)。

这个建议在使用 FXML 时尤其适用。 FXML 能够将fx:id分配给节点,该节点可以映射到控制器中的命名、类型字段(使用@FXML注释)。 通常,当涉及 FXML 时,这是通过 ID 引用节点的首选方法。 出于多种原因,它是首选:

  1. 当使用 Java 变量引用而不是查找时,您有类型化信息和编译时检查,而不是无类型运行时检查。
  2. 查找可能对时间敏感:正确的结果可能需要布局传递和/或 CSS 应用程序。
  3. 在布局传递和呈现阶段,控件上皮肤的 JavaFX 框架实现可能会添加或删除各种节点,这些节点会改变查找结果。
  4. 使用查找,您可以找到已通过私有皮肤实现添加到场景中的节点。 然后编写依赖于那些找到的节点的代码,你就打破了封装。 以后的 JavaFX 版本可能会更改私有皮肤实现,这会更改皮肤添加的结构或节点,然后破坏您的代码。

以上几点可以使在使用查找而不是依赖 Java 变量引用时更容易对错误进行编码。

了解 id 和 fx:id 之间的区别

不要将 FXML 中使用的fx:idCSS ID混淆,它们是不同的东西,尽管您可能经常希望将它们分配为相同的值,但您可以在 FXML 中的元素定义中这样做,例如:

<Button fx:id="myButton" id="myButton">

fx:id值将用于映射到具有@FXML注释的控制器中的变量引用(如果未提供注释,则通过对公共成员的反射):

@FXML Button myButton;

id值将映射到存储在节点中并与之关联的id 相关的文档是这样描述的:

此节点的 ID。 这个简单的字符串标识符对于在场景图中查找特定节点很有用。 虽然节点的 id 在场景图中应该是唯一的,但这种唯一性不是强制执行的。 这类似于 HTML 元素上的“id”属性(CSS ID 规范)。

查找基于 CSS 选择器

以下是节点查找文档中按 ID 查找的示例:

  • 例如,如果一个节点被赋予了“myId”的id,那么可以使用lookup方法来找到这个节点,如下所示: scene.lookup("#myId"); .

可以在任何节点或场景上调用查找。

节点lookup(selector)方法基于节点id而不是fx:id因为查找字符串基于 css。 查找使用css 选择器 因此,基于 ID 的查找将在 ID 前面有一个#前缀,因为这是通过 ID 选择节点的语法。 您不仅限于通过 ID 查找。 CSS 选择器语法非常强大,而且本身就是一种语言,因此您可以使用复杂的表达式来查询场景图节点树并根据需要提取匹配节点。

请注意,皮肤实现可以修改场景图

如果您确实想做 jquery 样式查找(这就是 JavaFX 方法lookup方法的类型),那么由于 kleopatra 在皮肤插入的节点上的第二个点,在何时以及如何执行此操作时要非常小心:

  • 皮肤插入的所有节点仅在附加皮肤后才可用,即显示后(fi "#drawBoard" 注入到 splitPane 的项目)

另请注意,可以通过 Controls 的fx-skin属性在 css 中应用fx-skin 因此,即使是 CSS 的应用程序也可以改变皮肤,这可以改变可以改变查找结果的场景图。

如何确保在查找时找到预期的节点

在进行查找之前,确保所有预期节点都在要查找的场景中并应用了预期属性(例如大小、颜色)等:

首先,将所需的节点添加到场景中。

其次,执行以下操作之一:

  1. 生成布局通道
  2. 显示相关阶段(这将导致布局通过)或
  3. Platform.runLater()块中进行查找。
    • 这将等待下一个脉冲,因此在执行之前完成当前脉冲的布局。 尽管Platform.runLater()规范并不能保证这一点,但实际上,这就是它的工作原理。

在大多数情况下,我首选的解决方案是生成布局通道。

要生成布局过程:

  1. 使用适当的 CSS 样式将所有必需的节点添加到场景中
  2. 在场景根或适当的父节点上调用applyCss()layout()方法。

然后调用所需的查找方法来查找节点。

没有显式布局传递的查找(小心)

如果您知道要查找的元素已经在场景中,那么您可以直接lookup()节点,它们将被找到。 例如,如果您只是在之前的一段代码中明确地将它们添加到场景中,那么您就知道它们不需要在后续脉冲中由皮肤创建。

但是,在执行布局之前,查找的节点可能不会返回某些属性(例如高度或宽度)的正确初始化值。 类似地,在将 CSS 应用于节点之前,由 CSS 设置的查找节点的任何属性都可能不正确。 因此,为了安全起见,我的建议是谨慎行事,并确保在尝试查找节点之前将布局和 CSS 应用于要查找的节点。

概括

正如可能从这个答案中得出的那样,JavaFX lookup()方法的使用会带来一些警告和注意事项。 这就是主要建议避免使用它们的原因,除非它们为您的特定应用程序增加了很大的好处。

请注意,查找可能有很大的好处:它们非常强大,某些 jquery 开发人员使用类似功能实现的效果令人难以置信,只是要小心使用它们的方式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM