Explicitly positioning nodes in JavaFX


Question

When I have clicked a button, it changes its position.

But when I move the mouse, the button comes back to the center of the scene, why?

I have the following code:

public class HolaMundo extends Application {

    Button btn;
    Scene scene;

    @Override
    public void start(Stage primaryStage) {
        btn = new Button();
        btn.setText("Hola Mundo");

        StackPane root = new StackPane();
        root.getChildren().add(btn);

        scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();

        scene.setOnMouseMoved(new EventHandler<MouseEvent>(){

            @Override
            public void handle(MouseEvent t) {
                 btn.setText(String.valueOf(t.getX() ));
            }
        });

        btn.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                btn.setLayoutX(Math.random() * (300 - btn.getWidth()));
                btn.setLayoutY(Math.random() * (250 - btn.getHeight())); 
            }
        });
    }

    public static void main(String[] args) {
        launch(args);
    }
}
1
6
1/14/2014 9:31:57 PM

Accepted Answer

Suggested approach

For your particular code snippet, perhaps using a Pane instead of a StackPane is the best approach.

Why your code doesn't do what you want

If you are going to manually set layout values, don't use a layout pane (such as a StackPane) which automatically sets layout values for you. If you use a layout pane, then the layout values you explicitly set will be overridden automatically the next time layout pane performs a layout pass (e.g. it is resized or some its content or location in the scene graph becomes dirty).

Options for explicitly laying out nodes

If you want to explicitly layout items in JavaFX do one of the following:

  1. Subclass Region and override layoutChildren.
  2. Place your content in a Pane if you need the container to be styled with CSS or implement resizing functions.
  3. Place your content in a Group if you don't need the container to be styled with CSS or implement resizing functions.

Related advice on node positioning

If you want to have automatically laid out components using the predefined layout managers but you want to adjust or temporarily modify the locations of some of the components from their default positions, you can adjust the translateX/Y values rather than layoutX/Y values. According to the definition of translateX:

The node's final translation will be computed as layoutX + translateX, where layoutX establishes the node's stable position and translateX optionally makes dynamic adjustments to that position. This variable can be used to alter the location of a node without disturbing its layoutBounds, which makes it useful for animating a node's location.

This means that the layout manager can compute a child's default position using layoutX, and you can adjust the position from the default using translateX.

18
1/14/2014 9:28:43 PM

Not having deeply investigated the current case, I see a difference when I use an AnchorPane instead of the StackPane to place the Button on.

By changing the label text of the Button by the mouseMoved-Event the Pane is rendered (layout is requested). With a StackPane placing all it's children in the center of itself the Button's position is reset to the center of the Pane. When you have a look into the layoutChildren method of StackPane you'll see a call to resizeRelocate. So layoutX and layoutY are reset and the button moves back to the center position (or whatever you set the alignment of the StackPane to).

So I think this is a correct behavior of the StackPane and I recommend to use another Pane, e.g. AnchorPane.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon