在javaFx中,为什么需要调用;getChildren();当我们向窗口添加元素时?

在javaFx中,为什么需要调用;getChildren();当我们向窗口添加元素时?,java,user-interface,javafx,Java,User Interface,Javafx,例如,当我们向窗格添加新按钮时,我们需要编写以下代码: StackPane pane = new StackPane(); pane.getChildren().add(new Button("OK")); 为什么我们需要调用“getChildren()?”它到底做了什么?为什么我们不能说: StackPane pane = new StackPane(); pane.add(new Button("OK")); 我们正在将按钮添加到窗格中。我们没有将其添加到其子项中,窗格跟踪其子项

例如,当我们向窗格添加新按钮时,我们需要编写以下代码:

 StackPane pane = new StackPane();
 pane.getChildren().add(new Button("OK"));
为什么我们需要调用“getChildren()?”它到底做了什么?为什么我们不能说:

 StackPane pane = new StackPane();
 pane.add(new Button("OK"));

我们正在将按钮添加到窗格中。我们没有将其添加到其子项中,

窗格跟踪其子项的数组。您不能直接调用
pane.add(新建按钮(“确定”))的原因
是因为StackPane类不提供该方法。您必须将元素直接添加到窗格的子数组。

而不是为子数组上的不同操作提供单独的方法,如
add()
addAll()
remove()
removeAll()
。。等等


提供众所周知的集合很简单,因此
列表
支持的每个操作都是可用的
,这将使类变得简单且不那么冗长。在本例中,
javafx.scene.layout.Pane
类将子类存储在
observeList
中,这是一个
列表

简单的回答是“您必须这样做,因为API就是这样编写的”。当然,您可能真正想问的是为什么API是这样编写的。我认为这相当于两个(相关的)问题。一个是关于方法名,以及该方法的作用,另一个是关于实际的API设计

一般来说,在计算中,重用现有的问题解决方案通常是有益的。(知道什么时候这样做是有益的是一门艺术,但在这两种情况下,这显然是一种好处。)通过两种不同的方式,这种API设计重用了已知问题的现有解决方案,从而使以前遇到过这些解决方案的程序员更容易理解和使用


场景图 从全局来看,考虑通用用户界面的总体结构。有一个大型容器(想想JavaFX中场景的根),其中包含各种UI组件。其中有些可能是简单的控件,如按钮和标签等,有些可能是其他容器,这些容器又包含各种UI组件。当然,其中一些也可能是容器,等等。如果不采用某种结构,这可能会变得复杂且难以使用

为了理解这个结构,我们把它抽象了。有一个根节点,它具有零个或多个其他节点的集合。每个节点都有一个由零个或多个其他节点组成的集合,依此类推。这是计算中一个非常著名的抽象结构,称为“树”,基本上每个计算机程序员(不管他们用哪种语言编程)都熟悉它。因此,如果我们认为它是一个树结构,因为我们已经熟悉它,我们可以使复杂性更容易处理。为了将其视为树结构,我们对类似树的方面使用标准名称。每个节点(根节点除外)都有一个“父节点”(它所属的容器):因此,您将在
节点
类(“节点”是树结构中的另一个术语)中找到一个名为的方法,该方法允许访问父节点(包含当前节点的容器)。类似地,包含其他节点的UI元素(节点)有一个名为
getChildren()
的方法,该方法返回当前节点中包含的所有节点的集合。OracleJavaFX教程中有一节介绍了这一点,其中有很多漂亮的图表和相关代码

因此,简而言之,之所以有一个名为
getChildren()
的方法,是因为我们将UI中所有内容的集合视为一个树结构,并且该方法名称准确地描述了该方法在所有UI元素的集合为一个树的上下文中的作用。术语“节点”、“父节点”和“子节点”对于有一点经验的程序员来说是可以立即识别的,并且有助于他们理解整体结构并使用它。他们基本上可以立即推断
getChildren()
返回该容器中立即包含的所有UI元素的列表(在您的示例中,该容器是
StackPane


Pane
s的API设计(如
StackPane
) 要考虑API设计,请考虑在操作
StackPane
中包含的内容方面可能需要做的所有事情。正如您所观察到的,您可能希望将一个新的UI元素(“节点”)添加到
堆栈窗格
,因此您可能需要一个名为
add(…)
的方法来接受
节点
。但你可能还需要或想做很多其他事情:

  • 从堆栈窗格中删除节点
  • 将整个节点集合添加到
    StackPane
  • StackPane
  • 堆栈窗格中删除(清除)所有节点
  • 由于堆栈窗格中节点的顺序很重要(它定义了z顺序,即如果节点重叠,它定义了在前面绘制的节点和在后面绘制的节点),因此您可能希望在特定位置插入节点(在某个对象的前面,但在另一个对象的后面)
  • 移除“后部”或前部或某个指定位置的节点
  • 还有很多很多其他的
因此,
StackPane
类的设计者本可以编写所有这些方法,但还有更好的方法

如果考虑实现
StackPane
,则需要某种方法来跟踪它包含的节点(UI元素)。这些节点的顺序很重要,因此我们必须跟踪它,并且我们需要有一个很好的方法来定义上面列出的所有功能。
StackPane
类(或其超类public class StackPane { private final List<Node> children = new ArrayList<>(); // or some other list implementation... public void add(Node node) { children.add(node); } public boolean remove(Node node) { return children.remove(node); } public void add(int index, Node node) { children.add(index, node); } public boolean remove(int index) { return children.remove(index); } public void addAll(Collection<Node> nodes) { children.addAll(nodes); } // lots more methods like this... // lots of layout code omitted... }
public class StackPane {
    private final List<Node> children = new ArrayList<>();

    public List<Node> getChildren() {
        return children ;
    }

    // layout code omitted...
}
StackPane pane = new StackPane();

Button button = new Button("OK");

pane.getChildren()
    // oooh, a List, I know how those work
    .add(button);

List<Label> lotsOfLabels = Arrays.asList(new Label("One"), new Label("Two"), new Label("Three"));

pane.getChildren()
    // still a list, I know more things I can do here:
    .addAll(lotsOfLabels);

pane.getChildren().remove(button);

pane.getChildren().clear();