Статьи

Панели макета JavaFX 2.0 — FlowPane и TilePane

FlowPanes и TilePanes — хорошие панели макетов, если вы хотите расположить своих детей последовательно один за другим, по горизонтали или по вертикали. Они очень похожи друг на друга, так как оба будут размещать своих дочерних элементов либо в столбцах (в случае горизонтального Flow / TilePane), и обтекать их шириной или в строках (в случае вертикального Flow / TilePane) и обтекать на своей высоте.

Единственное существенное отличие состоит в том, что TilePane помещает всех дочерних TilePane в плитки одинакового размера! Таким образом, размер самых больших дочерних элементов берется за размер каждой отдельной плитки в TilePane . Поэтому TilePane также является хорошим способом для одинакового размера и выравнивания кнопок и других элементов управления. (См. Мой предыдущий пост Размерные кнопки одинаково внутри VBox или HBox )

FlowPane и TilePane — Пример 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);
    }
}

Это небольшое приложение показывает основное различие между FlowPane и TilePane , помещая одинаковое содержимое в обе панели. Обе панели будут помещены в другой VBox с дополнительным Text сверху.

Я предполагаю, что только части кода с FlowPane, TilePane и загрузкой изображений являются новыми для вас. Если у вас есть проблемы с пониманием этого кода JavaFX, посмотрите мои предыдущие примеры, где я начал с основ JavaFX 2.0.

Обе панели предоставляют, среди прочего, setHgap и setVgap для объявления расстояния между каждым столбцом и каждой строкой. Для заполнения кнопок я решил загрузить несколько изображений.

В JavaFX 2.0 изображения могут быть показаны с помощью ImageView который ожидает объект Image . ( Примечание. Это изображение javafx.scene.image, а не изображение java.awt.image!)

Такой ImageView можно затем применить к любому Labeled объекту. Labeled — это подкласс Control и, среди прочего, абстрактный родительский класс Label и ButtonBase (который является базовым классом для кнопок любого типа), который позволяет вам устанавливать изображение для каждого типа метки и кнопки.

Все мои шесть кнопок имеют размер 128 × 128 пикселей. Чтобы показать вам разницу между FlowPane и TilePane я решил изменить размеры этих изображений. На данный момент это возможно только непосредственно в конструкторе класса Image как нет методов для изменения размера объекта Image дальнейшем. Один конструктор принимает InputStream , два двойных значения для ширины и высоты и два логических значения для сохранения соотношения сторон изображения и для свойства «smooth». Если вы хотите изменить размер изображения и сохранить соотношение сторон, вы можете просто указать ширину или высоту и сохранить соотношение, передав «true» в качестве первого логического значения. С помощью свойства «smooth» вы можете выбирать между более четким или более быстрым рендерингом изображения.

В зависимости от случайного значения, сгенерированного для размера, ваше приложение должно выглядеть примерно так:

Вы можете видеть, что изображения в основном одинаковы. Разница в том, что FlowPane все изображения непосредственно за другим, FlowPane только пробелом, заданным методом setHgap , тогда как TilePane помещает все изображения в плитки одинакового размера.

FlowPane и TilePane — Пример 2

Вот еще один небольшой пример: как отмечалось во введении к этому сообщению, TilePane также является очень хорошим способом для одинакового размера и выравнивания кнопок. Чтобы показать основное различие между FlowPane и TilePane другой раз, те же элементы будут снова помещены в обе панели.

Вот код:

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);
    }
}

Опять же, корневой узел — это VBox с FlowPane в верхней области и TilePane в нижней области. В коде есть некоторые части, которые могут быть для вас новыми. Прежде всего взгляните на строки 44-51. Здесь я создаю радиальный градиент для фона сцены с помощью одного из многочисленных классов построителей в JavaFX 2.0. Позже я расскажу о градиентах, а также о шаблоне сборки в своих постах, поэтому здесь я не буду много объяснять. На данный момент вам просто нужно знать, что эти линии создают радиальный фон, который затем применяется к сцене с помощью метода setFill setFill . (Как и в предыдущих примерах, мы могли бы указать заливку фона непосредственно в конструкторе сцены, потому что он ожидает объект Paint , который включает в себя не только нормальные цвета, но и любой вид градиента).

В отличие от первого примера, на этот раз мы используем вертикальные панели, которые заполнены кнопками. Поскольку я хочу, чтобы кнопки увеличивались до размера, предоставленного их родителями, я установил максимальную высоту, а также максимальную ширину каждой кнопки на константу Double.MAX_VALUE . (Посмотрите на мой предыдущий пример Sizing Buttons одинаково внутри VBox или HBox, если вы этого еще не сделали)

Ваше приложение должно выглядеть так:

Как вы можете видеть на обеих панелях, кнопки увеличиваются до ширины родительского элемента, но только на TilePane кнопки также растут вертикально, потому что каждая плитка на TilePane одинакового размера. Этот пример может показаться не очень важным, но в приложениях, которые я разрабатывал в JavaFX 2.0 до сих пор, я всегда хотел одинаково изменять размер и выравнивать кнопки, потому что это тонкий аспект, который делает ваше приложение более чистым и отточенным.

Если вы измените размер окна, оно должно выглядеть так:

Обратите внимание, что после того, как кнопки больше не расположены вертикально в FlowPane кнопки занимают только необходимое им пространство (в зависимости от их содержимого), тогда как в TilePane все кнопки по-прежнему имеют одинаковый размер.

Ссылка: JavaFX 2.0 Layout Panes — FlowPane и TilePane от нашего партнера по JCG Себастьяна Дамма в блоге Just my 2 cents о Java .