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

时间:2017-02-01 23:13:14

标签: java user-interface javafx

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

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

为什么我们需要拨打" getChildren()?"它甚至做了什么?为什么我们不能说:

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

我们将按钮添加到窗格。我们不会将其添加到其子女身上,

3 个答案:

答案 0 :(得分:5)

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

通常,在计算中,将现有解决方案重用于问题通常是有益的。 (知道何时执行此操作是有益的,但在这两种情况下,它显然都是一种好处。)这种API设计以两种不同的方式重用现有解决方案来解决众所周知的问题,因此,对于之前遇到过这些解决方案的程序员来说,更容易理解和使用。

场景图

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

要理解结构,我们将其抽象化。有一个根节点,其中包含零个或多个其他节点的集合。其中每个都有零个或多个其他节点的集合,依此类推。这是一个众所周知的计算抽象结构,称为“树”,基本上每个计算机程序员(无论他们编程的是哪种语言)都熟悉它。因此,如果我们将其视为树结构,因为我们已经熟悉它,我们可以使复杂性更容易使用。为了将其视为树结构,我们对树状方面使用标准名称。每个节点(根节点除外)只有一个" parent" (它所属的容器):所以你在Node类中找到一个方法("节点"是树结构的另一个术语),称为getParent() ,它允许访问父节点(包含当前节点的容器)。类似地,包含其他节点的UI元素(节点)具有名为getChildren()的方法,该方法返回当前节点中包含的所有节点的集合。 Oracle JavaFX教程有一个关于Scene graph的部分描述了这一部分,其中包含许多漂亮的图表和相关代码。

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

Pane s的API设计(例如StackPane

要考虑API设计,请考虑在操纵StackPane中包含的内容时可能要做的所有事情。如您所见,您可能希望向StackPane添加新的UI元素("节点"),因此您可能需要一个名为add(...)的方法接受Node }。但是,您可能还需要或想要做其他一些事情:

  • StackPane
  • 中删除节点
  • 将整个节点集合添加到StackPane
  • StackPane
  • 中删除整个节点集合
  • 从(清除)StackPane
  • 中删除所有节点
  • 由于堆栈窗格中节点的顺序很重要(它定义了z顺序,即如果节点重叠,它定义了在前面和后面绘制的节点),您可能希望在特定位置插入节点(在某事之前,但在其他事情之后)
  • 删除"后面"或前面或某个指定位置的节点
  • 以及许多其他人

所以StackPane类的设计师可能刚刚编写了所有这些方法,但是有更好的方法。

如果您考虑实施StackPane,您需要一些方法来跟踪它包含的节点(UI元素)。这些节点的顺序很重要,因此我们必须跟踪它,我们需要有一个很好的方法来定义上面列出的所有功能。 StackPane类(或其超类,Pane)的作者可以构建一个从头开始执行此操作的数据结构,但标准库中已存在一个。保存某些特定类型的对象集合并跟踪其顺序的数据结构称为List 1 List接口是< em>每个Java程序员都熟悉。

因此,如果您考虑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...
}

我认为你明白了。所有这些代码都没有真正做任何事情;它只是调用已经定义的行为。因此,只需通过提供对列表本身的访问 2 ,就可以为StackPane用户提供完全相同的功能,而不是这个臃肿的API:

public class StackPane {
    private final List<Node> children = new ArrayList<>();

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

    // layout code omitted...
}

现在,类不那么臃肿,API不那么臃肿,另外,用户收到List,如前所述,这是一种非常着名的对象类型。 JavaFX的新程序员,假设他们有一些(任何)Java经验,他们已经熟悉List的API,并且不需要学习很多新的才能使用它。所以程序员现在可以这样做(代码是非常规的,并插入了程序员的想法):

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();

我们有一个快乐的程序员,他立刻就知道了API,并且在第一次遇到JavaFX中的场景图时遇到了麻烦,因此是一个从编程团队中获得高效率的快乐老板。

总而言之,通过简单地公开List子节点,API可以公开操作StackPane内容所需的所有功能,而无需向其添加大量方法。 StackPane class 以利用每个Java程序员的现有知识的方式完成。

<强>脚注

  1. 实际上,Pane需要比List中定义的更多功能:他们需要一种方便的方法来了解列表更改的时间,添加或删除节点(因为窗格需要发生这种情况时重新计算其布局)。因此,JavaFX团队定义了List的子接口,称为ObservableList,它具有List的所有功能,并为&#34;观察&#34;添加了功能。清单。
  2. JavaFX团队有一件事需要在这里仔细思考: all List定义的适合子节点集合的功能吗?如果List接口定义了一些在这里没有意义的方法,那么使用这个实现可能是一个坏主意,并且在第一个代码块中建议的更臃肿的API可能实际上更好选择。

答案 1 :(得分:3)

而不是为add()addAll()remove()removeAll()等儿童提供针对不同操作的单独方法。

很容易提供众所周知的集合,以便List<E>支持的每个操作都可用,这将使该类变得简单且不那么冗长。在这种情况下,javafx.scene.layout.Pane类会将子项存储在ObservableList<Node> List<E>

答案 2 :(得分:0)

窗格会跟踪其子项的数组。您不能只调用word的原因是因为StackPane类不提供该方法。您必须将元素直接添加到窗格的子数组中。