javajavafx

ImageView Nodes seem to appear at the wrong y position


I have some strange problem with positioning ImageViews in a Scene in JavaFX.

I want three images of the same size (200 x 200) to appear next to each other at the same height. So I specify the same value in setTranslateY() for each picture. But for some reason, they appear on different heights: Everey next picture appears lower by 200 pixels than the previous one.

Here is the code:

public class Main extends Application {

    @Override
    public void start(Stage myStage) throws IOException {
        myStage.setTitle("title");
        myStage.setScene(getScene());
        stage = myStage;
        myStage.show();
    }

    public static void main(String[] args) {
        launch();
    }

    private Scene getScene() {
        List<Node> nodes = new ArrayList<>();
        Image i1 = loadFromPath("... some path");
        Image i2 = loadFromPath("... some other path");
        Image i3 = loadFromPath("... again some other path");
        ImageView v1 = new ImageView(i1);
        ImageView v2 = new ImageView(i2);
        ImageView v3 = new ImageView(i3);
        v1.setTranslateX(100);
        v1.setTranslateY(100);
        v2.setTranslateX(320);
        v2.setTranslateY(100);
        v3.setTranslateX(540);
        v3.setTranslateY(100);
        nodes.add(v1);
        nodes.add(v2);
        nodes.add(v3);
        VBox box = new VBox(nodes.toArray(new Node[0]));
        box.setMinWidth(800);
        box.setMaxWidth(800);
        box.setMinHeight(400);
        box.setMaxHeight(400);

        return new Scene(box);
    }

    private Image loadFromPath(String path) {
        FileInputStream input = null;
        try {
            input = new FileInputStream(path);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        return new Image(input);
    }
}

The result looks like this:

enter image description here

When I use setLayoutX() and setLayoutY() instead of setTranslateX() and setTranslateY(), the result is even worse:

enter image description here

Can someone please explain this result?


Solution

  • Layout panes, such as VBox, lay the components out for you according to an algorithm that is specific to the particular layout pane you are using. They do this by setting the layoutX and layoutY properties of the child nodes.

    In particular, a VBox ("vertical box") will lay out the child nodes in a vertical column: see the documentation.

    So if you use a layout pane, setting the layoutX and layoutY of the child nodes will have no effect, as they will subsequently be changed by the layout pane when it performs its layout. This explains the result you get in the second screen shot: the images are in the position determined by the layout algorithm of the VBox. The first image is in the top left of the box, and the second image is directly below it. If you remove all calls to setLayoutX(...) and setLayoutY(...), as well as calls to setTranslateX(...) and setTranslateY(...) you will see exactly the same results. The positions shown in the second screenshot are effectively the default positions for the layout defined by a VBox.

    Transformations, such as translations managed by setting the translateX and translateY properties, are applied after the layout pane performs its layout. So in the first screen shot, the first image is 100 pixels to the right, and 100 pixels below, the position the VBox placed it (the top left). The second image is 320 pixels to the right and 100 pixels below the position the VBox placed it (the position it appears in the second image). In other words, the translations are relative to the positions defined by the layout in the VBox.

    It appears from the coordinates you are setting that you want the images placed horizontally next to each other; to achieve this, just use a HBox instead of a VBox (as suggested in a comment).

    public class Main extends Application {
    
        @Override
        public void start(Stage myStage) throws IOException {
            myStage.setTitle("title");
            myStage.setScene(getScene());
            stage = myStage;
            myStage.show();
        }
    
        public static void main(String[] args) {
            launch();
        }
    
        private Scene getScene() {
            List<Node> nodes = new ArrayList<>();
            Image i1 = loadFromPath("... some path");
            Image i2 = loadFromPath("... some other path");
            Image i3 = loadFromPath("... again some other path");
            ImageView v1 = new ImageView(i1);
            ImageView v2 = new ImageView(i2);
            ImageView v3 = new ImageView(i3);
            //v1.setTranslateX(100);
            //v1.setTranslateY(100);
            //v2.setTranslateX(320);
            //v2.setTranslateY(100);
            //v3.setTranslateX(540);
            //v3.setTranslateY(100);
            nodes.add(v1);
            nodes.add(v2);
            nodes.add(v3);
            HBox box = new HBox(nodes.toArray(new Node[0]));
            box.setMinWidth(800);
            box.setMaxWidth(800);
            box.setMinHeight(400);
            box.setMaxHeight(400);
    
            return new Scene(box);
        }
    
        private Image loadFromPath(String path) {
            FileInputStream input = null;
            try {
                input = new FileInputStream(path);
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
            return new Image(input);
        }
    }
    

    You can use padding to put additional space around the content of the box, if needed:

    box.setPadding(new Insets(100, 0, 0, 100)); // top, right, bottom, left
    

    and spacing to put a gap between each image:

    box.setSpacing(20);
    

    If you really want to place everything manually (which is very strongly not recommended), use a container that performs no layout (for example, a Pane) instead of the VBox, and set the layoutX and, layoutY properties by hand.

    See the documentation and tutorial for more information.