当前位置: 首页 > 知识库问答 >
问题:

JavaFX,按Id查找节点

甄胡非
2023-03-14

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

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

进一步的研究让我在场景类中查找方法,但是官方留档(https://docs.oracle.com/javase/8/javafx/api/javafx/scene/Scene.html#lookup-java.lang.String-)缺乏对这种方法如何工作的描述,在我看来是不准确的(我费了很大劲才让事情运转起来)。

如本文所述(如何在JavaFX中找到ID为的元素?)方法查找的调用必须在方法显示之后进行,为什么?

第二个问题是为什么节点id前面必须加“#”(散列符号)?虽然官方文件规定它应该是“…CSS选择器…”?(我在这里有点困惑,因为类节点只包含一个类似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());
    }
}  

问题:

行<代码>画布板=(画布)主场景。查找(“绘图板”) 阶段之前不能调用code>。show() 它将产生空指针错误。为什么?

共有2个答案

易骁
2023-03-14

这个答案是对克利奥帕特拉答案的补充。

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

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

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

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

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

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

不要混淆FXML中使用的fx: id和CSS ID,它们是不同的东西,尽管您可能经常想将它们赋值为相同的值,您可以在FXML中的元素定义中这样做,例如:

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

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

@FXML Button myButton;

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

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

查找基于CSS选择器

下面是一个从节点查找留档按ID查找的示例:

  • 例如,如果一个节点的id是myId,那么可以使用查找方法来查找这个节点,如下所示:scene.lookup("#myId");

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

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

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

如果你确实想做一个jQuery风格的查找(这就是JavaFX方法Lookup方法的类型),那么在何时以及如何做的时候要非常小心,因为kleopatra在由皮肤插入的节点上的第二点:

  • 蒙皮插入的所有节点仅在蒙皮连接后可用,也就是在显示(f.i.“#drawBoard”,它被注入到splitPane的项目中)

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

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

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

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

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

  • 生成布局传递OR
  • 显示相关阶段(这将导致布局传递)OR
  • P中进行查找latform.run稍后()块。
    • 这将等待下一个脉冲,所以当前脉冲的布局在执行前就完成了。尽管Platform.run延迟()规范并不能保证这一点,实际上,这就是它的工作方式。

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

    要生成布局过程,请执行以下操作:

    1. 使用适当的CSS样式和

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

    没有明确布局过程的查找(小心)

    如果您知道要查找的元素已经在场景中,那么您可以直接look()节点,它们将被找到,例如,如果您只是在先前的一段代码中显式地将它们添加到场景中,因此您知道它们不需要由后续脉冲上的Skin创建。

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

    总结

    从这个答案可以看出,JavaFXlookup()方法的使用带有一些警告和注意事项。这就是主要建议避免使用它们的原因,除非它们为您的特定应用程序带来巨大好处。

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

姚高爽
2023-03-14

你引用的评论是:

如本文所述(如何在JavaFX中找到ID为的元素?)方法查找的调用必须在方法显示之后进行?

总的来说是错误的。加载后能否立即查找节点取决于添加节点的方式:

  • 作为子节点注入的所有节点都可以直接在loader之后使用(f.i.“#mainSplitPane”作为根节点的子节点)
  • 蒙皮插入的所有节点仅在蒙皮连接后可用,也就是在显示(f.i.“#drawBoard”,它被注入到splitPane的项目中)
 类似资料:
  • 问题内容: 我想通过查找数据。我知道该数据存在并且存在(我已经用pymongo测试了它)。 但是下面的代码找不到它: 它只是给我一个回报。 但是我可以使用pytmongo和python找到它。 结果如下: 有人有什么主意吗? 编辑:我已经尝试了: 但我仍然有0: 问题答案: 您可以使用然后仅传递id值,或者使用,然后还必须使用字段名称指定一个值: 如果没有错误,则表示找到了文档。 如果您始终看到已

  • 我正在使用selenium Java搜索不同的网站,我知道要查找特定的WebElement,有不同的方法,比如使用Xpath、使用Class或使用id。假设某网站具有如下所示的html结构 现在,如果我想从类名为“score”和“score_title”的div中提取文本,即1%的强度、43:1的情感、75%的激情和12%的到达,下面哪个选项最好。

  • 我试图创建一个HTML编辑器(学习目的),我试图使程序尽可能动态。我希望通过它的id查找FXML变量,如TextField,并在按下按钮时检索TextField中的文本。 我设置了2个映射变量 我希望类似于通过ActionEvent获取按钮ID/值的方式。 编辑:以下是完整的FXML btw

  • 我想通过我提供的id从托管bean中找到一些。

  • 我在我的一个项目中使用了neo4j,有一个节点只有一个属性,我想使用ID获得这个节点,它已经有一个ID了,但是当我使用这个代码时 它什么也不返回,这是我的节点 如果查询错误,那么如何使用数字查询

  • 问题内容: 我需要使用ObjectIdHex获取值,并进行更新并查看结果。我正在使用mongodb和golang,但是下面的代码无法正常工作 没有为我工作,并给我以下输出 我该如何解决这个问题?我需要使用oid获得价值并进行更新,我该怎么做 问题答案: 应该不是: