我正在尝试使用JavaFX 8创建一组自定义控件。我对做某些事情的正确方法有些困惑,例如布局我定义的用于构建控件的子对象。
我用来覆盖layoutChildren()方法,在其中重新定位子元素并调整子元素的大小;但是阅读layoutChildren()的javadoc时,会这样写:
在布局传递期间调用,以布局此父级中的子级。默认情况下,它将仅将托管的,可调整大小的内容的大小设置为其首选大小,并且不进行任何节点定位。
因此,根据文档,我不能对子代执行任何重定位(“节点定位”)。
我想了解的是在我的自定义控件中定位和调整子级大小的正确方法。
我不明白的另一件事是何时以及多少次调用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()每帧都会调用一次
最佳答案
您误解了Javadocs you quoted,它描述了Parent.layoutChildren()
的功能。并不是说子类不能定位节点;实际上,下一个句子是
子类应重写此函数以根据需要布局内容。
因此,这正是您应该覆盖以布局子节点的方法。
我不了解何时执行“版式传递”。
从package documentation for javafx.scene.layout
:
一旦应用程序创建并显示Scene
,场景图布局机制就会由系统自动驱动。场景图检测到影响布局的动态节点更改(例如大小或内容的更改),并调用requestLayout()
,该标记将分支标记为需要布局,以便在下一个脉冲上,通过以下方式对该分支执行自上而下的布局遍历:在该分支的根上调用layout()
。在该布局过程中,将在每个父级上调用layoutChildren()
回调方法以布局其子级。通过确保在一次通过中合并并处理多个布局请求,而不是对每一分钟的更改执行重新布局,该机制旨在最大化布局效率。因此,应用程序不应直接在节点上调用布局。
因此,如果任何子节点的大小或内容发生更改,父节点将“自动”(*)将其自身标记为需要布局。在每个渲染脉冲上,如果父母需要布局,则将调用其layoutChildren()
方法。这意味着您要做的就是实现layoutChildren()
方法,并将在需要时为您调用该方法。
(*)尽管我实际上没有看过源代码,但我对它的工作方式的理解是父级绑定到其子节点的布局范围:如果任何子节点的范围无效,那么它将重新计算其下一个渲染脉冲的布局。反过来,如果内容更改,则节点将使其自身的布局范围无效(例如,如果文本更改,则标签将使其布局范围无效等)。换句话说,JavaFX observable properties and bindings驱动布局机制。
因此(TL; DR):layoutChildren()
(或Parent
,甚至Region
,取决于您所需的功能)的子类的Pane
方法正是调整子节点的大小和位置的正确位置。每当(且仅当)父级需要重新计算其布局时,才会在每次渲染场景时调用该方法。