Reputation: 813
I'm trying to create a set of custom control using JavaFX 8. I'm a bit confused on what are the right way to do some stuff like laying out the children I define to build my control. I use to override the layoutChildren() method, where I relocate and resize the children; but reading the javadoc for the layoutChildren() there is written:
Invoked during the layout pass to layout the children in this Parent. By default it will only set the size of managed, resizable content to their preferred sizes and does not do any node positioning.
So, according the documentation, I must not perform any relocation ("node positioning") of the children.
What I'm trying to understand is what is the right way to position and resize the children in my custom control.
Another thing I don't understand is when and how many times layoutChildren() is called; documentation says "Invoked during the layout pass" but I don't understand when "layout pass" is performed.
I hope you can help me.
EDIT @James_D
This is an example what I said in the comment
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");
}
}
If you run it, you will notice that layoutChildren() is called every frames
Upvotes: 3
Views: 1538
Reputation: 209225
You have misinterpreted the Javadocs you quoted, which describe what Parent.layoutChildren()
does. It doesn't say that a subclass can't position nodes; in fact the very next sentence is
Subclasses should override this function to layout content as needed.
So this is exactly the method you should override in order to layout the child nodes.
I don't understand when "layout pass" is performed.
From the package documentation for javafx.scene.layout
:
The scene graph layout mechanism is driven automatically by the system once the application creates and displays a
Scene
. The scene graph detects dynamic node changes which affect layout (such as a change in size or content) and callsrequestLayout()
, which marks that branch as needing layout so that on the next pulse, a top-down layout pass is executed on that branch by invokinglayout()
on that branch's root. During that layout pass, thelayoutChildren()
callback method will be called on each parent to layout its children. This mechanism is designed to maximize layout efficiency by ensuring multiple layout requests are coalesced and processed in a single pass rather than executing re-layout on on each minute change. Therefore, applications should not invoke layout directly on nodes.
So the parent node will "automatically"(*) mark itself as needing layout if the size or content of any of its child nodes change. On each rendering pulse, if a parent needs layout, its layoutChildren()
method will be called. This means that all you have to do is implement the layoutChildren()
method and it will be invoked for you as and when needed.
(*) My understanding of how this works, though I haven't actually looked at the source code, is that the parent binds to the layout bounds of its child nodes: if any of the child nodes's bounds are invalidated, then it recomputes its layout on the next rendering pulse. In turn, a node will invalidate its own layout bounds if the content changes (e.g. a label will invalidate its layout bounds if the text changes, etc). In other words, the JavaFX observable properties and bindings drive the layout mechanism.
So (TL;DR): the layoutChildren()
method of a subclass of Parent
(or Region
, or even Pane
, depending on the functionality you need) is exactly the correct place to resize and reposition child nodes. The method will be called each time the scene is rendered if (and only if) the parent needs to recompute its layout.
Upvotes: 3