Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

190

ГЛАВА 7

только ось проходит снизу контейнера, а все компоненты прижимаются к ней нижней границей. Так же как для горизонтального, для вертикального выравнивания компонентов можно сказать, что использовать его просто, когда все компоненты в контейнере имеют одинаковое выравнивание.

Кстати, если вы просмотрите документацию по классу Component, то увидите, что для задания параметров выравнивания компонентов используются числа с плавающей запятой (float) от 0 до 1. Можно сказать, что они определяют, какая часть компонента находится за осью. Так, например, константы RIGHT_ALIGNMENT и TOP_ALIGNMENT равны нулю. Это значит, что весь компонент находится по одну сторону от оси (с правой или с нижней стороны, соответственно). Если увеличивать значения выравнивания, то компонент будет потихоньку «переползать» за ось и, в конце концов, окажется по другую ее сторону. Константы LEFT_ALIGNMENT и BOTTOM_ALIGNMENT как раз равны единице (то есть компонент находится целиком за осью). Так что это гораздо более тонкий инструмент, чем кажется поначалу, — значения констант соответствуют наиболее вероятным вариантам выравнивания компонентов, но вы также можете и напрямую указывать свои значения, что позволяет очень точно выстраивать компоненты относительно друг друга.

Хотя все это кажется интересным, реальная польза от таких изощренных возможностей блочного расположения практически нулевая. Невероятно сложно, а иногда вообще невозможно предугадать, как выстроятся компоненты в контейнере, чтобы правильно использовать для них разные варианты выравнивания.

С другой стороны, нет ничего проще совмещения нескольких панелей с блочным расположением, содержащих по несколько компонентов с одинаковым выравниванием. Любой пользовательский интерфейс можно разбить на горизонтальные и вертикальные полосы с компонентами, применяя для них подобные панели. Именно такой способ создания интерфейса дает наибольшие преимущества разработчику, и мы всячески рекомендуем его вам. А чтобы задание параметров выравнивания компонентов не отнимало у вас слишком много времени и сил, добавим в наш вспомогательный класс BoxLayoutUtils еще пару методов:

//com/porty/swing/BoxLayoutUtils.java

//Класс для удобной работы с менеджером BoxLayout package com.porty.swing;

import javax.swing.*;

public class BoxLayoutUtils

{

//задает единое выравнивание по оси X для

//группы компонентов

public static void setGroupAlignmentX(float alignment, JComponent... cs) {

for (JComponent c : cs) { c.setAlignmentX(alignment);

}

}

//задает единое выравнивание по оси Y для

//группы компонентов

public static void setGroupAlignmentY(float alignment,

Искусство расположения

191

JComponent... cs) { for (JComponent c : cs) {

c.setAlignmentY(alignment);

}

}

// далее идут методы, описанные ранее

}

Теперь, создавая интерфейс с помощью менеджера блочного расположения, смело добавляйте любые нужные вам компоненты, а потом просто передайте массив ссылок на них в один из наших методов. После этого все будет работать именно так, как нужно, и даст отличный результат. Остается заметить, что более или менее сложный интерфейс не обойдется одной панелью с блочным расположением, и в таком случае для всех вложенных панелей также следует задать единое выравнивание.

Если вы помните, в предыдущем разделе был показан пример, который почему-то работал не так, как нам хотелось. Теперь понятно, почему — у панели было центральное выравнивание, а у кнопки — выравнивание по левой границе. Используя новые методы класса BoxLayoutUtils, можно легко согласовать параметры выравнивания всех компонентов, и они займут положенные им места, задуманные нами с самого начала.

Расположение по группам: GroupLayout

Расположение GroupLayout было специально создано в поддержку визуального редактора для расположения компонентов Swing в редакторе NetBeans (редактор назывался Matisse), а затем перекочевало в JDK. Суть его состоит в том, что компоненты располагаются в вертикальных и горизонтальных группах. Похоже на совмещение нескольких блочных расположений в одном, однако особенностью является то, что один и тот же компонент вполне может участвовать в нескольких группах одновременно. К примеру, текстовые поля могут участвовать в горизонтальных группах вместе с другими компонентами, например надписями и кнопками, и одновременно с этим в вертикальных группах, что позволяет сделать их размер одинаковым. В группе можно указывать расстояние между компонентами. Окончательный вариант расположения создается двумя главными группами, вертикальной и горизонтальной.

Идея расположения по группам и их перекрытия неплоха, но скорее подходит для визуального редактора, собственно, для него это расположение и было разработано. При ручной разработке становится трудно читать код, так как компоненты располагаются на экране не совсем явно, и нужно выяснять, в каких группах они принимают участие. Так что создавая интерфейс в коде, лучше пользоваться другими альтернативами.

Гибкая сетка: GridBagLayout

Как правило, расположение многих созданных нами объектов в современных интерфейсах практически всегда выровнено по горизонтали и вертикали, то есть компоненты находятся в «сетке» с ячейками разных размеров. Мы группируем компоненты по назначению, выравнивая их по осям (за исключением авангардных или неряшливых интерфейсов, но их мы не станем рассматривать).

Создатели GridBagLayout приняли во внимание эти факты, и создали менеджер расположения, позволяющий нам разместить компоненты в сетке с ячейками гибких размеров, от нулевых до максимальных, позволенных контейнером. Формально, не считая совсем уже экстремальных вариантов, всегда можно представить любое расположение

192

ГЛАВА 7

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

