Desktop Java

JavaFX 2.0 Layout Panes – FlowPane and TilePane

FlowPanes and TilePanes are nice layout panes, if you want to layout your children consecutively one after another, either horizontally or vertically. They are quite similiar to each other as both will layout their children either in columns (in case of a horizontal Flow/TilePane) and wrap at their width or in rows (in case of a vertical Flow/TilePane) and wrap at their height.

The only major difference is, that the TilePane places all children in tiles that are the same size! So the size of the greatest children is taken for the size of each individual tile in the TilePane. Therefore a TilePane is also a nice way to size and align buttons and other controls equally. (See my previous post Sizing Buttons equally inside a VBox or HBox)

FlowPane and TilePane – Example 1

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import java.util.Random;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
 
/**
 *
 * Created on: 24.03.2012
 * @author Sebastian Damm
 */
public class FlowPaneAndTilePaneExample extends Application
{
    private Random random;
    private VBox root;       
     
    private FlowPane flowPane;
    private TilePane tilePane;
     
    @Override
    public void start(Stage primaryStage) throws Exception
    {               
        random = new Random();
        root = new VBox(30);
         
        VBox upperVBox = createUpperVBox();
        VBox lowerVBox = createLowerVBox();
 
        fillPanesWithImages();
                 
        root.getChildren().addAll(upperVBox, lowerVBox);
         
        Scene scene = new Scene(root, 800, 600, Color.ANTIQUEWHITE);
         
        primaryStage.setTitle("FlowPane and TilePane Example");
        primaryStage.setScene(scene);
        primaryStage.show();       
    }
     
    private VBox createUpperVBox()
    {
        VBox vbox = new VBox(20);
         
        Text textFlowPane = new Text("I am a FlowPane");
        textFlowPane.setFont(Font.font("Calibri", FontWeight.BOLD, 30));
        textFlowPane.setUnderline(true);
        textFlowPane.setEffect(new DropShadow());       
        VBox.setMargin(textFlowPane, new Insets(10, 0, 0, 10));
         
        flowPane = new FlowPane();
        flowPane.setHgap(5);
        flowPane.setVgap(5);
         
        vbox.getChildren().addAll(textFlowPane, flowPane);
        VBox.setMargin(vbox, new Insets(10));
         
        return vbox;
    }
     
    private VBox createLowerVBox()
    {
        VBox vbox = new VBox(20);
         
        Text textTilePane = new Text("I am a TilePane");
        textTilePane.setFont(Font.font("Calibri", FontWeight.BOLD, 30));
        textTilePane.setUnderline(true);       
        textTilePane.setEffect(new DropShadow());
        VBox.setMargin(textTilePane, new Insets(10, 0, 0, 10));
         
        tilePane = new TilePane();
        tilePane.setHgap(5);
        tilePane.setVgap(5);
                 
        vbox.getChildren().addAll(textTilePane, tilePane);
        VBox.setMargin(vbox, new Insets(10));
         
        return vbox;
    }
     
    private void fillPanesWithImages()
    {
        for (int i = 1; i <= 6; i++)
        {
            int imgSize = random.nextInt(128) + 1;
             
            Button bt = new Button();                       
            Image img = new Image(FlowPaneAndTilePaneExample.class
   .getResourceAsStream("images/person" + i + ".png"),
   imgSize > 50 ? imgSize : 50, 0, true, false);
            ImageView view = new ImageView(img);
            bt.setGraphic(view);
             
            flowPane.getChildren().add(bt);           
             
            Button bt2 = new Button();                       
            Image img2 = new Image(FlowPaneAndTilePaneExample.class
   .getResourceAsStream("images/person" + i + ".png")
   , imgSize > 50 ? imgSize : 50, 0, true, false);
            ImageView view2 = new ImageView(img2);
            bt2.setGraphic(view2);
             
            tilePane.getChildren().add(bt2);             
        }       
    }
 
    public static void main(String[] args)
    {
        Application.launch(args);
    }
}

This little application shows the major difference between a FlowPane and a TilePane by putting the same content in both panes. The both panes will be put in another VBox with an additional Text on top.

I am assuming that only the parts of the code with the FlowPane, the TilePane and the image loading are new to you by now. If you have problems understanding this JavaFX code please see my previous examples where I started with the basics of JavaFX 2.0.

Both panes provide amongst others a setHgap and a setVgap method to declare a spacing between each column and each row. To fill the buttons I chose to load some images.

In JavaFX 2.0 images can be shown with a ImageView which expects an Image object. (Note: This is an javafx.scene.image.Image, not an java.awt.image!)

Such an ImageView can then by applied to any Labeled object. Labeled is a subclass of Control and amongst others the abstract parent class of Label and ButtonBase (which is the base class for every kind of button), which allows you to set an image to every kind of label and button.

My six buttons are all 128×128 pixels. To show you the difference between a FlowPane and a TilePane I chose to resize these images. At the moment this is only possible directly in the constructor of the Image class as there are no methods to change the size of an Image object later on. One constructor takes an InputStream, two double values for the width and the height and two boolean values for preserving the aspect ratio of the image and for the ‘?smooth’ property. If you want to resize your image and keep the aspect ratio you can just specify either the width or the height and keep the ratio by passing ‘true’ as the first boolean value. With the ‘smooth’ property you can choose between a clearer or a faster rendering of the image.

Depending on the random value generated for the size, your application should look something like this:

