我正在尝试使用JavaFX 8创建一组自定义控件。我有点困惑在做什么是正确的方法来做一些事情,比如布置我定义的孩子来构建我的控件。 我用来覆盖layoutChildren()方法,在那里我重新定位和调整子节点的大小;但是读取layoutChildren()的javadoc时会写:
在布局传递期间调用以布置此Parent中的子项。默认情况下,它只会将托管,可调整大小的内容的大小设置为其首选大小,并且不会进行任何节点定位。
因此,根据文档,我不得对孩子进行任何重定位(“节点定位”)。
我想要了解的是在自定义控件中定位和调整子项大小的正确方法是什么。
我不明白的另一件事是调用layoutChildren()的次数和次数;文档说“在布局过程中调用”但我不明白何时执行“布局传递”。
我希望你能帮助我。
编辑@James_D
这是我在评论中所说的一个例子
public class MyControl extends TextField {
private Label label;
public MyControl() {
super();
setSkin(new TextFieldSkin(this));
label=new Label("This is my custom textfield");
getChildren().add(label);
}
@Override
protected void layoutChildren() {
super.layoutChildren();
label.relocate(0, -label.getHeight());
System.out.println("I'm laying out children");
}
}
如果你运行它,你会注意到每个帧都调用layoutChildren()
答案 0 :(得分:3)
您误解了Javadocs you quoted,其中描述了Parent.layoutChildren()
的作用。它并不是说子类不能定位节点;实际上下一句是
子类应该覆盖此函数以根据需要布局内容。
因此,这正是您应该覆盖的方法,以便布局子节点。
我不明白"布局通过"执行。
来自package documentation for javafx.scene.layout
:
一旦应用程序创建并显示
Scene
,系统就会自动驱动场景图布局机制。场景图检测影响布局的动态节点更改(例如大小或内容的更改)和调用requestLayout()
,它将该分支标记为需要布局,以便在下一个脉冲上执行自上而下的布局传递通过在该分支的根上调用layout()
来在该分支上。在该布局过程中,将在每个父级调用layoutChildren()
回调方法来布局其子级。此机制旨在通过确保多次布局请求在一次通过中合并和处理而不是在每次更改时执行重新布局来最大化布局效率。因此,应用程序不应直接在节点上调用布局。
因此,如果任何子节点的大小或内容发生变化,父节点将自动"(*)将自身标记为需要布局。在每个渲染脉冲上,如果父级需要布局,则将调用其layoutChildren()
方法。这意味着您所要做的就是实现layoutChildren()
方法,并在需要时为您调用。
(*)我对它如何工作的理解,虽然我实际上没有查看源代码,但是父对子节点的布局边界是绑定的:如果有任何子节点' s边界无效,然后在下一个渲染脉冲重新计算其布局。反过来,如果内容改变,节点将使其自己的布局边界无效(例如,如果文本改变,标签将使其布局边界无效等)。换句话说,JavaFX observable properties and bindings驱动布局机制。
所以(TL; DR):layoutChildren()
(或Parent
,甚至Region
的子类的Pane
方法,具体取决于您需要的功能)确切地调整子节点大小和重新定位的正确位置。每次渲染场景时都会调用该方法,如果(并且仅当)父级需要重新计算其布局。