Управление расположением компонентов в GridBagLayout осуществляется отдельным объектом под названием GridBagConstraints, который полностью описывает расположение компонента в сетке и его особенности. Настроив его, вы передаете его в метод add() вместе с добавляемым в контейнер компонентом. К сожалению, именно этот объект делает GridBagLayout такой удобной целью для нападок, а код с его применением напоминает прекрасно перемешанное спагетти. Дело в том, что все настройки хранятся в нем в виде полей (что противоречит концепциям объектно-ориентированного программирования), имена этих полей иногда не соответствуют производимому им эффекту, а количество «волшебных цифр» и избыточной информации поражает воображение и лишает код читаемости.

Давайте начнем с положения компонента в таблице. Его позиция определяется номером ячейки по вертикали и горизонтали:

gridx

(0,0) (1,0)

gridy

(0,1) (1,1)

(0,2)

Координаты в таблице, таким образом, хранятся в полях gridx и gridy. Так как указывать каждый раз новые координаты несколько утомительно, GridBagConstraints любезно предоставляет нам константу RELATIVE, согласно которой компонент будет добавлен в тот же ряд и следующий столбец за последним добавленным компонентов. Такое поведение напомнит нам последовательное расположение FlowLayout, тем более что по умолчанию компоненту придается предпочтительный размер. По умолчанию, если вы не указываете значение полей gridx и gridy, как раз RELATIVE и применяется, располагая компоненты в один ряд.

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

gridwidth — количество ячеек, занимаемое компонентов по горизонтали, «длина» компонента в ячейках

gridheight — количество ячеек, занимаемое компонентов по вертикали, «высота» компонента в ячейках

Очень часто компонент должен занять весь ряд или столбец, независимо от того, сколько там ячеек. Для этих случаев предусмотрено значение GridBagConstraints. REMINDER, которое и говорит, что компонент займет все оставшиеся ячейки по одной из осей или даже по обеим. Это тем более важно, что компонент в этом случае не зависит от появления большего количества ячеек в каком-то из рядом или столбцов, но все равно займет все ячейки.

Ячейки практически никогда не бывают одинакового размера, так как разнятся и размеры компонентов, и количество ячеек, ими занимаемое. Положение компонента в самой ячейке таблицы GridBagLayout определяется еще несколькими полями класса GridBagConstraints, и лучше всего увидеть, как они работают, на простой иллюстрации:

Искусство расположения

 

193

 

insets.top

 

 

anchor = NORTH

 

insets.

fill

insets.

left

right

 

Компонент

 

anchor =

anchor =

 

WEST

EAST

 

 

anchor = SOUTH

 

 

insets.bottom

 

Итак, согласно иллюстрации мы получаем следующее:

 

fill — определяет, будет ли компонент заполнять все пространство ячейки, и если будет, то как. Можно применить константы HORIZONTAL, VERTICAL и BOTH, что будет соответственно обозначать заполнение всего размера ячейки по горизонтали, вертикали, или всего пространства ячейки (по обоим направлениям). По умолчанию значение этого поля в объекте GridBagConstraints равняется NONE, что означает, что компонент остается предпочтительного размера и не желает изменяться вместе с размерами ячейки.

anchor — поле, значение которого указывает, к какому краю ячейки компоненту следует «прилепиться». На рисунке показаны наиболее вероятные варианты, которые, как мы видим, названы по сторонам света, к примеру, выравнивание по левому краю называется WEST (выравнивание на запад). Есть также возможность «прилеплять» компоненты к углам ячейки.

insets — важные для любого прилично выглядящего интерфейса отступы между компонентами. Отступы задаются в виде объекта Insets, и определяют пространство между ячейками сверху, снизу, слева и справа, в пикселях.

Итак, основная идея GridBagLayout кажется понятной, и ячейки различных размеров, с полным контролем над их содержимым должны позволять создавать любые интерфейсы. Рассмотрим простой пример:

//GridBagStart.java

//Первые опыты с расположением GridBagLayout import java.awt.*;

import javax.swing.*;

public class GridBagStart extends JFrame { public GridBagStart() {

super("GridBagStart");

// выход при закрытии окна setDefaultCloseOperation(EXIT_ON_CLOSE);

194

ГЛАВА 7

//устанавливаем расположение компонентов setLayout(new GridBagLayout());

//добавляем две кнопки, ячейки по умолчанию add(new JButton("Привет"));

add(new JButton("Отмена"));

//настройка ячейки для текстового поля

GridBagConstraints textFieldConstraints = new GridBagConstraints();

//заполнение ячейки по горизонтали textFieldConstraints.fill = GridBagConstraints.HORIZONTAL;

//просим занять все оставшиеся ячейки textFieldConstraints.gridwidth =

GridBagConstraints.REMAINDER; textFieldConstraints.weightx = 1.0f;

add(new JTextField(10), textFieldConstraints);

//выведем окно на экран

setSize(400, 200); setVisible(true);

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new GridBagStart(); } });

}

}

Здесь мы устанавливаем в качестве менеджера расположения GridBagLayout, добавляем две кнопки, причем, не указывая им явно объекта GridBagConstraints. Как мы помним, это означает, что они будут добавляться в ряд, друг за другом, и иметь предпочтительный размер (именно так по умолчанию настроены поля gridx, gridy и fill). Далее мы добавим текстовое поле, и хотим, чтобы оно заняло все оставшееся место контейнера. Для этого мы указываем в объекте GridBagConstraints что текстовое поле занимает все оставшиеся в ряду ячейки и заполняет ячейку по горизонтали. На первый взгляд, все просто и должно дать нужный результат. Запустив пример, мы увидим следующее:

Источник: https://studfile.net/preview/15935977/