Java — Swing и расположение элементов


Содержание

Графический интерфейс на Java Swing — GUI Tutorial

В Java есть 2 основных пакета для создания графических интерфейсов (Graphics User Interface). Это Abstract Windows Toolkit (AWT) и Swing. AWT использует виджеты операционной системы, поэтому эта библиотека немного быстрее. Но на мой взгляд, Swing более хорошо спроектирован.

В данном туториале мы рассмотрим основные элементы библиотеки Swing и создадим простой интерфейс (GUI) в качестве примера.

Для группировки компонент интерфейса используются контейнеры (Container). Для создания основного контейнера для приложения чаще всего используется контейнер JFrame (есть еще JWindows и JApplet). Проще всего унаследоваться от JFrame тем самым получить доступ ко множеству методов, например:

setBounds(x, y, w, h) — указывает координаты верхней левой вершины окна, а также его ширину и высоту.

setResizable(bool) — указывает, можно ли изменять размер окна.

setTitle(str) — устанавливает название окна.

setVisible(bool) — собственно отображает окно.

setDefaultCloseOperation(operation) — указывает операцию, которая будет произведена при закрытии окна.

Основные элементы управления:

  • JLabel — элемент для отображения фиксированного текста;
  • JTextField — простой edit-box;
  • JButton — обычная кнопка (button);
  • JCheckBox — элемент выбора (аналог checkbox);
  • JRadioButton — радио кнопка

Как видите, все довольно просто и логично.

При отображении элементов управления используются специальные менеджеры — LayoutManager. У всех LayoutManager’ов есть методы для добавления у удаления элементов.

FlowLayout — используется для последовательного отображения элементов. Если элемент не помещается в конкретную строку, он отображается в следующей.

GridLayout — отображения элементов в виде таблицы с одинаковыми размерами ячеек.

BorderLayout — используется при отображении не более 5 элементов. Эти элементы располагаются по краям фрейма и в ценрте: North, South, East, West, Center.

BoxLayout — отображает элементы в виде рядка или колонки.

GridBagLayout — позволяет назначать месторасположение и размер каждого виджета. Это самый сложный, но и самый эффективный вид отображения.

Стоит еще обратить внимание на обработку событий. Для этого используются так называемые Event Listeners.

Ну все, довольно теории, перейдем к примеру GUI:

getContentPane возвращает контейнер верхнего уровня. ButtonGroup служит для создания группы взаимосвязанных радио-кнопок.

Внутренний класс ButtonActionListener реализует интерфейс ActionListener. Для этого необходимо предоставить имплементацию метода actionPerformed.

JOptionPane служит для отображения диалоговых окон.

Жду ваших вопросов и комментариев. Если вы хотите больше узнать о Swing, скажите об этом, и в скором времени я напишу еще одну статью с более сложными приемами и компонентами.

Основные элементы Swing

• Окно или фрейм (frame), который может быть создан с помощьюкласса JFrame.• Невидимая панель (panel) или, как ещё её называют, pane (оконное стекло) содержит все кнопки, текстовые поля, метки и другие компоненты. Панели создаются с помощью класса JPanel.

• Оконные элементы управления, такие как кнопки JButton,текстовые поля JTextfield, списки JList, и так далее. • Схемы размещения (layout managers) компонент, которыепомогают организовать все эти кнопки и поля на панели.

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

фрейм, задайте его размер и сделайте его видимым.

Но отображение фрейма это только половина работы. Нужно ещё добавить обработку различных событий, например нажатий на кнопки. В этой главе я расскажу, как создавать окна с компонентами, а в следующей – обрабатывать события (events), которые могут произойти с компонентами окна.

Наша основная цель в этой главе – написать калькулятор, который позволяет сложить два числа и увидеть результат. Создайте новый проект в Eclipse, назовите его My Calculator и добавьте в него новый класс SimpleCalculator со следующим кодом:

public class SimpleCalculator <

public static void main(String[] args) <

JPanel windowContent= new JPanel();

// Задаём менеджер отображения для этой панели

FlowLayout fl = new FlowLayout();

// Создаём компоненты в памяти

JLabel label1 = new JLabel(«Number 1:»); JTextField field1 = new JTextField(10); JLabel label2 = new JLabel(«Number 2:»); JTextField field2 = new JTextField(10); JLabel label3 = new JLabel(«Sum:»); JTextField result = new JTextField(10); JButton go = new JButton(«Add»);

// Добавляем компоненты на панель

windowContent.add(label1); windowContent.add(field1); windowContent.add(label2); windowContent.add(field2); windowContent.add(label3); windowContent.add(result); windowContent.add(go);

// Создаём фрейм и задаём для него панель

JFrame frame = new JFrame(«My First Calculator»);

// задаём и размер и делаем фрейм видимым

Скомпилируйте и запустите эту программу. Должно появиться окноследующего вида:

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

В некоторых старомодных языках программирования необходимо было указывать координаты и размеры каждого компонента окна. Это работало хорошо, если было известно разрешающая способность экрана каждого пользователя. Кстати, людей, которые пользуются программами, называют пользователями (users). В Java есть схемы размещения (Layout Managers), которые позволяют разместить компоненты на экране, не зная точных позиций компонентов. Схемы гарантируют, что та часть интерфейса, за которую они отвечают, будет выглядеть правильно вне зависимости от размеров окна и разрешения экрана.Swing предоставляет следующие схемы:

Чтобы использовать любой из этих менеджеров, необходимо создать его экземпляр и затем назначить этот объект какому-­?нибудь

контейнеру (container), например панели, как это было в примере с SimpleCalculator.

FlowLayout – построчное расположение

По этой схемe компоненты размещаются в окне (или другом контейнере) строка за строкой. Например, текстовые метки, иконки, текстовые поля и кнопки будут добавляться в первую условную строку, пока в ней есть место. Когда первая строка заполнится, оставшиеся компоненты будут добавляться в следующую строку и так далее. Если пользователь изменит размер окна, картина может измениться. Просто потяните за угол калькулятора, чтобы поменять его размер. Посмотрите как java.awt.FlowLayout переупорядочивает элементы окна во время изменения его размеров.

В следующем примере кода, ключевое слово this представляетэкземпляр класса SimpleCalculator.FlowLayout fl = new FlowLayout();

Согласен, FlowLayout не лучшим образом подходит для нашего калькулятора. Давайте теперь попробуем что-­?нибудь другое.

Источник: Java Programming for Kids, Parents and Grandparents by Yakov Fain

Менеджеры размещения в Java

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

Менеджер размещения представляет собой один из классов FlowLayout, BorderLayout, GridLayout, CardLayout, BoxLayout, реализующих интерфейс LayoutManager, устанавливающий размещение.

Класс FlowLayout – менеджер поточной компоновки. При этом компо­ненты размещаются от левого верхнего угла окна, слева направо и сверху вниз, как и обычный текст. Этот менеджер используется по умолчанию при добавлении компонентов в апплеты. При использовании библиотеки AWT менеджер FlowLayout представляет собой класс, объявленный следующим образом:

public class FlowLayout extends Object

implements LayoutManager, Serializable

В следующем примере демонстрируются возможности поточной компоновки различных элементов управления.

/* пример # 1 : поточная компоновка по центру: FlowLayoutEx.java */

public class FlowLayoutEx extends JApplet <

private Component c[] = new Component[9];

Java — Swing и расположение элементов

При работе с Layout Manager вы выбираете один из его режимов размещения компонет, устанавливаете его для своей панели (окна апплета, приложения) и далее добавляете в панель компоненты используя разновидность метода add для этого режима. Перечислим основные режимы:

  • FlowLayout — режим, при котором компоненты размещаются слева на право и сверху вниз построчно (как текст) по мере их добавления. При этом вы не можете знать, например, сколько компонент в какой строке окажется — это решает сама система исходя из размеров компонент и размеров панели на которую они помещаются. В конструкторе класса реализирующего этот режим вы можете задать выравнивание компонент с помощью статических констант класса FlowLayout.CENTER, FlowLayout.LEFT, FlowLayout.RIGHT (по умолчанию первый). Кроме того там же можно указать горизонтальный и вертикальный зазор можду компонентами (по умолчанию он равен 5-ти пикселям). Этот режим размещения компонет мы и использовали в предыдущем шаге, помните в коде для Swing -компонент строку getContentPane().setLayout(new FlowLayout()); ею мы устанавливали для нашего апплета режим FlowLayout. А в коде под AWT компоненты мы этого не делали потому как для класса Applet он установлен по умолчанию, а вот для класса JApplet по умолчанию стоит BorderLayout а нам нужна была схожесть.
  • BorderLayout — С помощью этого режима вы можете разместить всего 5 компонент — 4 по сторонам мира и одну в центре, но зато он дает некоторую определенность в каком направлении от центра будет ваша компонента. Задают направление передав методу add в качестве параметра одну из статических констант соответствующего класса : BorderLayout.NORTH, BorderLayout.SOUTH, BorderLayout.EAST, BorderLayout.WEST, BorderLayout.CENTER. Опять же зазор можно задать (по умолчанию он равен 0). Ну насчет всего 5 — это я образно, вы можете разместить компоненты в панели JPanel (для AWT — Panel) а уже их размещать в главное окно приложения (апплета), мы так будем делать в сегодняшнем примере. Кроме того можно помещать панель в панель и так почти до бесконечности разместив таким образом какое угодно количество компонент с помощью режима BorderLayout.
  • GridLayout — в этом режиме компоненты размещаются в ячейки таблыцы, причем ячейки эти одинакового размера. Сколько строк и столбцов будет в этой таблице вы задаете в конструкторе, там же задают зазор (0 по умолчанию). Одиному из параметров (но не обеим одновременно) которые задают количество строк и столбцов можно задать 0, что будет значить что в строке или столбце соответственно может содержаться любое количество элементов. Зополнение таблицы идет слева на право и сверху вниз как в FlowLayout но здесь вы точно знаете где в каком ряду какая компонента окажется.
  • GridBagLayout — более функциональный аналог режима GridLayout. Здесь ячейки могут быть разного размера, компоненты могут иметь разные зазоры, занимать несколько ячеек и еще много чего. Это самый мощный режим и в то же время самый сложный (ну это не я так утверждаю — так в книжках пишут). При его использавании вы должны для каждого компонента передать классу этого режима условия (данные) как размещать этот компонент. С этой целью существует вспомогательный класс GridBagConstraints который является не больше как обгорткой для данных необходимых для размещения компонент. Это не значит что такой класс надо создавать для каждой компоненты — его можно создать один раз и далее меняя его поля устанавливать его для каждой компоненты с помощью метода setConstraints(ourComponent, ourGridBagConstraints) класса GridBagLayout. Рассмотрим поля которые вы можете задать в классе GridBagConstraints:
    • gridx, gridy — номер колонки и ряда ячейки(номер первой — 0) в которой будет размещен левый верхний угол компоненты (или вся компонента, если она занимает одну ячейку).
    • gridwidth, gridheight — указывает сколько ячеек по горизонтали и вертикали будет занимать компонента (по умолчанию равны 1). Кроме того можно использовать константы GridBagConstraints.REMAINDER и GridBagConstraints.RELATIVE чтобы указать что компонента будет последней в ряду (колонке) или следующей после существующей в ряду (колонке) соответственно.
    • fill — в этом поле вы можете задать что делать с компонентой в случае если выделенная область бальше ее размера. Константы здесь следующие: NONE — не увеличивать компоненту (стоит по умолчанию), HORIZONTAL — расширить компоненту чтоб ее ширина равнялась горизонтальному размеру выделенной области, VERTICAL — расширить компоненту чтоб ее высота равнялась вертикальному размеру выделенной области, BOTH — увеличить оба размера компоненты до размеров области.
    • ipadx, ipady — здесь можно задать на колько увеличить размеры компоненты по сравнению с минимальным. Например, ширина компоненты будет равняться minXsize+ipadx*2 пикселей.
    • insets — это поле характеризирует внешний зазор компоненты, представлено оно объектом Insets. Поля у этого обьекта следующие: top, left, bottom, right.
    • anchor — служит для задания места в выделенной компоненте области если компонентя меньше этой самой области. Значения может прринимать такие: CENTER(по умолчанию),NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST и NORTHWEST (это статические константы класса GridBagConstraints).
    • weightx, weighty — эти поля служат для того чтобы сообщить системе как распределить место между колонками и рядами соответственно (вроде приоритета при разделе места). Обычно принимают значения от 0.0 (по умолчанию) до 1.0.
  • CardLayout — режим при котором можно создать набор панелей как бы в одном месте и отобрадать по очереди с помощью соответствующих методов. Мы не будем пробовать его в сегоднешнем примере так как для его использования нужно обрабатывать события.
  • BoxLayout — этот режим размещает компоненты в один ряд слева направо или сверхзу вниз по вашему выбору. Выбираете вы передав конструктору одну из констант BoxLayout.Y_AXIS или BoxLayout.Х_AXIS, кроме того в конструкторе нужно указать для какого контейнера создается режим. Режим BoxLayout присутствует только библиотеке JFC/Swing в отличие от всех предыдущих режимов которые размещены в библиотеке AWT, поэтому вы не можете использовать его при написании GUI под AWT — компоненты.