You can see that the images are basically the same. The difference is, that the FlowPane lays out all images directly after another only separated by the gap specified with the setHgap method, whereas the TilePane put all images in tiles of the same size.

FlowPane and TilePane – Example 2

Here is another small example: As stated in the introduction of this post, a TilePane is also a very nice way for sizing and aligning buttons equally. To show the main difference between a FlowPane and a TilePane another time, the same elements will be put in both panes again.

Here is the code:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Separator;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.RadialGradientBuilder;
import javafx.scene.paint.Stop;
import javafx.scene.text.Font;
import javafx.stage.Stage;
 
/**
 *
 * Created on: 24.03.2012
 * @author Sebastian Damm
 */
public class FlowPaneAndTilePaneExample2 extends Application
{
    private VBox root;       
     
    private FlowPane flowPane;
    private TilePane tilePane;
     
    @Override
    public void start(Stage primaryStage) throws Exception
    {               
        root = new VBox();
        root.setAlignment(Pos.CENTER);
 
        initFlowPane();
        initTilePane();
        createButtons();
                 
        root.getChildren().addAll(flowPane, new Separator(), tilePane);
         
        Scene scene = new Scene(root, 400, 300);
        RadialGradient background = RadialGradientBuilder.create()
                .stops(new Stop(0d, Color.web("#fff"))
                    , new Stop(0.47, Color.web("#cbebff"))
                    , new Stop(1d, Color.web("#a1dbff"))
                )               
                .cycleMethod(CycleMethod.NO_CYCLE)
                .build();
        scene.setFill(background);
         
        primaryStage.setTitle("FlowPane and TilePane Example 2");
        primaryStage.setScene(scene);
        primaryStage.show();       
    }
     
    private void initFlowPane()
    {       
        flowPane = new FlowPane(Orientation.VERTICAL);
        flowPane.setHgap(5);
        flowPane.setVgap(5);       
        flowPane.setPrefHeight(200);
        flowPane.setAlignment(Pos.CENTER);
        VBox.setMargin(flowPane, new Insets(10));
    }
     
    private void initTilePane()
    {
        tilePane = new TilePane(Orientation.VERTICAL);
        tilePane.setHgap(5);
        tilePane.setVgap(5);
        tilePane.setPrefHeight(200);
        tilePane.setAlignment(Pos.CENTER);
        VBox.setMargin(tilePane, new Insets(10));
    }
         
    private void createButtons()
    {
        Button bt = new Button("1");
        bt.setMaxWidth(Double.MAX_VALUE);
        bt.setMaxHeight(Double.MAX_VALUE);
        Button bt2 = new Button("Button 1");
        bt2.setMaxWidth(Double.MAX_VALUE);
        bt2.setMaxHeight(Double.MAX_VALUE);
        Button bt3 = new Button("Button");
        bt3.setMaxWidth(Double.MAX_VALUE);
        bt3.setMaxHeight(Double.MAX_VALUE);
        bt3.setFont(Font.font("Cambria", 22));
      
        Button bt4 = new Button("1");
        bt4.setMaxWidth(Double.MAX_VALUE);
        bt4.setMaxHeight(Double.MAX_VALUE);
        Button bt5 = new Button("Button 1");
        bt5.setMaxWidth(Double.MAX_VALUE);
        bt5.setMaxHeight(Double.MAX_VALUE);
        Button bt6 = new Button("Button");
        bt6.setMaxWidth(Double.MAX_VALUE);
        bt6.setMaxHeight(Double.MAX_VALUE);
        bt6.setFont(Font.font("Helvetica", 22));
         
        flowPane.getChildren().addAll(bt, bt2, bt3);
        tilePane.getChildren().addAll(bt4, bt5, bt6);
    }
 
    public static void main(String[] args)
    {
        Application.launch(args);
    }
}

Again the root node is a VBox with a FlowPane in the upper region and a TilePane in the lower region. There are some parts in the code that may be new to you. First of all take a look at the lines 44-51. Here I create a radial gradient for the background of the scene with help from one of the numerous builder classes in JavaFX 2.0. I will cover gradients and also the builder pattern in own posts later on, so I won´t explain much here. For now you just need to know, that these lines create a radial background which is then applied to the scene via scene´s setFill method. (Like in previous examples we could have specified the background fill directly in the constructor of the scene, because it expects a Paint object, which includes not only normal colors, but also every kind of gradient).

In contrast to the first example, this time we use vertical panes which are populated with buttons. Because I want to allow the buttons to grow to whatever space is provided by their parent, I set the max height as well as the max width of every button to the constant Double.MAX_VALUE. (Take a look at my previous example Sizing Buttons equally inside a VBox or HBox if you haven´t already)

Your application should look like this:

As you can see in both panes the buttons grow to the width of their parent, but only in the TilePane the buttons also grow vertically because each tile in a TilePane is equally sized. This example may not seem very important but in the applications I developed in JavaFX 2.0 until now, I always wanted to size and align buttons equally because it is a subtle aspect that makes your application look more clean and polished.

If you resize you window, it should look like this:

Take a note, that once the buttons are not layed out vertically anymore in the FlowPane the buttons only occupy the space they need (based on their content) whereas in the TilePane all buttons are still of equal size.

Reference: JavaFX 2.0 Layout Panes – FlowPane and TilePane from our JCG partner Sebastian Damm at the Just my 2 cents about Java blog.

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button