Введение в JavaFx и работа с layout в примерах

в 16:07, , рубрики: borderpane, flowpane, gridpane, hbox, java, javafx, stackpane, tilepane, vbox

Доброго времени суток. В этой статье я расскажу основы работы с классами пакета javafx.scene.layout.* (BorderPane, AnchorPane, StackPane, GridPane, FlowPane, TilePane, HBox, VBox) и их особенностями в пошаговых примера и иллюстрациях. Также в кратце пробежимся по иерархии классов JavaFx.

Введение в JavaFx и работа с layout в примерах - 1

После установки Eclipse и плагина e(fx)lipse создадим новый проект:

Введение в JavaFx и работа с layout в примерах - 2

Выбираем JavaFx проект:

Введение в JavaFx и работа с layout в примерах - 3

Настраиваем проект:

Введение в JavaFx и работа с layout в примерах - 4

По дефолту Eclipse создаст такую заготовку:

package application;
	
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;

public class Main extends Application {
	@Override
	public void start(Stage primaryStage) {
		try {
			BorderPane root = new BorderPane();
			Scene scene = new Scene(root,400,400);
			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
			primaryStage.setScene(scene);
			primaryStage.show();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		launch(args);
	}
}

На сцену добавляется корневой узел, им должен быть класс унаследованный от Parent (узел, который может иметь детей) корневой узел добавляется на сцену (специальный контейнер для всего контента графа узлов), а она в свою очередь на стейжд, который и показывается.

Давайте разберемся с иерархией классов JavaFx:

Введение в JavaFx и работа с layout в примерах - 5

Самым базовым абстрактным классом является Node — узел в графе сцены, наследникам предоставляются поля для настройки размеров: (minWidth — минимальная ширина, minHeight — минимальная высота, prefWidth — предпочтительная ширина, prefHeight — предпочтительная высота, maxWidth — максимальная ширина, maxHeight — максимальная высота).

Координаты узла: getLayoutX() и getLayoutY()
relocate(double x, double y) — изменяет координаты узла, если необходимо настроить положение каждой координаты по отдельности, используйте setLayoutX(double value) и setLayoutY(double value)

Важно понимать, что выставленные координаты и размеры (pref то есть prefer — предпочтительно) при компоновке, в зависимости от иерархии графа узлов и их настроек могу отличаться от ожидаемых. Примеры приведу чуть позже.

Node не может содержать дочерних элементов это и логично, для этого существует Parent прямой наследник Node, в который можно добавлять и удалять дочерние узлы. Кстати, говоря, есть классы которые представляю узлы, но не содержат дочерние узлы: Camera, Canvas, ImageView, LightBase, MediaView, Parent, Shape, Shape3D, SubScene, SwingNode.

Нас сегодня интересует подгруппа наследников класса Region (базовый класс всех лайуат контейнеров) в частности поговорим о основных потомках класса Pane о панелях, которые чаще всего требуются в повседневной разработке.

BorderPane

Это специальная панель которая располагает свою дочерние узлы верхней(setTop(Node)), нижней(setBottom(Note)), левой(setLeft(Node)), правой(setRight(Node)) и центральной(setCenter(Node)) позициях, добавим несколько кнопок во все эти позиции на BotderPane:

BorderPane root = new BorderPane();
root.setLeft(new Button("Left"));
root.setTop(new Button("Top"));
root.setRight(new Button("Right"));
root.setBottom(new Button("Bottom"));
root.setCenter(new Button("Center"));

У секции top и bottom приоритет, поэтому они сужаю по высоте все остальные:

Введение в JavaFx и работа с layout в примерах - 6

При попытке задать координаты к примеру у верхней кнопки:

BorderPane root = new BorderPane();
root.setLeft(new Button("Left"));
Button topButton = new Button("Top"); 
topButton.setLayoutX(20.0);
topButton.setLayoutY(20.0);
root.setTop(topButton);
root.setRight(new Button("Right"));
root.setBottom(new Button("Bottom"));
root.setCenter(new Button("Center"));

Кнопка не изменит своего положения, почему так происходит? При компоновке детей в методе layoutChildren класса BorderPane переопределяется позиция каждого узла с учетом всех настроек. Решений исправить есть несколько, например поместить (обернуть) кнопку в дополнительный подходящий контейнер, например в базовую для всех панелей

Pane

BorderPane root = new BorderPane();
root.setLeft(new Button("Left"));
//
Pane pane = new Pane();
Button topButton = new Button("Top");
topButton.setLayoutX(20.0);
topButton.setLayoutY(20.0);
pane.getChildren().add(topButton);
root.setTop(pane);
//
root.setRight(new Button("Right"));
root.setBottom(new Button("Bottom"));
root.setCenter(new Button("Center"));

Потому как Pane никак не переопределяет положение своих детей, мы получаем нужное смещение:

Введение в JavaFx и работа с layout в примерах - 7

Можно не оборачивать кнопку, а просто выставить у нее внешний отступ c помощью статического метода setMargin(Node child, Insets value):

BorderPane.setMargin(topButton, new Insets(20.0));

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

Введение в JavaFx и работа с layout в примерах - 8

Уберем отступы снизу и справа и получим желаемое смещение кнопки:

BorderPane.setMargin(topButton, new Insets(20.0, 0.0, 0.0, 20.0));

StackPane

Теперь давайте рассмотрим ситуацию в которой панель навязывает детям не только позицию, но и пытается их растянуть по всей ширине и высоте, заполнив всю область контента, заменим BorderPane и добавим туда сначала TextArea и Button.

StackPane root = new StackPane();
root.getChildren().add(new TextArea("TextArea in StackPane"));
root.getChildren().add(new Button("Button in StackPane"));
Scene scene = new Scene(root,250,250);

Кстати, для добавления нескольких узлов в нужном порядке можно использовать метод:

root.getChildren().addAll(Node1, Node2, ....);

Что мы видим, текстовое поле растянулись по нашей панели, а кнопка центрировалась:

Введение в JavaFx и работа с layout в примерах - 9

Центрирование можно корректироваться с помощью setAlignment(Pos)

root.setAlignment(Pos.BOTTOM_RIGHT);

Сделать внутренний отступ с помощью setPadding(Insets)

root.setPadding(new Insets(10.0));

Введение в JavaFx и работа с layout в примерах - 10

Можно сделать индивидуальную настройку центрирования и отступов каждого узла с помощью статических методов:

StackPane root = new StackPane();
TextArea textArea = new TextArea("TextArea in StackPane");
StackPane.setMargin(textArea, new Insets(10.0, 0.0, 30.0, 50.0));
Button button = new Button("Button in StackPane");
StackPane.setAlignment(button, Pos.CENTER_RIGHT);
root.getChildren().addAll(textArea, button);

Результат:

Введение в JavaFx и работа с layout в примерах - 11

AnchorPane

Якорная панель позволяет края дочерних узлов привязывать смещениями к краям панели, рассмотрим пример:

Добавим кнопку и привяжем ее к правому краю:

AnchorPane root = new AnchorPane();
Button button = new Button("Button in AnchorPane");
root.getChildren().add(button);
AnchorPane.setRightAnchor(button, 10.0);

Введение в JavaFx и работа с layout в примерах - 12

Добавим привязку к нижнему краю:

AnchorPane.setBottomAnchor(button, 10.0);

Введение в JavaFx и работа с layout в примерах - 13

Теперь в левому краю и верхнему:

AnchorPane.setRightAnchor(button, 10.0);
AnchorPane.setTopAnchor(button, 10.0);

Получаем что края кнопки теперь жестко привязаны к краям панели, если начать тянуть окно, кнопка будет также тянуться.

Введение в JavaFx и работа с layout в примерах - 14

GridPane

Рассмотрим очень полезную сеточную панель, добавление детей в эту панель необходимо делать через метод add(Node child, int columnIndex, int rowIndex) в котором первый параметр добавляемый узле, второй номер колонки, третий номер строки, вот простой пример:

GridPane root = new GridPane();
// Для отображения сетки
root.setGridLinesVisible(true);
root.add(new Label("0x0"), 0, 0);
root.add(new Label("0x1"), 0, 1);
root.add(new Label("1x1"), 1, 1);
root.add(new Label("1x2"), 1, 2);
root.add(new Label("5x5"), 5, 5);

Мы видим, что узлы можно добавлять в любую ячейку, пустые столбцы и строки создаются автоматически:

Введение в JavaFx и работа с layout в примерах - 15

Для работы с колонками, есть специальный класс ColumnConstraints, для этого надо создать колонки, настроить их и добавить их в GridPane, в примеру если мы хотим выставить ширины первой колонки 130, а второй 20%:

GridPane root = new GridPane();
root.setGridLinesVisible(true);
root.add(new Label("0x0"), 0, 0);
root.add(new Label("0x1"), 0, 1);
root.add(new Label("1x1"), 1, 1);
root.add(new Label("1x2"), 1, 2);
root.add(new Label("5x5"), 5, 5);
//
ColumnConstraints columnConstraints = new ColumnConstraints();
columnConstraints.setPrefWidth(130.0);
ColumnConstraints columnConstraints1 = new ColumnConstraints();
columnConstraints1.setPercentWidth(20);
root.getColumnConstraints().addAll(columnConstraints, columnConstraints1);
//

Получаем:

Введение в JavaFx и работа с layout в примерах - 16

Это самые базовые возможности, потребуется написать отдельную статья, для охвата всех тонкостей настройки GridPane.

FlowPane

Давайте сразу к примеру из него сразу станет все понятно, добавим шесть кнопок в FlowPane:

FlowPane root = new FlowPane();
root.getChildren().add(new Button("Button #1"));
root.getChildren().add(new Button("Button #2"));
root.getChildren().add(new Button("Button #3"));
root.getChildren().add(new Button("Button #4"));
root.getChildren().add(new Button("Button #5"));
root.getChildren().add(new Button("Button #6"));

Посмотрим результат, кнопка выводятся одна за другой (по дефолту у FlowPane горизонтальный вывод Orientation.HORIZONTAL)

Введение в JavaFx и работа с layout в примерах - 17
Теперь при уменьшении окна по ширине, мы видим что дочерние узлы начинают переносится:

Введение в JavaFx и работа с layout в примерах - 18
И соответственно если выставить вертикальную ориентацию

root.setOrientation(Orientation.VERTICAL);

Введение в JavaFx и работа с layout в примерах - 19
При при уменьшении высоты окна получаем перенос:

Введение в JavaFx и работа с layout в примерах - 20

Вертикальные и горизонтальные отступы между элементами можно настроиться с помощью:

root.setVgap(8);
root.setHgap(4);

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

Введение в JavaFx и работа с layout в примерах - 21
Как мы видим все узлы теперь находятся в «тайла» одинаковых размеров, растянутых по наибольшему узлу.

HBox и VBox

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

VBox root = new VBox();
HBox hBox = new HBox();
hBox.getChildren().addAll(new Button("Button#1"), new Button("Button#2"));
Slider slider = new Slider(1.0, 10.0, 4.0);
slider.setShowTickLabels(true);
root.getChildren().add(new Label("Label"));
root.getChildren().addAll(hBox, slider);

Введение в JavaFx и работа с layout в примерах - 22

Это все наследники класса Pane, спасибо за внимание.

Автор: VADMARK

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js