How to bind stage resizing with resizing of components?
Asked Answered
F

3

18

I have a JavaFX 2.0 application with FXML. I want the components (TextFields, ComboBoxes, layouts, and so on) to be resized when a window with an application is resized. So...

When building GUI applications with JavaFX, you will notice that certain classes in the API already implement properties. For example, the javafx.scene.shape.Rectangle class contains properties for arcHeight, arcWidth, height, width, x, and y. For each of these properties there will be corresponding methods that match the conventions previously described. For example, getArcHeight(), setArcHeight(double), arcHeightProperty(), which together indicate (to both developers and tools) that the given property exists.*

  • To add listener to a stage I have to do something like:

       stage.resizableProperty().addListener(new ChangeListener<Boolean>(){
        @Override
        public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2){
            throw new UnsupportedOperationException("Not supported yet.");
        }
    
    });
    

So there are two questions:

  • To make some binding I have to get my stage in the controller class. So - how can I get a stage in a controller class?
  • It looks like UI controls do not have any width\height properties, to be bound to something. Or perhaps I have not found them.

So, how can I solve my problem?

UPD. About Scene Builder to Sergey Grinev: When I use Ctrl+K on my component (tell it to occupy the whole area of its parent component) - everything is ok.

But what if I want to tell my component to occupy 50% of an area? For exammple I have a tab with two VBoxes on it. The tab's width is 100px. The Vbox's widths are 50px for each. VBox1 has x1=0, and x2=50, and VBox2 has x1=50, and x2=100. Then I resize my window with JavaFX application. Now I have tab's Width = 200px. But my VBoxes widths are still = 50px: VBox1 has x1=0, and x2=50, and VBox2 has x1=150, and x2=200. And I need them to be VBox1 x1=0 and x2=100 and VBox2 x1=100 and x2=200. Where x1 and x2 values are the coordinates of VBoxes corners.

How can Scene Builder help me in this situation?

Fishmonger answered 25/4, 2012 at 15:7 Comment(0)
C
7

Another easy option is to use JavaFX Scene Builder (recently become available as public beta: http://www.oracle.com/technetwork/java/javafx/tools/index.html)

It allows to create UI by drag-and-drop and anchor UI elements to borders (by anchor tool), so they move/resize with the window borders.

UPDATE:

To achieve autoresizing percentage layout you can use GridPane with ColumnConstraint:

public void start(Stage stage) {

    VBox box1 = new VBox(1);
    VBox box2 = new VBox(1);

    //random content
    RectangleBuilder builder = RectangleBuilder.create().width(20).height(20);
    box1.getChildren().addAll(builder.build(), builder.build(), builder.build());
    builder.fill(Color.RED);
    box2.getChildren().addAll(builder.build(), builder.build(), builder.build());

    //half by half screen
    GridPane grid = new GridPane();
    grid.addRow(0, box1, box2);

    ColumnConstraints halfConstraint = ColumnConstraintsBuilder.create().percentWidth(50).build();
    grid.getColumnConstraints().addAll(halfConstraint, halfConstraint);

    stage.setScene(new Scene(grid, 300, 250));
    stage.show();
}
Choirmaster answered 26/4, 2012 at 8:33 Comment(1)
I've replied to your comment as an update for the question, as i was out of characters.Fishmonger
F
21

Some suggestions for building resizable guis =>

  • Make sure the root of your app is a Pane subclass, rather than a Group.
  • Unclamp the max size of buttons (e.g button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE).
  • Most of the time the layout panes can handle the resizing for you and you don't need bindings and listeners => i.e. only bind when you really need to.
  • UI controls and layout panes are Regions => all Regions have read-only height and width properties you can listen to and bind to.
  • You can not directly bind the height and width properties of a control to another value. Instead use the minWidth/Height, prefWidth/Height, maxWidth/Height properties.
  • The scene has height and width properties you can bind to if needed. If you do this, when the stage is resized, the scene will be resized, then your bound components will be resized. If the root of your scene is a layout pane rather than a group, this binding is usually unnecessary.
  • If you don't set a height and width on the scene, for a stand-alone app, the scene (and stage) will be sized to fit the preferred size of the scene's root node (which is usually what you want).
  • If all else fails you can start overriding Parent's layoutchildren method.

Here is an example of a resizable JavaFX UI which implements some of the principles above.

Flail answered 25/4, 2012 at 19:34 Comment(0)
C
7

Another easy option is to use JavaFX Scene Builder (recently become available as public beta: http://www.oracle.com/technetwork/java/javafx/tools/index.html)

It allows to create UI by drag-and-drop and anchor UI elements to borders (by anchor tool), so they move/resize with the window borders.

UPDATE:

To achieve autoresizing percentage layout you can use GridPane with ColumnConstraint:

public void start(Stage stage) {

    VBox box1 = new VBox(1);
    VBox box2 = new VBox(1);

    //random content
    RectangleBuilder builder = RectangleBuilder.create().width(20).height(20);
    box1.getChildren().addAll(builder.build(), builder.build(), builder.build());
    builder.fill(Color.RED);
    box2.getChildren().addAll(builder.build(), builder.build(), builder.build());

    //half by half screen
    GridPane grid = new GridPane();
    grid.addRow(0, box1, box2);

    ColumnConstraints halfConstraint = ColumnConstraintsBuilder.create().percentWidth(50).build();
    grid.getColumnConstraints().addAll(halfConstraint, halfConstraint);

    stage.setScene(new Scene(grid, 300, 250));
    stage.show();
}
Choirmaster answered 26/4, 2012 at 8:33 Comment(1)
I've replied to your comment as an update for the question, as i was out of characters.Fishmonger
B
3

Suggestions:

  1. If you have only one main stage then store it in a static field/variable while the app start and access it in all controller classes.
  2. You can put all controls/components into a layout/pane that manages its children layout automatically. For different layouts check the api doc. After that, bind the stage scene's width and height properties to this layout's properties accordingly. More on binding look at Using JavaFX Properties and Binding.
    Example of unidirectional binding
Bergess answered 25/4, 2012 at 16:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.