Думаю, что комбинируя вышеперечисленные режимы и возможности JPanel можно разместить компоненты любым образом.

Сегодня напишем приложение с использованием JFC/Swing (неконсольное конечно). Для этого унаследуем свой главный класс от класса JFrame. Вот его родословная:
java.lang.Object -> java.awt.Component -> java.awt.Container -> java.awt.Window -> java.awt.Frame -> javax.swing.JFrame
С первыми тремя объектами мы уже знакомы. Класс Window представляет собой окно без меню и рамки и содержит основные методы для работы с окнами. Главная его роль быть базовым для более используемых окон Frame, Dialog и др. Его потомок Frame служит для создания окон которые имеют строку названия, рамку, могут иметь меню. Класс JFrame Swing-аналог своего AWT-родителя.

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

Массивы в Java являются объектами специального встроенного класса. Для их создания используют оператор []. Чтобы определить размер массива этот клаас содержит поле length.
Пример создания одномерного массива объектов:

AnyObject[] matrixOfAnyObj; //обьявляем массив matrixOfAnyObj
matrixOfAnyObj=new AnyObject[5]; //создаем массив размером в 5 элементов
//создадим объекты — элементы массива в цыкле
//индекс первого элемента массива == 0
for (int i=0; i Автор: Vitaly

Как получить все элементы внутри JFrame?

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

Я хочу сделать что-то подобное и получить все элементы, не требуя всех имен панелей. Как я это делаю. Код ниже не получает все элементы.

java swing jcomponent

4 ответа

35 Решение aioobe [2011-06-27 19:16:00]


Вы можете написать рекурсивный метод и рекурсию в каждом контейнере:

Этот сайт содержит пример кода:

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

14 toto2 [2011-06-27 19:21:00]

Посмотрите на документ для JFrame. Все, что вы помещаете в JFrame , фактически помещается в корневую панель, содержащуюся в кадре.

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

0 flakes [2020-12-09 19:25:00]

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

и его можно использовать следующим образом:

Java — Swing и расположение элементов

Последнее изменение: 6 октября 2010г.

О менеджерах раскладки

До того, как я начал писать на Java, да и после этого тоже, я, разумеется, писал и на других языках. В том числе и приложения с пользовательским интерфейсом. На C++ – врать не буду, но, кажется, это была библиотека MFC. Ну и, естественно, на Delphi. Хорошо помню, как при переходе с версии 3 на версию 4 появилась новая возможность – привязывать край компоненты к краю формы. В результате чего при изменении размеров компонента двигалась и растягивалась. Тогда это показалось прорывом.

Однако такого подхода к пользовательскому интерфейсу как в Java я не встречал нигде. Я имею в виду способ расположения компонент в контейнере. Если для тех же C++ и Delphi привычным является позиционирование компонент по абсолютным координатам, то в Java это скорее исключение из правил. Когда сталкиваешься с таким подходом впервые – это повергает в легкую прострацию. Когда начинает получаться – ощущаешь чуть ли не восторг от того, как все работает при изменении размеров и Look&Feel-а.

Однако между прострацией и восторгом существует ощутимый промежуток времени. Когда очень хочется бросить все и работать по-старому, потому как по-новому не получается. Эта статья призвана сократить данный промежуток до минимума. Итак – мы поговорим о менеджерах раскладки, они же layout manager-ы.

Содержание разговора будет следующим:

Менеджер раскладки – что это и зачем нужно

Думаю, не сильно ошибусь, если скажу, что вопрос «а зачем это нужно?» задает подавляющее большинство тех, кто сталкивается с менеджерами раскладки. Ответ кроется в заявленной изначально особенности Java, а именно – переносимости. Не секрет, что вид одних и тех же компонент на разных платформах сильно различается. Вряд ли кто-нибудь спутает окно под Motif с окном под Windows или Mac. Более того, кроме внешнего вида компонент различаются их линейные размеры. Именно потому неприменим дизайн в абсолютных единицах. Возьмем такой простой пример:

Хочу напомнить, что кроссплатформенный UI – Swing – появился гораздо позже, и был включен в Java только с версии 1.2. А до этого разработчики имели дело с AWT, базировавшемся на платформенном UI. Однако в примере я применяю именно Swing из-за наличия возможности переключиться в нужный UI.

Две метки, два поля ввода, две кнопки. Все в абсолютных координатах. И вот, как это окно выглядит в разных системах: Windows, Motif, GTK+ 2.0 (реализация Java 5), GTK+ 2.2 (реализация Java 6). В Windows и GTK 2.0 все неплохо. В GTK 2.2 хуже – не поместился текст «Cancel» на кнопке. И совсем плохо в Motif – кнопки смотрятся просто кошмарно.

Насчет GTK+ – этот вид пользовательского интерфейса наиболее распространен под X-Windows, хотя он, в общем-то, кроссплатформенный. В скомпилированном виде в Java он не входит, однако присутствует в стандартном файле исходников, src.zip . Я собрал их для версий Java 5 и Java 6. Обращаю особое внимание, что они не совместимы даже на уровне кода – версия под Java 6 не соберется под Java 5, и наоборот 1 .

И я взял всего лишь три простейших элемента. А если взять все имеющиеся?

Проблема в общем-то ясна. И решение для нее было найдено весьма оптимальное. По сути, что меняется при переходе между платформами? Размеры компонент, расстояния между ними. А взаимное расположение – оно остается тем же самым. Таким образом, разделив взаимное расположение – раскладку – и позиционирование, мы получим хорошее – и независимое от платформы! – средство для компоновки пользовательского интерфейса.

Вот так и родилась идея менеджеров раскладки. По сути они предлагают описать, не где, а как именно должны располагаться компоненты, а позиционированием занимаются сами. При этом учитываются как параметры, заданные разработчиком, так и особенности платформенного UI. А как именно это делается – мы рассмотрим в следующей части.

Теоретическая основа и принципы работы

Как я уже говорил, взаимное расположение компонентов – это еще не все. Для их реального позиционирования в контейнере нужен какой-то механизм определения размеров компонент в каждом конкретном случае. Для этого были введены такие понятия как предпочтительный размер (preferredSize) и минимальный размер (minimumSize). Эти параметры можно получить от каждой компоненты – у класса java.awt.Component есть соответствующие методы, – и менеджеры раскладки полагаются именно на них. А вот под этими методами лежат сильно разные механизмы.

В компонентах из пакета java.awt применяются специальные объекты. у меня всегда были затруднения с переводом слова peer, в русском я аналога не знаю, слово «ровня» дает слабое представление. В общем, это некий сопутствующий объект, реализованный для платформы, на которой идет исполнение. Этот объект – peer – инициализируется при привязке компонента к native-ресурсу где-то в недрах java.awt.ToolKit . Именно такие peer-объекты и отвечают за определение размеров компонентов, их отрисовку и многое другое.

В легковесных компонентах, находящихся в пакете javax.swing , применяется в чем-то схожий подход. peer-объекты заменены на т.н. делегатов пользовательского интерфейса. Наборы этих делегатов объединены в логические группы, которые называются Look&Feel. И точно так же как peer-объекты делегаты пользовательского интерфейса отвечают за определение размеров компонент, их отрисовку и т.п. Ключевое отличие следующее – LookAndFeel не привязан к платформе, он является эмуляцией ее внешнего вида. Точность эмуляции зависит исключительно от точности реализации, причем можно эмулировать и поведение компонент, например, подсветку при наведении мыши. И, соответственно, реализовав собственный LookAndFeel, можно получить принципиально иной вид приложения, не меняя ни строчки исходного кода. Именно потому в примере выше я вместо тестирования на различных платформах просто взял и поменял внешний вид. А Swing является хорошим примером реализации собственного LookAndFeel. И в варианте Swing приведенный выше пример выглядит вот так.

Впрочем, речь сейчас идет не о LookAndFeel-ах, достойных отдельной статьи. Речь о менеджерах компоновки. И для них основным является тот факт, что у любой компоненты можно получить ее предпочтительные и минимальные размеры, соответствующие текущему пользовательскому интерфейсу. Тем самым мы можем отделить логику абсолютного расположения от логики раскладки.

Второй теоретический момент, которого я хотел коснуться – внутреннее устройство менеджеров компоновки. Как я уже говорил, некоторые менеджеры позволяют задавать дополнительные пераметры, влияющие на позиционирование компонент. Иногда эти параметры общие, типа вертикального/горизонтального зазора, а иногда – отдельные для каждого компонента. Соответственно, все индивидуальные настройки хранятся в самом менеджере. Из этого вытекает вывод – менеджеры раскладки не рекомендуется разделять между несколькими контейнерами. Даже если вы уверены, что сейчас менеджер не держит внутри себя никакой дополнительной информации – в будущем это может измениться, ибо гарантируется исключительно API. В дальнейшем, когда я буду расказывать о самих менеджерах, я буду упоминать, с состоянием они или без него.

Ну, с теорией мы закончили и дошли до самого интересного. А именно –

Какие менеджеры раскладки есть в Java

Все менеджеры раскладки реализуют интерфейс java.awt.LayoutManager или java.awt.LayoutManager2 , который унаследован от первого. Всего в API их насчитывается 30 штук (я сейчас говорю о Java 6). Хотя большая часть из них занимается раскладкой составных частей сложных компонент – выпадающие списки, полосы прокрутки и т.п. Мы рассмотрим девять их них, которые предназначены для стороннего использования: java.awt.BorderLayout, java.awt.CardLayout, java.awt.FlowLayout, java.awt.GridLayout, java.awt.GridBagLayout, javax.swing.BoxLayout, javax.swing.GroupLayout, javax.swing.OverlayLayout и javax.swing.SpringLayout. Сначала будут layout-менеджеры из AWT, потом из Swing. Да, поскольку все они реализуют один интерфейс, а все компоненты – и native, и легковесные – унаследованы от одного и того же java.awt.Component – любые менеджеры могут работать с любыми компонентами.

Однако смешивать native и легковесные компоненты не стоит из-за определенных проблем с отрисовкой. Да и вообще использование AWT на сегодняшний день, на мой взгляд, потеряло актуальность.

Итак, начнем по порядку! Первый по алфавиту у нас –

java.awt.BorderLayout

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

Как рассчитывается положение компонент. Берутся предпочтительные размеры (preferred size), после чего верхняя и нижняя компоненты сохраняют высоту, не менее предпочтительной, а по ширине занимают все пространство контейнера. Боковые компоненты сохраняют ширину, не менее предпочтительной, а по высоте занимают столько, сколько осталось от высоты контейнера минус высоты верхней и нижней компоненты минус вертикальные зазоры (они задаются в конструкторе). Центральной компоненте достается то, что осталось в середине – по высоте она такая же, как боковые, по ширине – ширина контейнера минус ширины боковых компонент минус горизонтальные зазоры. Предпочтительные размеры при этом во внимание не принимаются.

Как осуществляется добавление компоненты в контейнер. Если использовать просто метод add(Component) – компонента добавится без указания положения и окажется в центре. Для того, чтобы расположить компоненту не в центре – нужно указать ограничения (constraints), в данном случае положение в контейнере. Это делается с помощью метода контейнера add(Component, Object) , вторым параметром передается именно ограничение.

Вопрос на засыпку. Что передавать? И если вы думаете, что вопрос простой – вы глубоко заблуждаетесь. У рассматриваемого BorderLayout таких ограничений – аж 13! Вот они ( http://java.sun.com/javase/6/docs/api/java/awt/BorderLayout.html#field_summary):

  • AFTER_LAST_LINE
  • AFTER_LINE_ENDS
  • BEFORE_FIRST_LINE
  • BEFORE_LINE_BEGINS
  • CENTER
  • EAST
  • NORTH
  • SOUTH
  • WEST
  • PAGE_END
  • LINE_END
  • PAGE_START
  • LINE_START

И это при том, что позиций в контейнере всего пять. Вопрос – зачем столько?

Давайте разбираться. Прежде всего, CENTER . Эта константа не нуждается в комментариях. Позиция по центру контейнера и путаницы быть не может. Далее, первые 4 константы – синонимы последних четырех. Об этом сказано в API и рекомендуется применять именно последние. Таким образом остаются две группы:

  • NORTH , SOUTH , WEST , EAST
  • PAGE_START , PAGE_END , LINE_START , LINE_END

Т.е. вопрос, по существу, сводится к следующему. Зачем нужны две группы констант? Ответ кроется в таком классе как java.awt.ComponentOrientation . Для чего он служит:

The ComponentOrientation class encapsulates the language-sensitive orientation that is to be used to order the elements of a component or of text.

Т.е. этот класс отвечает за ориентацию текста, зависящую от языка. К примеру, в романских языках письмо идет слева направо, сверху вниз, а в арабских – справа налево, сверху вниз. В японском же вообще – сверху вниз справа налево.

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

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

Цукерберг рекомендует:  Html - Использование атрибута defer

В двух нижних окнах используются константы, зависящие от ориентации, в двух правых – выставляется ориентация справа налево. Тест наглядно показывает, что при использовании набора констант NORTH , SOUTH , WEST , EAST указание ориентации не влияет на на расположение компонент, а при использовании набора PAGE_START , PAGE_END , LINE_START , LINE_END – влияет.

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

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

В заключение могу сказать, что BorderLayout является одним из наиболее удобных и часто используемых менеджеров раскладки. Именно этот менеджер установлен по умолчанию у панели содержимого (content pane) javax.swing.JFrame и javax.swing.JDialog , а также у java.awt.Frame и java.awt.Dialog .

Перейдем теперь к следующему менеджеру –

java.awt.CardLayout

Признаюсь честно – за все время знакомства с Java я не использовал CardLayout ни разу. Просто не было необходимости. Мне сложно представить, где его можно использовать кроме как при реализации закладок, а это уже сделано за нас. Но тем не менее, такой менеджер существует и мы его расмотрим. В окне, приведенном на рисунке, менеджер CardLayout выставлен в контейнере, лежащем в центре окна. Сверху и снизу – элементы, демонстрирующие работу менеджера.

Менеджер раскладки CardLayout позволяет разложить компоненты как колоду карт – одна над другой. При этом видна всегда только верхняя компонента – остальные спрятаны (посредством вызова setVisible(false) ). Компоненты занимают все доступное пространство контейнера, предпочтительные размеры игнорируются. Менеджер хранит в себе все компоненты в порядке добавления, т.е. он с состоянием.

Немаловажный момент – каждой компоненте ставится в соответствие некое имя. Попытка добавить компоненту в контейнер без указания имени приведет к ошибке! Таким образом, при использовании CardLayout для добавления компонент в контейнер необходимо вызывать метод контейнера add(String, Component) . Первым параметром передается имя, вторым компонента.

На самом деле тут ситуация несколько интереснее. Я не стал заострять на этом внимание при рассмотрении BorderLayout , но там то же самое. Дело в том, что имя используется в качестве ограничения (constraint), а потому – методы add(String, Component) и add(Component, Object) дадут одинаковый результат. В случае BorderLayout набор ограничений (по совместительству имен) содержит 13 элементов, в случае же CardLayout имена не ограничены.

Если в каждый момент времени видна только одна компонента, то возникает резонный вопрос – как увидель остальные? Очень просто. Менеджер раскладки позволяет позиционироваться по компонентам. Поскольку он держит их порядок – перемещаться можно на первую, последнюю, следующую и предыдущую компоненты. Если нет предыдущей или следующей – показывается последняя или первая соответственно, т.е. навигация по next / previous – циклическая.

Существует также возможность спозиционироваться на нужную компоненту по ее имени, тому самому, которое выставляется при добавлении в контейнер. Формально уникальность имен не требуется, можно хоть все компоненты добавить с одним и тем же именем. Однако, если вы хотите использовать именя для позиционирования – они должны быть уникальны. Позиционирование производится с помощью метода show(Container, String) менеджера CardLayout . Обратите внимание, что контейнер передается в явном виде. Это нужно потому, что менеджер хранит только соответствия компонент именам и порядок добавления компонент (хотя реально он им не пользуется, а перебирает содержимое контейнера). Однако передать «чужой» контейнер не получится – у переданного контейнера проверяется менеджер раскладки.

Собственно, больше о CardLayout рассказать, пожалуй, нечего. Ниже приведен пример, иллюстрирующий описаные возможности.

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

Переходим теперь к следующему менеджеру компоновки –

java.awt.FlowLayout

Это, пожалуй, самый простой менеджер компоновки. Он выстраивает компоненты в линию. Стоит упомянуть, что именно этот менеджер выставлен по умолчанию у обычной панели ( java.awt.Panel / javax.swing.JPanel ). В окне, приведенном на рисунке, менеджер FlowLayout выставлен на верхней панели, имеющей красную границу.

Размеры компонентам выставляются предпочтительные. Если компонента не помещается на текущей линии – она переносится на следующую. Причем обратите внимание – компонента может оказаться за пределами видимой границы контейнера! Это можно увидеть в примере, приведенном ниже – достаточно сильно уменьшить ширину окна, чтобы текстовое поле «убежало» за нижнюю границу.

«Располагает в линию» – это, конечно, хорошо. Однако такое определение ни о чем не говорит, ибо эту самую строку можно прижать к краю, а можно расположить по центру. Да и по вертикали есть, где развернуться. Об этом мы и поговорим.

Начнем с горизонтали. У FlowLayout есть пять режимов выравнивания: FlowLayout.LEFT , FlowLayout.CENTER , FlowLayout.RIGHT , FlowLayout.LEADING , FlowLayout.TRAILING . С первыми тремя понятно, а что означают последние два?

Честно сказать, я был абсолютно уверен, что только в случае использования этих двух типов выравнивания учитывается направление текста языка, пресловутый ComponentOrientation . Однако это не так. FlowLayout всегда учитывает направление текста и располагает компоненты согласно ему. Режимы выравнивания определяют только одно – куда будет выровнена уже сформированная строка. Так вот, в случае использования абсолютных режимов – FlowLayout.LEFT , FlowLayout.CENTER и FlowLayout.RIGHT – строка будет выровнена по левому краю, центру и правому краю соответственно. Вне зависимости от направления текста. А вот при использовании FlowLayout.LEADING и FlowLayout.TRAILING строка будет выравниваться по логическому началу и концу строки – левый/правый край соответственно при направлении текста слева направо и правый/левый край при направлении текста справа налево.

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

Что такое базовая линия. Это логическое понятие. У шрифта базовой называется линия, на которой расположены нижние края букв (имеются в виду буквы, не уходящие ниже строки – ‘a’, ‘b’, ‘c’, но не ‘g’, ‘p’, ‘y’). Точно так же можно определить базовую линию компоненты. Для JLabel , JButton , к примеру, – это базовая линия надписи на них. Честно сказать, я не вдавался глубоко в этот вопрос, да и не важен он сейчас. Желающие могут посмотреть в исходном коде, как определяется базовая линия для различных компонент.

Я специально изменил в примере размеры шрифта, чтобы базовые линии компонент не совпадали. Если галка Align on baseline не выставлена – компоненты выровнены по центру. Если же ее поставить – они выравниваются по базовой линии JLabel , т.к. она самая низкая из всех.

Ну, вот теперь можно и перейти к примеру:

Пожалуй, про FlowLayout больше сказать нечего. Разве что только то, что этот менеджер без состояния – раскладка пересчитывается каждый раз.

Идем дальше. Следующий по списку –

java.awt.GridLayout

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

Количество строк или столбцов может быть задано равным 0 – тогда этот параметр рассчитывается по количеству компонент и второму параметру. Оба в 0 выставлены быть не могут – конструктор не позволит. Если же оба параметры ненулевые – количество строк имеет преимущество, количество столбцов рассчитывается.


Тот факт, что все столбцы должны быть одной ширины, приводит к одному любопытному эффекту. При изменении ширины контейнера раскладка меняется скачкообразно. Скажем, если у нас есть 4 столбца, для изменения раскладки нужно изменить ширину на 4, 8 и т.д. точек, тогда каждый столбец получит по одной точке дополнительной ширины. Со строками то же самое. Чем больше столбцов/строк – тем явственней это проявляется.

Ориентация текста – ComponentOrientation – учитывается при раскладке. Предпочтительные размеры компонент – наоборот. Разумеется, как и любой менеджер раскладки GridLayout умеет рассчитывать предпочтительный размер контейнера, причем за размеры ячейки берется максимальные ширина и высота из всех предпочтительных размеров всех компонент контейнера. Но при раскладке все компоненты полностью занимают предоставленные им ячейки.

Небольшой пример, иллюстрирующий использование GridLayout , приведен ниже.

Кто следующий? О, тут есть о чем поговорить! Следующий у нас –

java.awt.GridBagLayout

Это, пожалуй, самый используемый менеджер раскладки. Причиной тому его гибкость и большой набор настроек. GridBagLayout раскладывает компоненты в прямоугольной сетке. На снимке окна эта сетка нарисована в явном виде (вручную). В реальном окне ее, естественно, не видно. Компонента может занимать любую прямоугольную область, даже состоящую из нескольких ячеек сетки. На рисунке это хорошо видно – картинка слева и текстовые поля занимают по две ячейки.

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

Итак, что можно указать в качестке ограничений. Ну, во-первых, положение в сетке. Координаты верхнего левого угла. Во-вторых – количество занимаемых строк/столбцов. Заполняет ли компонента ячейку, в каком направлении. Куда прижимается – к какой-либо стороне, в угол, по центру. Пустое пространство вокруг компоненты. Распределение свободного пространства. И вот это вот обилие настроек играет злую шутку – человек, встретившийся с ними впервые, просто теряется. Между тем, достаточно разобраться с ними всего лишь один раз. Этим мы сейчас и займемся. Пока же я приведу исходный код того окна, которое используется в качестве иллюстрации.

Этот код дан скорее для общего представления об использовании GridBagLayout . А рассмотрение ограничений мы начнем с weightx / weighty .

Параметры weightx / weighty

Эти два параметра вызывают больше всего вопросов. Мы рассмотрим использование weightx , второй параметр аналогичен.

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

Ответом на этот вопрос служат параметры weightx , указаные у каждой из компонент. Если оба параметра выставлены в 0 – свободное пространство остается снаружи компонент, они висят в центре контейнера. Это как раз тот случай, которые показан на снимке.

Пусть теперь хотя бы у какой-нибудь из компонент ненулевой weightx . Как рассчитывается распределение? Пусть у первой компоненты для определенности выставлено значение weightx=A , у второй – weightx=B . Ширина свободного пространства – W . Тогда первая компонента получит в свое пользование W * A / (A + B) пространства, вторая – W * B / (A + B) . Если мы сложим эти две величины, то увидим, что в сумме они дадут W , т.е. пространство распределено полностью.

Когда я говорю, что пространство отдано компоненте – я имею в виду столбец, в котором она находится. Его ширина принимается равной предпочтительной ширине компоненты плюс выделенное свободное пространство. Т.е. при соотношении weightx двух компонент, равном 1:1, ширины столбцов, которые они занимают, вовсе необязательно будут равны. Лишнее пространство – оно да, распределится поровну. А остальное зависит от предпочтительных размеров самих компонент. Вот это очень часто служит камнем преткновения.

Код этого примера приведен ниже.

Мой вам совет – поиграйтесь с этим тестом. Посмотрите, как распределяется пространство. Можете раскомментировать строку, в которой выставляется другой предпочтительный размер правому выпадающему списку, и посмотреть, как распределяется пространство при равных weightx . Довольно простой код, а понимание использования weightx дает хорошее.

На самом деле и тут есть свои тонкости. Прежде всего – несовместимые weightx . Допустим, у нас две строки, по две компоненты в каждой. В первой weightx установлены в 1 и 2, во второй – в 2 и 1. Вот и вопрос – что будет. Не, тут-то все просто, возьмутся максимальные значения. Но существует множество ситуаций, когда распределение пространства, выполняемое GridBagLayout -ом, неочевидно. Хотя чаще всего все свободное пространство отдается одному столбцу, так что с проблемами я, например, в жизни не сталкивался ни разу.

Точно так же работает и weighty , алгоритмы написаны под копирку.

Перейдем теперь к положению в сетке –

Параметры gridx / gridy / gridwidth / gridheight

На первый взгляд тут все просто. gridx / gridy определяют координаты в сетке ячейки, в которой находится компонента. Ну или левой верхней ячейки, если компонента занимает несколько. В приведенном примере две ячейки по вертикали занимает изображение, две ячейки по горизонтали – поля ввода.

В принципе, этих знаний уже достаточно, чтобы разложить компоненты в сетке. Есть, однако, пара нюансов – константы GridBagConstraints.RELATIVE и GridBagConstraints.REMAINDER . Первая из них является допустимым значением для всех четырех параметров, вторая – для gridwidth / gridheight . Особенность этих констант в том, что они относительные.

Сначала поговорим о gridx / gridy . Дообавление в контейнер компоненты с ограничением gr > приведет к тому, что эта компонента будет расположена непосредственно за компонентой, добавленной в контейнер перед ней. Аналогично с gridy – добавляемая компонента расположится под компонентой, добавленной перед ней.

Теперь о gridwidth / gridheight . Для них GridBagConstraints.RELATIVE означает, что компонента будет расположена за последней из уже добавленных в контейнер, находящейся в той же самой строке (в том же самом столбце), что и добавляемая. Константа же GridBagConstraints.REMAINDER указывает на то, что компонента будет расположена самой последней в строке или столбце соответственно.

Преимущество такого подхода – можно менять расположение компонент, изменяя порядок их добавления в контейнер. Недостаток очевиден – относительные инструкции могут быть противоречивы. Скажем, если у компоненты при добавлении выставить одновременно gr > – она должна быть смещена относительно последней добавленной по двум перпендикулярным осям одновременно. В документации рекомендуется использовать абсолютные позиции. Вероятно поэтому во многих визуальных дизайнерах отсутствует возможность выставлять относительные значения в явном – текстовом – виде. Использовать значения констант при этом не запрещается. Я сам когда-то начинал именно с относительных координат, однако в последнее время склоняюсь к абсолютным ввиду большей предсказуемости. По большому счету – дело вкуса.

Следующий интересный аспект –

Параметры anchor / fill

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

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

Для начала объясню, почему эти два параметра, anchor и fill , я поместил в одном разделе. Казалось бы, где Рим, а где Крым. Однако эти параметры неявным образом связаны. Вернее, так – установка параметра fill может нейтрализовать установку anchor . Если компонента, допустим, растянута по горизонтали, то я могу прижать ее к правой стороне занимаемой области, к левой, вообще оставить в центре – на расположение это никак не повлияет. Существенно только вертикальное расположение. То же самое получится при вертикальном растяжении. А если компонента занимает всю предоставленную область (т.е. растянута по обеим осям) – значение anchor не играет вообще никакой роли.

Кстати, именно поэтому в JFormDesigner и IntelliJ IDEA в визуальных дизайнерах эти два параметра вообще совмещены. Вместо них используются настройки Vertical align/Horizontal align (у IDEA) и h align/v align (у JFormDesigner). Логику я понимаю, но не одобряю.

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

Как и в случае с java.awt.BorderLayout есть относительные и абсолютные константы. Абсолютные опираются на стороны света (сверху, по часовой стрелке):

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

  • PAGE_START
  • FIRST_LINE_END
  • LINE_END
  • LAST_LINE_END
  • PAGE_END
  • LAST_LINE_START
  • LINE_START
  • FIRST_LINE_STAT

И особняком стоят константы, появившиеся в версии Java 6:

  • BASELINE
  • BASELINE_LEADING
  • BASELINE_TRAILING
  • ABOVE_BASELINE
  • ABOVE_BASELINE_LEADING
  • ABOVE_BASELINE_TRAILING
  • BELOW_BASELINE
  • BELOW_BASELINE_LEADING
  • BELOW_BASELINE_TRAILING

Ну и, естественно, CENTER , куда без нее?

С первыми двумя блоками, думаю, понятно. Остановимся на третьем. Он опять-таки завязан на понятие базовой линии. Легко заметить, что параметры только относительные (что неудивительно, на мой взгляд, ибо предназначены они в первую очеред для создания UI, ориентированного на текст). Оставив в стороне всё с приставками _LEADING / _TRAILING , мы получим три варианта – BASELINE , ABOVE_BASELINE и BELOW_BASELINE .

Работают они так. BASELINE выравнивает компоненту по базовой линии первой строки. Поясню, что это. На приведенном примере можно увидеть две текстовых метки. Кнопка же (вернее, область, где она находится) занимает по вертикали две строки. Так вот, берутся компоненты только из первой строки, в данном случае это метка Label И сама кнопка! Причем обратите внимание – хотя бы у одной компоненты в ограничениях должен стоять тип выравнивания BASELINE (допустимы BASELINE_LEADING / BASELINE_TRAILING ), иначе считается, что базовой линии нет.

Когда базовая линия определена, рассчитывается положение компоненты. BASELINE означает, что базовая линия компоненты выровнена по базовой линии строки. ABOVE_BASELINE – что нижний край компоненты выровнен по базовой линии строки. Соответственно, BELOW_BASELINE выравнивает верхний край компоненты по базовой линии. Если же базовая линия не определена – компонента центрируется по вертикали.

Ну и, естественно, иллюстративный пример:

Обратите внимание на положение кнопки при выравнивании по базовой линии. Замените выравнивание для метки Label – вместо BASELINE поставьте CENTER . И посмотрите, что получается в режимах ABOVE_BASELINE / BELOW_BASELINE . Поскольку базовая линия не определяется – компонента центрируется по вертикали.

Ну вот, с этим вроде тоже разобрались. Осталось немного.

Параметры insets и ipadx / ipady

В отличие от рассматриваемых ранее параметров, определявших логическое размещение компонент, insets , ipadx и ipady служат для абсолютной корректировки. insets описывает прозрачную рамку вокруг компоненты. Это очень полезно для того, чтобы оставлять между компонентами небольшие расстояния. ipadx и ipady наоборот – добавляются к размерам самой компоненты. Причем обратите внимание – значение параметра добавляется с каждой стороны. Т.е. размер компоненты увеличивается на 2*ipadx по горизонтали и 2*ipady по вертикали. Честно сказать, эти параметры я не использую.

Ну, вот, вроде и все про GridBagLayout . Информации много, но повторюсь – если ее осознать, использовать этот менеджер просто. Основное, что надо сделать при использовании GridBagLayout :

  1. Расчертить сетку и определить положение компонент. За это отвечают gridx / gridy / gridwidth / gridheight .
  2. Определить, как распределять свободное пространство. Это weightx / weighty .
  3. Определить расположение компонент в предоставляемых им областях. Это параметры anchor и fill .
  4. Определить промежутки между компонентами и (если необходимо) «прибавки» к размерам – insets и ipadx / ipady .

Именно в перечисленном порядке и указываются эти параметры в конструкторе GridBagConstraints . Хочу обратить внимание еще и на то, что совсем необязательно каждый раз создавать новый экземпляр. Можно создать один и устанавливать ему нужные параметры (они все public ) – при установке ограничений переданный экземпляр GridBagConstraints клонируется!

Уф. можно пересести дух. Мы закончили рассматривать менеджеры, существующие в AWT еще с Java 1.0. Теперь перейдем к тому, что появилось в Swing.

Здесь я хочу сделать одно разъяснение. Первоначально я намеревался плотно углубиться во все имеющиеся в Swing менеджеры раскладки. Однако по мере знакомства с ними у меня все больше возникало ощущение, что, собственно, углубляться-то не стоит. Внимания заслуживает только BoxLayout, и то по большей части обзорного. У GroupLayout и SpringLayout достаточно специфическое предназначение, если верить документации. Предназначения же OverlayLayout я, честно сказать, просто не понимаю.

Как бы то ни было – менеджеры раскладки в Swing тоже существуют. И мы на них посмотрим. Первым будет.

javax.swing.BoxLayout

Представьте себе коробку. Длинную и узкую. Что бы вы ни клали туда – все будет собираться в стопку, даже если теоретически есть возможность положить рядом. Схематически это и есть поведение BoxLayout . Этот менеджер раскладывает компоненты в том порядке, в каком они были добавлены в контейнер, друг за дружкой, вплотную.

Единственный конструктор BoxLayout -а требует указания контейнера, на объектах которого менеджер и будет ставить свои опыты. В смысле, выполнять раскладки. Уже хотя бы по этому можно заключить, что менеджер – с состоянием.

Кроме указания контейнера конструктор требует указать ось, вдоль которой он будет осуществлять раскладку. Вариантов тут предусмотрено четыре: X_AXIS , Y_AXIS , LINE_AXIS , PAGE_AXIS . Как нетрудно догадаться, первые два из них – абсолютные, следующие два зависят от ориентации текста.

Как, собственно, менеджер осуществляет раскладку. По выбранной оси понятно – он располагает компоненты вплотную друг к другу. А что происходит по другой оси?

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

У каждой компоненты (здесь я имею в виду любого наследника java.awt.Component ) есть такие методы как getAlignmentX и getAlignmentY , возвращающие значения типа float в промежутке [0;1] . Это значение определяет положение некой воображаемой оси (от левой границы и верха соответственно). Так вот, компоненты располагаются в контейнере так, чтобы эти воображаемые оси были на одной линии. На сайте Sun есть замечательный иллюстративный пример на эту тему: http://java.sun.com/docs/books/tutorialJWS/uiswing/layout/ex6/BoxLayoutDemo2.jnlp (если JavaWebStart у вас не настроен – можно просто скопировать ссылку и в командной строке набрать javaws ). Там прорисованы три прямоугольника, по которым можно кликать и выставлять таким образом новое значение alignmentX .

В выбор положения оси, по которой выравниваются все компоненты, я углубляться не буду. Желающие могут найти этот алгоритм в исходниках класса SizeRequirements , метод getAlignedSizeRequirements .

Весьма полезным при использовании BoxLayout будет класс javax.swing.Box . Он представляет собой JComponent с уже установленным BoxLayout . Интереснее, однако, его статические методы: createHorizontalGlue , createVerticalGlue , createHorizontalStrut , createVerticalStrut . Первые два из них позволяют создать невидимую компоненту, которая будет забирать под себя все свободное пространство соответственно по горизонтали/вертикали. Вторые два позволяют создать невидимую компоненту с указанной соответственно шириной/высотой и нулевым вторым размером. Это может быть полезно для создания зазоров между компонентами либо для обеспечения ширины/высоты контейнера (если использовать их в вертикальной/горизонтальной раскладке соответственно). Для той же цели служит и метод createRigidArea , с той только разницей, что он создает невидимую компоненту с обоими фиксированными размерами.

Код примера приведен ниже:

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

Собственно, про BoxLayout сказать больше нечего. Самый наглядный пример его применения – строка меню ( javax.swing.JMenuBar ).

Пойдем дальше. Следующий менеджер –

javax.swing.GroupLayout

Этот менеджер раскладки появился только в Java 6. Как сказано в документации:

GroupLayout is intended for use by builders, but may be hand-coded as well.

Собственно говоря, вот это «is intended for use by» меня и остановило. Подробнее на эту тему я поговорю в разделе, посвященном SpringLayout. Пока же краткая характеристика.

GroupLayout раскладывает компоненты по группам. Группы имеют направление по оси и могут быть параллельными и последовательными. В последовательной группе у каждой следующей компоненты координата вдоль оси на единицу больше (имеется в виду координата в сетке), в параллельной – компоненты имеют одну и ту же координату. Собственно, все это описано в API: http://java.sun.com/javase/6/docs/api/javax/swing/GroupLayout.html. Там же приведены и варинты использования.

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

Больше о GroupLayout я говорить не буду, по крайней мере пока. В Sun Java Tutorial он описан достаточно хорошо: http://java.sun.com/docs/books/tutorial/uiswing/layout/group.html. А мы пойдем дальше. Следующий менеджер –

javax.swing.OverlayLayout

Этот менеджер для меня – самая большая загадка. Он располагает компоненты одну над другой. Причем первая компонента располагается ближе всех, последняя – дальше всех. Как это выглядит для трех меток – вы можете увидеть на снимке окна. Я специально сделал разные цвета, чтобы было лучше видно. Исходный текст тут.

Зачем нужен такой менеджер раскладки – я не могу придумать при всем желании. Кстати, в tutorial от Sun он даже не упоминается.

Ну и последний менеджер –

javax.swing.SpringLayout


Он появился в Java в версии 1.4. Принцип действия – связывание границ компонент. Т.е. граница компоненты привязывается к границе другой компоненты или к границе контейнера. Можно задать фиксированное расстояние, можно использовать специальный класс javax.swing.Spring , который держит в себе минимальное, предпочтительное и максимальное значение для длины связи.

Как сказано в документации (вернее, в tutorial):

The SpringLayout class was added in JDK version 1.4 to support layout in GUI builders.

Вам не кажется, что я только что цитировал что-то подобное?

Как я уже говорил, SpringLayout появился в версии 1.4, вышедшей, если я правильно помню, в 2002-м году. Ну никак не позже 2003-го, ибо в декабре 2003-го я уже смотрел на Java 5 beta. Так вот, как я упоминал в статье о визуальных дизайнерах, SpringLayout до сих пор не поддерживается ни одним из визуальных дизайнеров. По причине того, что он сложен для реализации и практически никем не используется.

Философское отступление

На самом деле ситуация более чем прозрачна. Чем дальше, чем больше я лично сталкиваюсь с таким явлением – то, что написано специально для чего-то, как правило, оказывается нежизнеспособным. Происходит это потому, что разработчики пытаются предсказать варианты использования, а это дело гиблое. Варианты использования можно пощупать только на практике.

Классический пример – EJB, entity-beans (до 3.0). Этот вариант ORM был разработан и стандартизирован ДО того, как его начали использовать. И по прошествии нескольких лет мучений в версии 3.0 это творение было заменено на то, что уже давно было известно как best practice, а именно – Hibernate.

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

То же самое произошло и со SpringLayout -ом. Он написан специально для использования в визуальных редакторах – и является единственным менеджером, который не поддерживает ни один визуальный редактор.

Что касается меня – упоминание о том, что эта функциональность разработана «специально для», вызывает у меня изначальную настороженность. Особенно в случае, если эта функциональность не выкристаллизовалась естественным образом из давно проверенного решения.

Ввиду вышеуказанного отступления я счел нецелесообразным тратить время на исследование SpringLayout . К тому же он достаточно хорошо описан в Sun Java Tutorial – http://java.sun.com/docs/books/tutorial/uiswing/layout/spring.html. Ровно по той же причине я пока не стал тратить время на GroupLayout . Посмотрим, насколько он приживется. В данный момент он не поддерживается ни в IntelliJ IDEA 7, ни в NetBeans 6 (по крайней мере в beta1 я его не видел).

Ну, вот мы и добрались до последней темы.

Пишем собственный менеджер – CircleLayout

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

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

Цукерберг рекомендует:  Меняем jQuery Ready() на чистый JavaScript

Для упрощения задачи будем писать менеджер без состояния. Таким образом, из всех методов java.awt.LayoutManager2 нам надо реализовать только лишь layoutContainer , minimumLayoutSize , preferredLayoutSize и maximumLayoutSize . Причем реализация последнего тривиальна:

Опять-таки для упрощения задачи будем всегда полагаться на предпочтительный размер компонент. Таким образом minimumLayoutSize всегда будет тем же самым, что и preferredLayoutSize .

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

Собственно, всё. После определения радиуса окружности и ширины полосы рассчитываютя полуоси эллипса (в зависимости от выбранного режима растяжения), а потом компоненты располагаются центрами по этому эллипсу.

Полный исходный код вместе с примером использования находится вот тут: circle-layout.zip. Как всегда в архиве присутствует и build-файл для ant.

Ну, вот и всё. Я рассказал о менеджерах раскладки все, что хотел. Надеюсь, было познавательно!

Введение в Swing

Описание: Это практическое введение в Swing — первая часть серии по Swing-программированию, состоящей из двух частей. В данном руководстве рассмотрены основные компоненты библиотеки Swing. Java-программист и любитель Swing Майкл Абернети рассказывает об основных строительных блоках и о процессе создания простого, но функционального Swing-приложения.

Содержание

Перед началом работы

О данном руководстве

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

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

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

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

Загрузка инструментальных средств и исходных кодов

Для работы с данным руководством вам необходимо следующее программное обеспечение:

  • JDK 5.0.
  • IDE или текстовый редактор. Я рекомендую Eclipse (ссылки на дополнительную информацию по Eclipse можно найти в разделе Ресурсы).
  • swing1.jar для системы резервирования билетов.

Введение в Swing

Введение в UI

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

Итак, я опять задаю вопрос: что такое UI? Вы могли бы определить его как кнопки, на которые нажимаете, поле адреса, в которое вводите информацию, и окна, которые открываете и закрываете. Все это — элементы UI, но это не только вещи, которые вы видите на экране. Мышка, клавиатура, громкость музыки, цвета экрана, используемые шрифты и расположение объекта по отношению к другому объекту — все это тоже составляет UI. Вообще говоря, любой объект, принимающий участие во взаимодействии пользователя и компьютера, является частью UI. Это кажется таким простым, что вы, должно быть, удивитесь тому, как много людей и корпораций занимаются этим годами. Действительно, сейчас есть дисциплины в колледжах, полностью посвященные этому взаимодействию.

Роль Swing

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

  • Клавиатура: Swing предоставляет способ перехвата пользовательского ввода.
  • Цвета: Swing предоставляет способ менять цвета, которые вы видите на экране.
  • Текстовое поле для ввода: Swing предоставляет текстовые компоненты для обработки всех повседневных задач.
  • Громкость музыки: Ну что ж … Swing не совершенен.

В любом случае Swing предоставляет вам все инструменты, необходимые для создания вашего собственного UI.

Swing идет даже на шаг дальше и реализует известный шаблон проектирования c основными принципами UI. Этот шаблон проектирования называется Модель-Представление-Контроллер (Model-View-Controller — MVC) и стремится «разделить роли». MVC хранит код, ответственный за внешний вид чего-нибудь, отдельно от кода, обрабатывающего данные, и отдельно от кода, реагирующего на взаимодействие и выполняющего изменения.

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

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

Эта концепция лежит в основе шаблона проектирования MVC: пусть каждый аспект UI имеет дело с тем, что он хорошо знает. Если вы все еще смущены, примеры в оставшейся части руководства, будем надеяться, смягчат это; но помните об основном принципе: визуальные компоненты отображают данные, а другие классы управляют ими.

JComponent

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

  • JComponent — базовый класс не только для Swing-компонентов, но также и для пользовательских компонентов. (более подробная информация приведена в руководстве «Swing для среднего уровня«).
  • Он обеспечивает инфраструктуру окрашивания для всех компонентов — нечто, становящееся удобным для пользовательских компонентов.
  • Он знает, как обрабатывать все нажатия клавиш на клавиатуре. Подклассы, следовательно, должны только прослушивать определенные клавиши.
  • Он содержит метод add() , который позволяет вам добавить другие JComponent . Этим способом вы можете добавить любой Swing-компонент к любому другому Swing-компоненту для создания вложенных компонентов (например, JPanel, содержащую JButton, или даже более причудливые комбинации, например JMenu, содержащее JButton).

Простые Swing-виджеты

JLabel

Самым основным компонентом в библиотеке Swing является JLabel. Он делает именно то, чего вы от него ожидаете: располагается в нужном месте, улучшает вид приложения и описывает другие компоненты. На приведенном ниже изображении показан JLabel в действии:

Не очень впечатляюще, но все равно полезно. Фактически, вы используете компоненты JLabel по всему приложению не только как текстовые описания, но и как графические описания. Когда бы вы ни увидели изображение в Swing-приложении, есть шанс, что это JLabel. У JLabel не много методов для новичков в Swing, кроме, возможно, ожидаемых вами. К основным методам относится установка текста, изображения, выравнивания и других компонентов, которые описывает метка:

  • get/setText() : Получить/установить текст в метке.
  • get/setIcon() : Получить/установить изображение в метке.
  • get/setHorizontalAlignment() : Получить/установить горизонтальную позицию текста.
  • get/setVerticalAlignment() : Получить/установить вертикальную позицию текста.
  • get/setDisplayedMnemonic() : Получить/установить мнемонику (подчеркнутый символ) для метки.
  • get/setLabelFor() : Получить/установить компонент, к которому присоединена данная метка; когда пользователь нажимает комбинацию клавиш Alt+мнемоника, фокус перемещается на указанный компонент.

JButton

Основным активным компонентом в Swing является JButton, кнопка, которую вы видите (с надписями OK и Cancel) в каждом окне; он делает именно то, что ожидается от кнопки — вы нажимаете на нее, и что-то происходит. Что именно происходит? Да, вы должны определить это (дополнительная информация приведена в разделе События). JButton в действии выглядит следующим образом:

Методы, используемые для изменения свойств JButton, аналогичны методам JLabel (вы обнаружите, что они аналогичны для большинства Swing-компонентов). Они управляют текстом, изображениями и ориентацией:

  • get/setText() : Получить/установить текст в кнопке.
  • get/setIcon() : Получить/установить изображение в кнопке.
  • get/setHorizontalAlignment() : Получить/установить горизонтальную позицию текста.
  • get/setVerticalAlignment() : Получить/установить вертикальную позицию текста.
  • get/setDisplayedMnenomic() : Получить/установить мнемонику (подчеркнутый символ), которая в комбинации с кнопкой Alt вызывает нажатие кнопки.

Кроме этих методов я познакомлю вас с еще одной группой методов, которые содержит JButton. Эти методы используют все различные состояния кнопки. Состояние — это свойство, описывающее компонент, обычно имеющее значение true/false. В случае с JButton он имеет следующие возможные состояния: активная/неактивная, выбранная/не выбранная, мышка сверху/мышки нет, нажата/отжата. Кроме того, вы можете комбинировать состояния, например: кнопка может быть выбрана и над ней находится мышка. Сейчас вы, возможно, спрашиваете себя, для чего мне все эти состояния. В качестве примера переместитесь к кнопке Back вашего браузера. Обратите внимание на то, как меняется изображение при наведении на нее указателя мышки, и как оно меняется при нажатии на кнопку. Эта кнопка использует различные состояния. Использование различных изображений для каждого состояния является популярным и очень эффективным способом отображать действия пользователей. Методами состояния для JButton являются:

  • get/setDisabledIcon()
  • get/setDisabledSelectedIcon()
  • get/setIcon()
  • get/setPressedIcon()
  • get/setRolloverIcon()
  • get/setRolloverSelectedIcon()
  • get/setSelectedIcon()

JTextField

Основным текстовым компонентом в Swing является JTextField; он позволяет пользователям вводить текст в UI. Я уверен, что вы знакомы с текстовым полем; вы должны были использовать его при вводе имени пользователя и пароля для получения данного руководства. Вы вводите, удаляете, выделяете текст, перемещаете курсор — Swing заботится обо всем, что вы делаете. Как UI-разработчику вам действительно не нужно делать много для того, чтобы использовать возможности JTextField.

В любом случае, вот как выглядит JTextField в действии:

Вы должны интересоваться только одним методом при работе с JTextField, и это должен быть, очевидно, метод, устанавливающий текст: get/setText() . Этот метод получает/устанавливает текст внутри JTextField.

JFrame

Пока я рассмотрел три основных строительных блока Swing: метку, кнопку и текстовое поле; теперь вы должны куда-то поместить их. Они не могут просто летать по экрану в надежде на то, что пользователь знает, как работать с ними. Класс JFrame делает именно это — он является контейнером, позволяющим добавлять к себе другие компоненты для их организации и предоставления пользователю. Он имеет много других бонусов, но я считаю, что легче всего сначала на него посмотреть:

JFrame

JFrame на самом деле делает больше, чем просто позволяет вам размещать компоненты на нем и предоставлять их пользователю. Несмотря на всю его кажущуюся простоту, он фактически является одним из самых сложных компонентов в Swing-пакетах. Говоря очень упрощенно, JFrame выступает в качестве моста между независимыми от OS Swing-частями и реальной OS, на которой они работают. JFrame регистрируется как окно в OS и таким образом получает многие из знакомых свойств окна операционной системы: минимизация/максимизация, изменение размеров и перемещение. Хотя для целей данного руководства совершенно достаточно считать JFrame палитрой, на которой вы размещаете компоненты. Некоторыми из методов, которые вы можете вызвать с JFrame для изменения его свойств, являются:

  • get/setTitle() : Получить/установить заголовок фрейма.
  • get/setState() : Получить/установить состояние фрейма (минимизировать, максимизировать и т.д.).
  • is/setVisible() : Получить/установить видимость фрейма, другими словами, отображение на экране.
  • get/setLocation() : Получить/установить месторасположение в окне, где фрейм должен появиться.
  • get/setSize() : Получить/установить размер фрейма.
  • add() : Добавить компоненты к фрейму.

Простое приложение

Как и во всех руководствах «Введение в х», у нас есть необходимый элемент — демонстрация HelloWorld. Однако этот пример полезен не только для того, чтобы увидеть, как работает Swing-приложение, но также и для проверки правильности установки системы. Если это простое приложение работает, любое другое тоже будет работать нормально. На приведенном ниже рисунке показан завершенный пример:

Пример HelloWorld

Первым вашим действием является создание класса. Swing-приложение, которое размещает компоненты в JFrame, должно быть подклассом класса JFrame , например так:


Сделав так, вы получаете все свойства JFrame, описанные выше, и, что самое важное, поддержку окна в OS. Следующий шаг — размещение компонентов на экране. В данном примере мы используем схему null. Более подробно о схемах и менеджерах схем вы узнаете позже. Для этого примера числа указывают позицию в JFrame в пикселях:

Теперь, когда компоненты размещены в JFrame, необходимо, чтобы JFrame показался на экране; также нужно сделать ваше приложение запускаемым. Как и во всех Java-приложениях, вы должны добавить метод main, для того чтобы сделать Swing-приложение запускаемым. Внутри этого метода main необходимо создать объект вашего приложения HelloWorld и вызвать setVisible() :

Это все, что нужно для создания приложения!

Дополнительные Swing-виджеты

JComboBox

В этом разделе мы рассмотрим все остальные компоненты в библиотеке Swing: как их использовать, и на что они похожи. Это даст лучшее представление о той мощи, которую дает вам (как UI-разработчику) Swing.

Мы начнем с JComboBox. Комбинированный список является знакомым ниспадающим списком элементов, в котором пользователи могут либо выбрать ноль или один (и только один) элемент из списка. В некоторых версиях комбинированного списка вы можете ввести ваш собственный вариант. Хорошим примером является адресная строка вашего браузера; это комбинированный список, позволяющий ввести собственный вариант. Вот как выглядит JComboBox в Swing:

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

  • addItem() : Добавить элемент к JComboBox.
  • get/setSelectedIndex() : Получить/установить индекс выбранного элемента в JComboBox.
  • get/setSelectedItem() : Получить/установить выбранный объект.
  • removeAllItems() : Удалить все объекты из JComboBox.
  • remoteItem() : Удалить конкретный объект из JComboBox.

JPasswordField

Разновидностью JTextField является JPasswordField, который позволяет скрывать все символы, отображаемые на экране в текстовом поле. В конце концов, что это за пароль, который могут видеть все желающие в то время, когда вы его вводите? Возможно, это вообще не пароль, и в настоящее время, когда ваши персональные данные очень важны, вы должны использовать все возможности по их защите, которые можете получить. Вот как JPasswordField выглядит в Swing:

Дополнительные «защищенные» методы JPasswordField незначительно меняют свое поведение по сравнению с JTextField, для того чтобы вы не смогли прочитать текст:

  • get/setEchoChar() : Получить/установить символ, который появляется в JPasswordField при вводе текста. При получении пароля символ «echo» не возвращается; вместо него возвращается реальный символ.
  • getText() : Вы не должны использовать эту функцию, поскольку она имеет некоторые проблемы с защитой (для интересующихся — String хранится в оперативной памяти, и возможный дамп кучи может открыть пароль).
  • getPassword() : Это корректный метод для получения пароля из JPasswordField, поскольку возвращает массив char[] , содержащий пароль. Для гарантирования нормальной защищенности массив должен быть очищен в 0, для того чтобы он не оставался в оперативной памяти.

JCheckBox/JRadioButton

Компоненты JCheckBox и JRadioButton предоставляют пользователю варианты для выбора, обычно в многовариантном формате. В чем отличия? С практической точки зрения они одинаковы. Их поведение одинаково. Однако в обычной UI-практике они имеют небольшое различие. JRadioButton обычно группируются вместе для предоставления пользователю вопроса с принудительным ответом (ответы взаимоисключающие — может быть только один ответ на вопрос). Поведение JRadioButton заставляет делать именно так. Как только вы выбрали JRadioButton, вы не можете снять его отметку до тех пор, пока не выберете другой вариант из группы. JCheckBox работает иначе. Он позволяет отмечать/снимать отметку с варианта в любое время и выбирать несколько ответов на вопрос.

Вот пример. На вопрос «Вы юноша или девушка?» есть два уникальных варианта ответа: «Юноша» или «Девушка». Пользователь должен выбрать один и не может выбрать оба. С другой стороны, на вопрос «Каково ваше хобби?» можно дать несколько ответов: «Бег», «Сон» или «Чтение».

Классом, который позволяет группировать вместе компоненты JCheckBox или JRadioButton, является класс ButtonGroup . Он позволяет группировать варианты (например, «Юноша» и «Девушка») таким образом, что при выборе одного, с другого отметка автоматически снимается.

Вот как JCheckBox и JRadioButton выглядят в Swing:

Важными методами ButtonGroup, которые стоит запомнить, являются:

  • add() : Добавить JCheckBox или JRadioButton к ButtonGroup.
  • getElements() : Получить все компоненты в ButtonGroup, для того чтобы можно было выполнить итерацию по ним для поиска выбранного.

JMenu/JMenuItem/JMenuBar

Компоненты JMenu, JMenuItem и JMenuBar являются главными строительными блоками для разработки системы меню в вашем JFrame. Основой любой системы меню является JMenuBar. Он простой и скучный, но он необходим, поскольку каждый JMenu и JMenuItem создается с ним. Для присоединения JMenuBar к JFrame используется метод setJMenuBar() . После его закрепления в JFrame вы можете добавлять все меню, подменю и элементы меню, какие хотите.

Различие JMenu/JMenuItem, возможно, очевидно, но фактически оно скрыто внутри и как раз не является очевидным. Если вы посмотрите на иерархию классов, JMenu является подклассом JMenuItem . Однако по внешнему виду они имеют отличие: JMenu используется для содержания других JMenuItem и JMenu; JMenuItem при выборе активизирует действие.

JMenuItem также поддерживает сокращенные клавиатурные команды. Как и большинство приложений, которые вы используете, Swing-приложения позволяют нажать Ctrl+(клавиша) для активизации действия, аналогично нажатию соответствующего элемента меню. Вспомните Ctrl+X и Ctrl+V, которые вы используете для копирования и вставки.

Кроме того, JMenu и JMenuItem поддерживают мнемоники. Вы можете использовать клавишу Alt вместе с буквой, назначенной меню в качестве мнемоники (например, нажатие Alt+F, а затем Alt+x закрывает приложение в Windows).

Вот как выглядят JMenuBar с компонентами JMenu и JMenuItem в Swing:

Важными методами, которые вам нужны в этих классах, являются:

  • JMenuItem и JMenu:
    • get/setAccelerator() : Получить/установить комбинацию Ctrl+клавиша, используемую в качестве клавиатурного сокращения.
    • get/setText() : Получить/установить текст для меню.
    • get/setIcon() : Получить/установить изображение, используемое в меню.
  • Только JMenu:
    • add() : Добавить еще один JMenu или JMenuItem к JMenu (создание вложенного меню).

JSlider

Компонент JSlider используется в приложении для изменения числового значения. Это быстрый и простой способ позволить пользователям визуально получить ответную реакцию не только на их текущий выбор, но увидеть диапазон допустимых значений. Подумайте, вы могли бы предоставить текстовое поле и позволить пользователю ввести значение, но тогда у вас добавляется лишняя работа — вы должны гарантировать, что введенное значение является числом, которое попадает в требуемый диапазон. В качестве примера представьте, что у вас есть финансовый Web-сайт, который запрашивает желаемый процент инвестиций в акции. Вы должны проверять вводимые в текстовое поле значения, для того чтобы гарантировать, что они являются числом и находятся в диапазоне от 0 до 100. Если вы вместо этого используете JSlider, введенные значения гарантировано будут числами в требуемом диапазоне.

В Swing JSlider выглядит следующим образом:

Важными методами в JSlider являются:

  • get/setMinimum() : Получить/установить минимальное значение, которое вы можете выбрать.
  • get/setMaximum() : Получить/установить максимальное значение, которое вы можете выбрать.
  • get/setOrientation() : Получить/установить ориентацию JSlider (вверх/вниз или вправо/влево).
  • get/setValue() : Получить/установить начальное значение JSlider.

JSpinner

Во многом аналогично JSlider вы можете использовать JSpinner, для того чтобы позволить пользователям выбирать целочисленное значение. Одним важным преимуществом является его компактный размер по сравнению с JSlider. Хотя недостатком является то, что вы не можете легко установить его границы.

Однако сравнение этих двух компонентов здесь заканчивается. JSpinner намного более гибок и может быть использован для выбора из любой группы значений. Кроме выбора чисел он может быть использован для выбора дат, имен, цветов, чего-угодно. Это делает JSpinner чрезвычайно мощным, позволяя вам предоставлять компонент, содержащий только предопределенные варианты. В этом он аналогичен JComboBox, хотя их использование не должно быть взаимозаменяемо. Вы должны использовать JSpinner только для логически последовательных вариантов — числа и даты идеально подходят для этого. JComboBox, с другой стороны, более хороший выбор для представления кажущихся случайными вариантов, которые не имеют взаимосвязи между одним вариантом и следующим.

JSpinner выглядит так:

Важными методами являются:

  • get/setValue() : Получить/установить начальное значение JSpinner, которое в базовом случае должно быть целым числом.
  • getNextValue() : Получить следующее значение, которое будет выбрано после нажатия клавиши управления курсором «стрелка вверх».
  • getPreviousValue() : Получить предыдущее значение, которое будет выбрано после нажатия клавиши управления курсором «стрелка вниз».

JToolBar

Компонент JToolBar выступает как палитра для других компонентов (JButtons, JComboBoxes и т.д.), которые совместно образуют панели инструментов, встречающиеся в большинстве приложений. Панели инструментов позволяют программе размещать часто используемые команды в удобном месте и группировать их. Часто, но не всегда, кнопки панели инструментов соответствуют командам в меню. Хотя это и не обязательно, но применение панелей стало общепринятой практикой, и вы тоже должны обращать на них внимание.

JToolBar также предлагает еще одну функцию, которую вы встречали в других панелях инструментов: способность «перемещаться» (то есть, становится отдельным фреймом над главным фреймом).

Ниже показан не перемещающийся JToolBar:

Важным методом JToolBar является is/setFloatable() , который получает/устанавливает свойство: может ли JToolBar перемещаться.

JToolTip

Вы, возможно, встречали JToolTip повсюду, но никогда не знали, как они называются. Они похожи на пластиковые концы ваших шнурков — они повсюду, но вы не знаете правильного названия (они называются aglet (пистон), если кому интересно). JToolTip — это небольшие «пузырьки», которые всплывают тогда, когда вы наводите и держите курсор мышки над чем-нибудь. Они могут быть очень полезны в приложениях, предоставляя подсказки для элементов, детализируя информацию или даже показывая полный текст элемента в сжатых UI. Они активизируются в Swing, если оставить курсор мышки над компонентом на определенное количество времени; обычно они появляются примерно через секунду после остановки мышки и остаются видимыми до тех пор, пока курсор остается над компонентом.

Компоненты JToolTip легко использовать. Метод setToolTip() является методом класса JComponent, то есть, каждый Swing-компонент может иметь всплывающую подсказку, связанную с ним. Хотя JToolTip и сам является Swing-классом, он, фактически, не предоставляет в настоящее время дополнительной функциональности и не должен создаваться. Вы можете обратиться к нему и использовать, вызывая функцию setToolTip() в JComponent.

Вот как выглядит JToolTip:

JOptionPane

Компонент JOptionPane — это своего рода «ускоряющий» класс в Swing. Часто, как UI-разработчик, вы хотели бы предоставить быстрое сообщение для ваших пользователей, говоря им об ошибке или выдавая какую-нибудь другую информацию. Возможно, вы хотели бы быстро получить некоторые данные, например, имя или номер. В Swing класс JOptionPane предоставляет возможность выполнить эти рутинные задачи. Вместо того, чтобы каждый раз изобретать колесо, Swing предоставляет базовый, но полезный класс для предоставления UI-разработчикам способа выдачи и получения простых сообщений.

Вот внешний вид JOptionPane:

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

JTextArea

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

JTextArea в Swing выглядит так:

Важными методами для разрешения переноса строки и переноса слова являются:

  • is/setLineWrap() : Устанавливает, должна ли переноситься строка, если она становится слишком длинной.
  • is/setWrapStyleWord() : Устанавливает, должно ли переноситься слово на следующую строку, если оно слишком длинное.

JScrollPane

Рассматривая приведенный выше пример, предположим, что JTextArea содержит слишком много текста для данного пространства. Что тогда? Если вы думаете, что полосы прокрутки появятся автоматически, то, к сожалению, ошибаетесь. JScrollPane устраняет этот пробел, предоставляя Swing-компонент для обработки всех действий по прокрутке. То есть (хотя потребуются некоторые усилия, чтобы обеспечить панель прокрутки для каждого компонента, который в ней нуждается), если вы добавите ее, она будет управлять всем автоматически, включая скрытие/показ полос прокрутки при необходимости.

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

Этот обновленный пример выглядит следующим образом:

Компонент JScrollPane предоставляет также два компонента JScrollBar, которые он создаст. Эти компоненты JScrollBar содержат методы, которые вы можете использовать для изменения их поведения (их рассмотрение выходят за рамки данного руководства).

Методами, которые вы должны использовать с JScrollPane, являются:

  • getHorizontalScrollBar() : Возвращает горизонтальный компонент JScrollBar.
  • getVerticalScrollBar(): Возвращает вертикальный компонент JScrollBar.
  • get/setHorizontalScrollBarPolicy() : Эта «политика» может принимать одно из следующих значений: Always (всегда), Never (никогда), или As Needed (по необходимости).
  • get/setVerticalScrollBarPolicy() : Аналогично горизонтальной функции.

JList

Компонент JList является полезным компонентом для предоставления пользователю многих вариантов для выбора. Вы можете представлять его как расширение JComboBox. JList предоставляет больше вариантов и добавляет возможность выбора нескольких вариантов. Выбор между JList и JComboBox часто заключается в следующем: если вам требуется возможность множественного выбора или имеется более 15 вариантов (хотя это число не является общим правилом), вы должны всегда выбирать JList.

Вы должны использовать JList совместно с JScrollPane, как было показано выше, поскольку он может предоставлять больше вариантов, чем помещается в видимой области.

JList имеет также модель выбора (также имеющуюся в JTable), которую вы можете установить в различные типы выбора вариантов. Такими типами являются: одиночный выбор (вы можете выбрать только один вариант), одиночный интервал (вы можете выбрать смежные варианты, но в любом их количестве) и множественный интервал (вы можете выбрать любое число вариантов в любых комбинациях).

JList является первым из компонентов, которые я называю «сложными компонентами», и к которым относятся также компоненты JTable и JTree, допускающие большое количество пользовательских настроек, в том числе внешнего вида и способа обработки данных. Поскольку данное руководство предназначено только для рассмотрения основ Swing, я не буду углубляться в эти расширенные функции, но при работе с этими компонентами вы должны помнить — они более сложны, чем рассмотренные до сих пор компоненты.


JList в Swing выглядит так:

Цукерберг рекомендует:  Полезная функция вывод количества памяти в читабельном формате

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

  • get/setSelectedIndex() : Получить/установить выбранную строку списка; в случае со списками с множественным выбором возвращается int[] .
  • get/setSelectionMode() : Как объяснялось ранее, получить/установить режим выбора в одиночный выбор, одиночный интервал или множественный интервал.
  • setListData() : Установить данные для использования в JList.
  • get/setSelectedValue() : Получить выбранный объект (как противоположность выбранному номеру строки).

JTable

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

К сожалению, имеется и недостаток. Он известен также как наиболее сложный Swing-компонент для работы. Многие UI-разработчики мучались при изучении каждой подробности JTable. Я избавлю вас от этого.

Многие концепции JList присутствуют также и в JTable, в том числе, например, множественный выбор интервалов. Но однострочные концепции JList меняются на ячеистую структуру JTable. Это означает, что есть различные способы делать этот выбор в JTable: в виде столбцов, строк или индивидуальных ячеек.

В Swing JTable выглядит так:

Конечно, рассмотрение функциональности JTable выходит за рамки данного руководства.

JTree

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

JTree наследует свою функциональность из концепции дерева с ветвями и листьями. Вы, возможно, знакомы с этой концепцией по работе с Internet Explorer в Windows — вы можете расширить и свернуть ветвь для отображения различных листьев, которые вы можете отметить, или с которых можете снять отметку.

Скорее всего, вы обнаружите, что древо не настолько полезно в приложении, как таблица или список, поэтому в Интернете нет большого количества полезных примеров. Фактически, аналогично JTable, компонент JTree не имеет каких-либо функций для новичков. Если вы решите использовать JTree, то тут же перейдете на средний уровень и должны будете изучить концепции, лежащие в основе его работы. В примере приложения не используется JTree, поэтому, к сожалению, ни руководство для новичков, ни руководство для среднего уровня квалификации не рассматривает детально этот менее популярный компонент.

Однако бывают ситуации, когда дерево логично использовать в качестве UI-компонента. Система файлов и каталогов — это один из примеров, как в Internet Explorer, и JTree является наилучшим компонентом для данных с иерархической структурой, иными словами, когда данные имеют форму дерева.

В Swing компонент JTree выглядит так:

Концепции Swing

Схемы, модели и события

Теперь, когда вы знакомы с большинством (но определенно не со всеми) компонентов, которые можно использовать для создания UI, необходимо что-то с ними делать реальное. Вы не можете просто случайно разместить их на экране и ожидать от них немедленной работы. Вы должны разместить их в определенные места, реагировать на взаимодействие с ними, обновлять их на основе этого взаимодействия и заполнять данными. Необходимо дальнейшее изучение других важных частей UI для заполнения пробелов в ваших знаниях.

Поэтому, давайте рассмотрим:

  • Схемы (layout): Swing содержит множество схем, которые представляют собой классы, управляющие размещением компонентов в приложении и тем, что должно произойти с ними при изменении размеров окна приложения или при удалении или добавлении компонентов.
  • События (event): Вы должны реагировать на нажатия клавиши, нажатия кнопки мыши и на все остальное, что пользователь может сделать в UI. Подумайте о том, что было бы, если бы вы этого не делали — пользователи нажимают на кнопки, а ничего не происходит.
  • Модели (model): Для более продвинутых компонентов (списки, таблицы, деревья) и даже для некоторых более простых, например, JComboBox, модели — это самый эффективный способ работы с данными. Они удаляют большую часть работы по обработке данных из самого компонента (вспомните обсуждение MVC) и предоставляют оболочку для общих объектных классов данных (например, Vector и ArrayList ).

Простые схемы

Как уже говорилось в предыдущем разделе, схема управляет расположением компонентов в приложении. Вашим первым вопросом мог бы быть вопрос «почему я просто не могу указать месторасположение, используя пиксели»? Да, вы можете, но тогда сразу же возникнет проблема при изменении размеров окна, или еще хуже: если пользователь изменит разрешение экрана, или кто-то попробует запустить приложение в другой OS. Менеджеры схем берут все это на себя. Никто не использует одни и те же настройки, поэтому менеджеры схем создают «относительные» схемы, позволяя вам указывать, как должно происходить изменение размеров относительно других размещенных компонентов. Хорошая новость: это легче сделать, чем сказать. Вы просто вызываете setLayout(yourLayout) для настройки менеджера схемы. Последующие вызовы add() добавляют компонент в контейнер и позволяют менеджеру схемы позаботиться об их размещении.

В Swing включено много схем; такое впечатление, что с каждой версией появляется новая, которая служит еще одной цели. Однако некоторые проверенные временем схемы существовали всегда; под словом всегда я понимаю время с появления первой версии языка Java в 1995 году. Такими схемами являются FlowLayout, GridLayout и BorderLayout.

В схеме FlowLayout компоненты располагаются слева направо. Когда пространство заканчивается, происходит переход на новую строку. Это самая простая в использовании схема и наименее мощная:

Схема FlowLayout в работе

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

Схема GridLayout в работе

Схема BorderLayout все еще является очень полезной, несмотря на добавление в Swing новых схем. Даже опытные UI-разработчики часто используют BorderLayout. В ней используются понятия North (север), South (юг), East (восток), West (запад) и Center (центр) для размещения компонентов на экране:

Схема BorderLayout в работе

GridBagLayout

В то время как приведенные выше примеры хороши для простых схем, более продвинутые UI требуют использования более сложного менеджера схемы. Вот здесь нужен GridBagLayout. К сожалению, этот менеджер схем очень запутанный и тяжелый, и каждый, кто работал с ним, согласится с этим. Я тоже не могу не согласиться, но, несмотря на его сложность, он, вероятно, является наилучшим (из всех менеджеров схем, встроенных в Swing) способом создания прекрасно выглядящего UI.

Вот мой первый совет: В новейших версиях Eclipse есть встроенный визуальный построитель, автоматически генерирующий требуемый для GridBagLayout код для каждого экрана. Используйте его! Он сбережет бесчисленные часы бесполезных игр с числами для нормального отображения. Поэтому, имея возможность пуститься в объяснения работы GridBagLayout и настройки его для наилучшего выполнения работы, я, тем не менее, просто посоветую: найдите визуальный редактор и сгенерируйте код. Это сэкономит вам часы.

События

Наконец, мы перешли к наиболее важным элементам Swing — работе с событиями и реакции на взаимодействия с UI. Swing обрабатывает события, используя модель событие/прослушиватель. Эта модель работает путем разрешения некоторым классам регистрироваться на события от компонента. Такой класс называется прослушивателем (listener), поскольку он ждет возникновения событий в компоненте и выполняет действия при их возникновении. Компонент сам знает, как «активировать» событие (то есть, он знает типы взаимодействий, которые может генерировать, и как дать прослушивателям знать, когда происходят эти взаимодействия). Он передает это взаимодействие при помощи событий (event), классов, содержащих информацию о взаимодействии.

Убрав техническую болтовню, рассмотрим некоторые примеры работы событий в Swing. Я начну с простейшего примера — кнопки JButton и вывода сообщений «Hello» на консоль при ее нажатии.

JButton знает, когда на нее нажимают; это обрабатывается внутри компонента, код для обработки этого не нужен. Однако прослушиватель должен зарегистрироваться для получения этого события от JButton, для того чтобы вы могли вывести сообщение «Hello». Класс listener делает это, реализуя интерфейс listener и вызывая метод addActionListener() компонента JButton:

Компонент JList работает аналогично. Когда кто-то выбирает что-то в JList, вы выводите на консоли сообщение о выбранном объекте:

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

Модели

Вы должны знать о Java Collections, наборе Java-классов, обрабатывающих данные. К этим классам относятся ArrayList , HashMap и Set . Большинство приложений использует эти классы повсюду при перемещении данных взад и вперед. Однако при необходимости использования этих данных в UI возникает одно ограничение. UI не знает, как их отображать. Подумайте об этом минутку. Если у вас есть JList и ArrayList некоторых объектов данных (например, объектов Person ), как JList узнает, что отображать? Должен ли он отображать только фамилию, или фамилию и имя вместе?

Вот где нужна концепция модели. Хотя понятие модели относится к более широкой области, в примерах данного руководства я использую понятие UI-модель для описания классов, которые компоненты используют для отображения данных.

Каждый компонент, работающий с коллекцией данных в Swing, использует концепцию модели, и это предпочтительный способ использования и управления данными. Он четко отделяет работу UI от используемых данных (вспомните пример MVC). Модель описывает компоненту, как отображать коллекцию данных. Что я имею в виду под словом «описывает»? Каждый компонент требует немного разного описания:

  • JComboBox требует от своей модели описания текста для отображения в виде вариантов выбора и количества существующих вариантов.
  • JSpinner требует от своей модели описания текста для отображения, а также описания предыдущего и следующего вариантов.
  • JList тоже требует от своей модели описания текста для отображения как вариантов выбора и количества существующих вариантов.
  • JTable нужно намного больше — он требует от своей модели описания количества существующих строк и столбцов, названий столбцов, класса каждого столбца и текста для отображения в каждой ячейке.
  • JTree требует от своей модели описания корневого узла, предков и дочерних элементов для всего дерева.

Вы можете спросить, зачем все это делать? Зачем понадобилось разделять всю эту функциональность? Представьте себе сценарий: у вас есть законченная таблица со многими столбцами данных, и вы используете эту таблицу на различных экранах. Если вы неожиданно решите удалить один из столбцов, что было бы легче, изменить код в каждом единичном экземпляре JTable, использованном вами, или изменить его в одном классе модели, который вы создали для использования с каждым экземпляром JTable? Очевидно, что лучше изменить классы.

Примеры модели

Давайте рассмотрим, как работает модель, на простом примере JComboBox. В предыдущем разделе о JComboBox я показал вам, как добавлять элементы к данным, вызывая setItem() . Хотя это и приемлемо для простых демонстраций, в реальных приложениях так делают не часто. В конце концов, когда имеется 25 вариантов, и они постоянно меняются, неужели вы захотите проходить по ним в цикле, вызывая addItem() 25 раз? Конечно же, нет.

JComboBox имеет метод setModel() , который принимает экземпляр класса ComboBoxModel . Вы должны использовать этот метод вместо метода addItem() для создания данных в JComboBox.

Предположим, что имеется ArrayList с алфавитом в качестве данных («A», «B», «C» и т.д.):

Хорошей стороной использования модели является то, что вы можете повторно использовать ее снова и снова. Например, предположим, что данные в JComboBox должны быть изменены с букв алфавита на числа от 1 до 27. Вы можете выполнить это изменение одной простой строкой, которая использует новый List данных для заполнения JComboBox без использования дополнительного кода:

Модели являются полезными возможностями Swing, поскольку они предоставляют возможность повторного использования кода и намного более легко работают с данными. Как это часто случается в крупномасштабных приложениях, разработчики серверной части создают и извлекают данные и передают их UI-разработчикам. Обязанностью UI-разработчика является работать с данными и отображать их правильно, а модели — инструменты, которые делают это.

Объединение всего этого вместе

Пример приложения

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

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

Это простое по внешнему виду приложение, которое позволит вам увидеть в действии все элементы Swing. Данный пример должен ответить на любые вопросы, которые, возможно, остались у вас от предыдущих разделов. Перед началом работы посмотрим на законченный продукт:

Пример приложения

Шаг 1: Размещение компонентов

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

Шаг 2: Инициализация данных

Приложение не может работать без данных. Давайте подумаем о том, какого рода данные нам нужны для данного приложения. Во-первых, нам необходим список городов для выбора города отправления и прибытия. Затем, нам необходим список рейсов для поиска.

Для этого примера я использую фиктивные данные, потому что главное в приложении — это Swing, а не данные. Все данные создаются в классе DataHandler . Этот класс управляет городами отправления и прибытия, а также обрабатывает операции поиска рейсов и извлечения записей.

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

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

Шаг 3: Управление событиями

Исследуем приложение и рассмотрим действия, которые должны происходить. Во-первых, нужно знать, когда пользователь нажимает кнопку Search, для того чтобы можно было выполнить поиск рейсов. Во-вторых, вы должны знать, когда пользователь выбирает таблицу записей для предотвращения возможных ошибок при попытках пользователя купить билет на рейс тогда, когда не были выбраны записи в таблице. Наконец, необходимо знать, когда пользователь нажимает кнопку Purchase для передачи выбранных данных в класс обработчика данных.

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

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

Затем исследуем, что происходит при нажатии пользователем кнопки Purchase:

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

Наконец, вы должны проверить ошибки и убедиться в том, что никто не пытается купить билет, не выбрав рейса в таблице. Самый простой способ сделать это — запретить компоненты, которые может использовать пользователь для покупки билетов (текстовое поле и кнопку) и разрешать их только после выбора пользователем строки:

Шаг 4: Модели

Далее рассмотрим модели, используемые вами для обработки всех данных, передаваемых в обе стороны в этом приложении. Анализируя приложения и изучив этот демонстрационный пример, вы должны четко увидеть, что вам нужны две модели: модель для JComboBoxes и модель для JTable.

Начнем с простейшей модели JComboBox. Я не буду приводить здесь исходный код, поскольку он аналогичен рассмотренному несколькими разделами ранее (и, фактически, может быть использован для любого из ваших JComboBox). Хотя есть некоторые важные моменты, помните о преимуществах использования моделей. И хотя у вас имеется только один класс модели, вы повторно используете его, создавая два экземпляра класса и предоставляя их каждому из JComboBox. Таким образом, оба экземпляра могут обрабатывать свои собственные данные, хотя вы пишете только один класс для этого. Вот как это делается:

Перейдем к модели для JTable. Эта модель сложнее, чем для JComboBox, и требует более пристального рассмотрения. Начнем с ваших знаний ComboBoxModel и посмотрим, что нужно добавить для JTable. Поскольку JTable содержит те же данные, что и ComboBox, но в нескольких столбцах, вам нужна более полная информация из модели. То есть, кроме знания количества строк данных вы должны знать количество столбцов, названия столбцов и значение конкретной ячейки, а не только самого объекта. Это позволяет вам не только отобразить объект данных, но также отобразить поля объекта данных. В данном примере мы не отображаем объект Flight; вместо этого мы отображаем поля: город отправления, город прибытия, номер рейса и количество доступных билетов. Ниже приведен код, используемый для создания TableModel и для установки этой модели для JTable:

Из за большого объема кода, необходимого для создания TableModel , я не буду приводить его здесь, а перенаправлю вас к исходному коду примера приложения (ссылка для загрузки приведена в разделе «Ресурсы«) для более пристального исследования его работы. Кроме того, это только поверхностно затрагивает TableModel . Как я уже говорил ранее, JTable является самым сложным и трудным компонентом для работы со Swing, и его элементы, в том числе TableModel , не намного более просты.

Шаг 5: Звонки и свистки

В любом приложении пользователи ожидают определенное количество звонков и свистков как дополнительную функциональность, а также как способ предотвратить возникновение ошибок. В данном примере, хотя основная функциональность по поиску рейса и покупке билетов работает, вы не рассматривали возможные ошибки, которые могут возникать. Для защиты от ошибок вы должны добавить сообщение об ошибке при попытках пользователя заказать больше билетов, чем доступно. Как отобразить ошибки? Если вы вспомните раздел о компоненте JOptionPane, Swing имеет готовый компонент для такого рода немедленной реакции.

Рассмотрим условие ошибки, а также то, что активизирует сообщение об ошибке:


Теперь позаботимся о сообщении об ошибке. Вспомните JOptionPane и его обильное количество настроек. Выделим настройки, которые хотим иметь в сообщении об ошибке, перед тем, как решим, какой тип JOptionPane создавать. Это должно быть сообщение об ошибке, а не информационное сообщение. Используйте простой заголовок, например «Error». Детальное сообщение состоит из того, что выдала исключительная ситуация. И, наконец, ошибка возникла по вине пользователей, поэтому должно быть достаточно наличия кнопок OK и Cancel.

Вот код для создания такой JOptionPane:

И вот, как это все выглядит:

Пример сообщения об ошибке

И напоследок…

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

Особенно обратите внимание на то, как я использовал модели для облегчения процесса работы с данными. Используя довольно простой код, вы можете обрабатывать любой тип данных, которые могут получать как JTable, так и JComboBox. Кроме того, обратите внимание на взаимосвязи событие/прослушиватель — как взаимодействующие компоненты принимают участие во взаимоотношении событие/прослушиватель, т.е. на то, как заинтересованные стороны (особенно другие компоненты) должны регистрироваться на эти события.

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

Резюме

Резюме

Данное руководство познакомило вас с библиотекой Swing и с концепциями, которые вы должны знать для создания базового, функционального пользовательского интерфейса. Во время изучения данного руководства:

  • Вы познакомились со Swing-компонентами. Вы должны уметь распознавать компоненты, когда видите их на экране, и применять их в ваших собственных приложениях, используя основные функции этих компонентов. Кроме того, вы должны иметь общее представление о том, когда нужно использовать компоненты (например, когда использовать JCheckBox, а когда JRadioButton).
  • Вы узнали, как разместить эти компоненты на экране при помощи менеджеров схем. Менеджеры схем существуют со времен появления Java, и, к сожалению, наиболее мощный менеджер схем наиболее тяжело использовать. Однако визуальные редакторы (например, редактор в Eclipse) делают процесс размещения чрезвычайно легким, создавая весь код схемы автоматически.
  • Вы узнали о модели событий, о том, как Swing использует модель событие/прослушиватель во всех своих компонентах для разрешения одному компоненту принимать пользовательское взаимодействие с приложением и передавать это взаимодействие в другие классы. Эти другие классы регистрируются на события от компонента и выполняют соответствующее действие при получении события. Модель событие/прослушиватель используется во всей библиотеке Swing, и вы должны глубоко изучить ее для лучшей работы с каждым Swing-компонентом.
  • Вы узнали о моделях данных и о том, как они соответствуют архитектуре MVC в Swing. Модели позволяют компонентам отображать данные без каких-либо знаний о самих данных. Они также позволяют вам повторно использовать модели, разрешая нескольким компонентам, отображающим похожие данные, использовать одну и ту же модель, устраняя необходимость создания оригинальной модели для каждого экземпляра компонента. В крупномасштабных приложениях модели служат «транслятором» между серверными объектами данных и клиентскими Swing-компонентами.

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

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

Дальнейшие действия

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

  • Более продвинутые функциональные возможности JTable, включая свойства таблицы, более тонкое управление TableModel, TableRenderer для изменения внешнего вида JTable и сортировку столбцов таблицы.
  • Потоки и их соответствие Swing. Поскольку пользователи не принимают интерфейс, который блокируется при выборке из базы данных, Swing должен использовать отдельные потоки для длительных операций.
  • Создание пользовательских компонентов. Если вы чувствуете ограниченность того, что может предложить вам Swing, я расскажу, как создавать компоненты, которые могут выглядеть и вести себя не так, как встроенные Swing-компоненты.
  • Пользовательское поведение и внешний вид. Я расскажу о том, как полностью изменить внешний вид приложения при помощи двух методов: первый создает новое пользовательское поведение и внешний вид UI, а второй использует библиотеку Synth, поддерживающую интерфейс с легко измененяемым внешним видом (skinnable).

Ресурсы

Научиться

  • Оригинал руководства «Introduction to Swing«.
  • Загрузите swing1.jar, который содержит исходный код приложений «Hello World» и системы резервирования билетов.
  • Не пропустите продолжение данного руководства «Swing для среднего уровня«, основанное на материале данного руководства.
  • Руководство Sun по Swing — хорошее дополнение к данному руководству, охватывающее не рассмотренные здесь компоненты.
  • В Swing Javadoc приводится описание всех возможных функций Swing-компонентов.
  • Web-страница JavaDesktop предлагает новые приемы работы в Swing.
  • Дискуссионный форум по программированию клиентских Java-приложений является еще одним хорошим местом для получения справочной информации по Swing.
  • На сайте developerWorks опубликовано множество статей по Swing:
    • В серии статей Джона Жуковски (John Zukowski) «Магия с Merlin» и «Укрощение тигра» постоянно упоминается Swing и смежные темы.
    • Майкл Абернети (Michael Abernethy) написал более продвинутые статьи по Swing, в том числе «Легкая разработка в Swing при помощи свободно распространяемой среды TableModel» (developerWorks, октябрь 2004), «Станьте мастером с IFrame» (developerWorks, март 2004) и «Продвинутый Synth» (developerWorks, февраль 2005).
  • Зона developerWorks открытого исходного кода имеет целый раздел, посвященный Eclipse-разработке.
  • В зоне developerWorks по Java-технологии вы найдете статьи по любому аспекту Java-программирования, в том числе и по рассмотренной в данном руководстве теме.

Получить продукты и технологии

  • eclipse.org — это официальный ресурс платформы разработки Eclipse. Здесь вы найдете файлы для загрузки, статьи и дискуссионные форумы, которые помогут вам профессионально использовать Eclipse.

Об авторе

Майкл Абернети (Michael Abernethy) в настоящее время работает руководителем группы тестировщиков в IBM WebSphere System Management, расположенной в Austin, TX. До этого он работал разработчиком Swing UI.

Изменить расположение кнопок в Java/Swing

Я пытаюсь добавить кнопку в свой графический интерфейс, используя Gridbagconstraints в LayoutManager в Java. Расположение кнопки всегда находится в центре, независимо от координат.

Выход

Вопрос

Как определить координаты кнопок и поместить их в нужное место?

Вам нужно установить weightx и weighty ибо GridBagConstraints объекта.

Если вы не укажете хотя бы одно ненулевое значение для weightx или weighty, все компоненты скомпонованы в центре их контейнера. Это связано с тем, что когда вес равен 0.0 (по умолчанию), GridBagLayout помещает дополнительное пространство между сеткой ячеек и краями контейнера. Как использовать GridBagLayout

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

Лекция 09. GUI.Swing¶

Введение¶

Пакеты¶

Для построения GUI в Java используются следующие основные пакеты:

  1. Awt — появился в первых версиях, используется мало.
  2. Swing — самый популярный пакет.
  3. JavaFX — современный и перспективный.

Awt пример¶

Swing пример¶

JavaFX пример¶

Сравнение¶

Недостатки awt:

  • ‘’тяжеловесные’’ компоненты (системозависимые);
  • невозможность изменения внешнего вида.

Недостатки swing:

  • ограничения в изменении внешнего вида;
  • ориентация на desktop-приложения.

Swing¶

Простое приложение¶

Рассмотрим простейшее Swing-приложение.

Контейнеры верхнего уровня¶

Все swing-программы должны включать в себя Контейнеры первого уровня

  • JFrame
  • JApplet
  • JWindow
  • JDialog

Эти контейнеры находятся на вершине иерархии контейнеров и

содержат в себе другие, ‘’легковесные’’ контейнеры и элементы

управления. Разница между JFrame и JWindow может быть показана

на следующем примере:

Окно JFrame, в отличие от JWindow

Как получить все элементы внутри JFrame?

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

Я хочу сделать что-то подобное и получить все элементы, не требуя всех имен панелей. Как я это делаю. Код ниже не получает все элементы.

java swing jcomponent

4 ответа

35 Решение aioobe [2011-06-27 19:16:00]

Вы можете написать рекурсивный метод и рекурсию в каждом контейнере:

Этот сайт содержит пример кода:

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

14 toto2 [2011-06-27 19:21:00]

Посмотрите на документ для JFrame. Все, что вы помещаете в JFrame , фактически помещается в корневую панель, содержащуюся в кадре.

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

0 flakes [2020-12-09 19:25:00]

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

и его можно использовать следующим образом:

Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих