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

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

160

ГЛАВА 6

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

Резюме

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

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

Как вы уже могли убедиться из предыдущих примеров, добавление компонентов на форму в Java происходит достаточно необычно. Мы использовали метод add(), и, как по волшебству, на форме появлялись наши компоненты — кнопки, надписи, текстовые поля. Это может показаться странным, потому что для создания интерфейса необходимо точно указывать размер компонента и его расположение на экране — причем часто для этого используются так называемые «абсолютные» координаты, полностью зависящие от разрешающей способности экрана и его размеров. Чтобы облегчить работу с такими интерфейсами, для их создания в подавляющем большинстве сред разработки используются визуальные средства, «строители GUI». Процесс происходит следующим образом. Вы создаете новую главную форму — обычно окно с рамкой или диалоговое окно — и располагаете на ней необходимые вам компоненты, задавая для них подходящие размеры и позиции. После этого ваша форма запоминается в файлах ресурсов, которые при компиляции программы присоединяются к программе и загружаются при ее запуске. Все очень быстро и удобно.

Однако оказывается, что использование такой же схемы в Java является не лучшей идеей. Ведь здесь дела обстоят несколько иначе — не стоит забывать, что программа на Java «пишется один раз, а выполняется везде». Если уж мы собираемся писать коммерческое приложение, опирающееся на все возможности и всю мощь этого языка и его библиотек, то, конечно, мы не упустим шанса написать программу, поддерживающую все широко распространенные операционные системы, при этом абсолютно ее не изменяя. Преимущества Java перед другими языками, средами и подходами здесь неоспоримы. Но в таком случае придется как-то учесть все различия между этими операционными системами и используемыми в них виртуальными машинами Java — представьте себе, что разрешение экрана в одной системе превосходит разрешение других систем в несколько раз, а значит, меняются размеры всех объектов, выводимых на экран. Практически во всех операционных системах компоненты имеют свои особенности, и кнопка, прекрасно подходящая по размерам для Windows, в другой операционной системе может оказаться слишком большой или слишком маленькой. Или, например, у некоторого пользователя со слабым зрением все шрифты специально увеличены в два раза — в программе, использующей абсолютные размеры и написанной для другой системы, он вообще ничего не увидит. Поэтому обычные подходы (явное указание координат компонента и его размеров) в Java не оправданы.

Язык Java недаром носит титул переносимого между платформами — со всеми различиями операционных систем прекрасно справится виртуальная машина Java. Однако остается вопрос правильного расположения компонентов на форме — здесь необходима гибкость и независимость от конкретных размеров. Все это обеспечивает менеджер расположения (layout manager), который играет очень важную роль в разработке пользовательского интерфейса. Менеджер расположения — это некая программная служба1, ассоциированная с формой (в Java обычно говорят с контейнером) и определяющая, каким образом на ней будут располагаться компоненты. Независимо от платформы, виртуальной машины, разрешения и размеров экрана менеджер расположения гарантирует, что

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

162

ГЛАВА 7

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

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

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

Как работает менеджер расположения

Поддержка менеджеров расположения встроена в базовый класс всех контейнеров java.awt.Container. Все компоненты библиотеки Swing унаследованы от базового класса этой библиотеки JComponent, который, в свою очередь унаследован от класса Container. Таким образом, для любого компонента Swing вы сможете установить требуемый менеджер расположения или узнать, какой менеджер им используется в данный момент. Для этого предназначены методы setLayout() и getLayout(). Конечно, изменять расположение вы будете только в контейнерах, которые предназначены для размещения в них компонентов пользовательского интерфейса, то есть в панелях JPanel и окнах (унаследованных от класса Window). Вряд ли стоит менять расположение в кнопках или флажках, хотя такая возможность имеется.

Обязанности менеджеров расположения описаны в интерфейсе LayoutManager из пакета java.awt (есть и расширенная версия этого интерфейса с именем LayoutManager2). Любой класс, претендующий на роль менеджера расположения, должен реализовать один из этих двух интерфейсов. Они не слишком сложны, и методов в них немного, так что написать собственный менеджер расположения — не такая уж непосильная задача. Впрочем, в стандартной библиотеке Java существует несколько готовых менеджеров расположения, и с их помощью можно реализовать абсолютно любое расположение. Поэтому основная часть данной главы будет посвящена изучению уже имеющихся менеджеров, однако и вопросы создания собственных менеджеров расположения мы также затронем.

Контейнер вызывает методы менеджера расположения каждый раз при изменении своих размеров или при первом появлении на экране. Кроме того, вы можете и программно запросить менеджер расположения заново расположить компоненты в контейнере: для этого служит так называемая проверка корректности (валидация) контейнера и содержащихся в нем компонентов. Проверка корректности очень полезна, если ваш интерфейс динамически меняется, к примеру, если динамически меняются размеры компонентов (вы можете прямо во время выполнения программы изменить текст надписи или количество столбцов таблицы — все это приведет к изменению размеров). После изменений компонентам может не хватать прежних размеров или наоборот, прежний размер будет для них слишком велик, так что расположение компонентов нужно будет провести заново, иначе интерфейс рискует стать неряшливым и неудобным. Для этого и предназначена проверка корректности. Выполнить проверку корректно-

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

163

сти для любого компонента Swing, будь это контейнер или отдельный компонент, позволяет метод revalidate(), определенный в базовом классе библиотеки JComponent2 .

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

Предпочтительный размер. Такой размер идеально подходит данному компоненту. По умолчанию все размеры компонентов устанавливаются UI-представителями текущего внешнего вида и поведения (look and feel), но вы можете изменить их. Предпочтительный размер можно изменить с помощью метода setPrefferedSize().

Минимальный размер. Этот параметр показывает, до каких пор менеджер расположения может уменьшать размер компонента. После достижения минимального размера всех компонентов контейнер больше не сможет уменьшить свой размер. Минимальный размер также можно изменить, для этого предназначен метод setMinimumSize().

Максимальный размер. Этот параметр говорит о том, насколько можно увеличивать компонент при увеличении размеров контейнера. Например, максимальный размер текстового поля JTextField не ограничен, что не всегда удобно (чаще всего оно должно сохранять свой предпочтительный размер). Эту оплошность мы сможем исправить с помощью метода setMaximumSize(), устанавливающего максимальный размер. Большая часть менеджеров расположения игнорируют максимальный размер, работая в основном с предпочтительным и минимальным размерами.

Выравнивание по осям X и Y. Эти параметры нужны только менеджеру BoxLayout, причем для него они играют важнейшую роль. Поэтому их мы рассмотрим, когда дойдем до описания этого менеджера.

Границы контейнера. Эти параметры контейнера, которые позволяет получить метод getInsets(), определяют размеры отступов от границ контейнера. Иногда менеджеру расположения приходится их учитывать. В Swing и для компонентов, и для контейнеров применяются рамки Border, которые находятся прямо в пространстве компонента, отдельных отступов нет, что делает работу компонентов Swing более простой и предсказуемой.

Базовая линия. Типографский термин, обозначающий основание букв, по которому выравниваются строки текста. Для компонентов интерфейса это означает, что существует линия, по которой выравнивание компонента будет оптимальным, к примеру, базовая линия текста в текстовом поле и базовая линия текста надписи на кнопке. В противном случае менеджер расположения не знает, как выравнивать компоненты и сможет выровнять их только по верхней и нижней границе или по центру, что может привести к неудовлетворительным результатам и визуальному расхождению. Для определения базовой линии предназначен метод getBaseline(), который в своих компонентах можно переопределять.

Работа менеджера расположения происходит следующим образом: он ждет прихода сигнала от контейнера, требующего расположить в нем компоненты (этому соответствует вызов метода layoutContainer() из интерфейса LayoutManager). В методе layoutContainer()

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

2 Мы подробно обсуждали механизмы (к слову, довольно сложные) работы метода revalidate() в главе 5. Как правило, проверка корректности не вызывает проблем, но если вы вдруг оказались в сложной ситуации, ответ можно найти в упомянутой главе 5.

164

ГЛАВА 7

и контейнера, должен расположить компоненты на определенных позициях в контейнере, вызывая для каждого из них метод setBounds(), позволяющий указать (в пикселах, в системе координат контейнера) прямоугольник, который будет занимать компонент. Для сложных менеджеров расположения с гибким поведением это означает массу работы и сложные вычисления, однако простые алгоритмы расположения компонентов реализовать совсем несложно. Попробуем теперь написать свой первый менеджер расположения компонентов, он будет располагать компоненты вертикально с расстоянием между ними в 5 пикселов и использовать для всех компонентов предпочтительный размер. Вот что у нас получится:

//VerticalLayout.java

//Простой менеджер расположения, располагает

//компоненты в вертикальный ряд с отступами import java.awt.*;

import javax.swing.*;

public class VerticalLayout implements LayoutManager {

//отступ между компонентами public int GAP = 5;

//сигнал расположить компоненты в контейнере public void layoutContainer(Container c) {

Component comps[] = c.getComponents(); int currentY = GAP;

for (Component comp : comps) {

//предпочтительный размер компонента

Dimension pref = comp.getPreferredSize();

//указываем положение компонента на экране comp.setBounds(GAP, currentY,

pref.width, pref.height);

//промежуток между компонентами

currentY += GAP; currentY += pref.height;

}

}

//эти два метода нам не понадобятся public void addLayoutComponent(

String name, Component comp) {

}

public void removeLayoutComponent( Component comp) {

}

//минимальный размер для контейнера

public Dimension minimumLayoutSize(Container c) { return calculateBestSize(c);

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