Графический интерфейс программы на java. Улучшаем интерфейс Java-приложения


Библиотека Swing

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

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

В Java есть три библиотеки визуальных компонентов для создания графического интерфейса пользователя. Самая ранняя из них называется AWT. Считается, что при ее проектировании был допущен ряд недочетов, вследствие которых с ней довольно сложно работать. Библиотека Swing разработана на базе AWT и заменяет большинство ее компонентов своими, спроектированными более тщательно и удобно. Третья, самая новая библиотека, называется SWT.

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

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

Окно JFrame

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

В библиотеке Swing описан класс JFrame , представляющий собой окно с рамкой и строкой заголовка (с кнопками «Свернуть», «Во весь экран» и «Закрыть»). Оно может изменять размеры и перемещаться по экрану.

об окнах Swing

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

Конструктор JFrame() без параметров создает пустое окно. Конструктор JFrame(String title) создает пустое окно с заголовком title .

Чтобы написать простейшую программу, выводящую на экран пустое окно, нам потребуется еще три метода:

setSize(int width, int height) - устанавливает размеры окна. Если не задать размеры, окно будет иметь нулевую высоту независимо от того, что в нем находится и пользователю после запуска придется растягивать окно вручную. Размеры окна включают не только «рабочую» область, но и границы и строку заголовка.

setDefaultCloseOperation(int operation) - позволяет указать действие, которое необходимо выполнить, когда пользователь закрывает окно нажатием на крестик. Обычно в программе есть одно или несколько окон при закрытии которых программа прекращает работу. Для того, чтобы запрограммировать это поведение, следует в качестве параметра operation передать константу EXIT_ON_CLOSE , описанную в классе JFrame .

setVisible(boolean visible) - когда окно создается, оно по умолчанию невидимо. Чтобы отобразить окно на экране, вызывается данный метод с параметром true . Если вызвать его с параметром false , окно снова станет невидимым.

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

import javax.swing.*; public class MyClass { public static void main (String args) { JFrame myWindow = new JFrame("Пробное окно" ); myWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); myWindow.setSize(400, 300); myWindow.setVisible(true ); } }

Обратите внимание, для работы с большинством классов библиотеки Swing понадобится импортировать пакет java.swing.*

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

В файле SimpleWindow.java:

public class SimpleWindow extends JFrame { SimpleWindow(){ super ("Пробное окно" ); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(250, 100); } }

В файле Program.java:

public class Program { public static void main (String args) { JFrame myWindow = new SimpleWindow(); myWindow.setVisible(true ); } }

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

Обратите внимание, что метод setVisible() не вызывается в классе SimpleWindow , что вполне логично: за тем, где какая кнопка расположена и какие размеры оно должно иметь, следит само окно, а вот принимать решение о том, какое окно в какой момент выводится на экран - прерогатива управляющего класса программы.

Панель содержимого

Напрямую в окне элементы управления не размещаются. Для этого служит панель содержимого, занимающая все пространство окна* . Обратиться к этой панели можно методом getContentPane() класса JFrame . С помощью метода add(Component component) можно добавить на нее любой элемент управления.

В примерах этого занятия мы будем использовать только один элемент управления - кнопку (не вдаваясь в подробности ее устройства). Кнопка описывается классом JButton и создается конструктором с параметром типа String - надписью.

Добавим кнопку в панель содержимого нашего окна командами:

JButton newButton = new JButton(); getContentPane().add(newButton);

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

Класс Container (контейнер)

Элементы, которые содержат другие элементы, называются контейнерами. Все они являются потомками класса Container и наследуют от него ряд полезных методов:

add(Component component) - добавляет в контейнер элемент component ;

remove(Component component) - удаляет из контейнера элемент component ;

removeAll() - удаляет все элементы контейнера;

getComponentCount() - возвращает число элементов контейнера.

Кроме перечисленных в классе Container определено около двух десятков методов для управления набором компонентов, содержащихся в контейнере. Как видно, они похожи на методы класса-коллекции. Это неудивительно, ведь по сути контейнер и является коллекцией, но коллекцией особого рода - визуальной. Кроме хранения элементов контейнер занимается их пространственным расположением и прорисовкой. В частности, он имеет метод getComponentAt(int x, int y) , возвращающий компонент, в который попадает точка с заданными координатами (координаты отсчитываются от левого верхнего угла компонента) и ряд других. Мы не будем подробно рассматривать абстрактный контейнер, а сразу перейдем к его наиболее часто используемому потомку - классу JPanel .

Класс JPanel (панель)

Панель JPanel - это элемент управления, представляющий собой прямоугольное пространство, на котором можно размещать другие элементы. Элементы добавляются и удаляются методами, унаследованными от класса Container .

В примере с кнопкой мы наблюдали, как добавленная на панель содержимого кнопка заняла все ее пространство. Это происходит не всегда. На самом деле у каждой панели есть так называемый менеджер размещения , который определяет стратегию взаимного расположения элементов, добавляемых на панель. Его можно изменить методом setLayout(LayoutManager manager) . Но чтобы передать в этот метод нужный параметр, необходимо знать, какими бывают менеджеры.

Менеджер последовательного размещения FlowLayout

Самый простой менеджер размещения - FlowLayout . Он размещает добавляемые на панель компоненты строго по очереди, строка за строкой, в зависимости от размеров панели. Как только очередной элемент не помещается в текущей строке, он переносится на следующую. Лучше всего пронаблюдать это на примере. Изменим конструктор класса SimpleWindow следующим образом:

SimpleWindow(){ super ("Пробное окно" ); setDefaultCloseOperation(EXIT_ON_CLOSE); JPanel panel = new JPanel(); panel.setLayout(new FlowLayout()); panel.add(new JButton("Кнопка" )); panel.add(new JButton("+" )); panel.add(new JButton("-" )); panel.add(new JButton("Кнопка с длинной надписью" )); setContentPane(panel); setSize(250, 100); }

Менеджеры расположения описаны в пакете java.awt. Не забывайте импортировать нужные классы.

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

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

FlowLayout newLayout = new FlowLayout(); panel.setLayout(newLayout);

Мы используем одну:

Panel.setLayout(new FlowLayout());

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

о взаимоотношениях панели и ее менеджера

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

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

JPanel panel = new JPanel(); panel.setLayout(new FlowLayout());

можно написать:

JPanel panel = new JPanel(new FlowLayout());

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

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

Метод setContentPane(JPanel panel) позволяет заменить панель содержимого окна.

Менеджер граничного размещения BorderLayout

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

При добавлении элемента на панель с менеджером размещения BorderLayout , необходимо дополнительно указывать в методе add() , какая из областей имеется в виду. Для этого служат строки с названиями сторон света: "North" , "South" , "East" , "West" и "Center" . Но вместо них рекомендуется использовать константы, определенные в классе BorderLayout: NORTH , SOUTH , EAST , WEST и CENTER (поскольку в строке можно допустить ошибку и не заметить этого, а при попытке написать неправильно имя константы компилятор выдаст предупреждение). Если же использовать метод add() как обычно, с одним параметром, элемент будет добавлен в центр.

Панель содержимого имеет именно такое расположение, именно поэтому кнопка и занимала все окно целиком (она была добавлена в центральную область). Чтобы пронаблюдать эффект BorderLayout , добавим кнопки во все пять областей:

SimpleWindow(){ super("Пробное окно" ); setDefaultCloseOperation(EXIT_ON_CLOSE); getContentPane().add(new JButton("Кнопка" ), BorderLayout.NORTH); getContentPane().add(new JButton("+" ), BorderLayout.EAST); getContentPane().add(new JButton("-" ), BorderLayout.WEST); getContentPane().add(new JButton("Кнопка с длинной надписью" ), BorderLayout.SOUTH); getContentPane().add(new JButton("В ЦЕНТР!" )); setSize(250, 100); }

Эффект будет хорошо наблюдаться, если изменять размеры окна.

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

Менеджер табличного размещения GridLayout

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

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

SimpleWindow(){ super("Пробное окно" ); setDefaultCloseOperation(EXIT_ON_CLOSE); JPanel panel = new JPanel(); panel.setLayout(new GridLayout(2,3,5,10)); panel.add(new JButton("Кнопка" )); panel.add(new JButton("+" )); panel.add(new JButton("-" )); panel.add(new JButton("Кнопка с длинной надписью" )); panel.add(new JButton("еще кнопка" )); setContentPane(panel); setSize(250, 100); }

Менеджер блочного размещения BoxLayout и класс Box

Менеджер BoxLayout размещает элементы на панели в строку или в столбец.

Обычно для работы с этим менеджером используют вспомогательный класс Box , представляющий собой панель, для которой уже настроено блочное размещение. Создается такая панель не конструктором, а одним из двух статических методов, определенных в классе Box: createHorizontalBox() и createVerticalBox() .

Элементы, добавленные на панель с блочным размещением, выстраиваются один за другим. Расстояние между элементами по умолчанию нулевое. Однако вместо компонента можно добавить невидимую «распорку», единственная задача которой - раздвигать соседние элементы, обеспечивая между ними заданное расстояние. Горизонтальная распорка создается статическим методом createHorizontalStrut(int width) , а вертикальная - методом createVerticalStrut(int height) . Оба метода определены в классе Box , а целочисленный параметр в каждом из них определяет размер распорки.

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

Понять особенности работы этого менеджера лучше на наглядном примере. Мы расположим четыре кнопки вертикально, поставив между двумя центральными «пружину», а между остальными - распорки в 10 пикселов.

SimpleWindow(){ super("Пробное окно" ); setDefaultCloseOperation(EXIT_ON_CLOSE); Box box = Box.createVerticalBox(); box.add(new JButton("Кнопка" )); box.add(Box.createVerticalStrut(10)); box.add(new JButton("+" )); box.add(Box.createVerticalGlue()); box.add(new JButton("-" )); box.add(Box.createVerticalStrut(10)); box.add(new JButton("Кнопка с длинной надписью" )); setContentPane(box); setSize(250, 100); }

Особенности выравнивания элементов

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

Однако при разработке окна программы может понадобиться, чтобы какие-то элементы были выровнены иначе, например, по правому краю или по центру. Для того, чтобы установить выравнивание любого визуального компонента (например, кнопки или панели), используются методы setAlignmentX(float alignment) - выравнивание по горизонтали и setAlignmentY(float alignment) - выравнивание по вертикали. В качестве параметра проще всего использовать константы, определенные в классе JComponent . Для выравнивания по горизонтали служат константы LEFT_ALIGNMENT (по левому краю), RIGHT_ALIGNMENT (по правому краю) и CENTER_ALIGNMENT (по центру). Для выравнивания по вертикали - BOTTOM_ALIGNMENT (по нижнему краю), TOP_ALIGNMENT (по верхнему краю) и CENTER_ALIGNMENT (по центру).

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

Box.add(new JButton("-" ));

На три других:

JButton rightButton = new JButton("-" ); rightButton.setAlignmentX(JComponent.RIGHT_ALIGNMENT); box.add(rightButton);

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

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

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

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

о выравнивании элементов

Параметр выравнивания на самом деле представляет собой вещественное число в диапазоне от 0 до 1. Он показывает, какая часть компонента окажется слева от линии выравнивания, т.е. в каких пропорциях компонент будет «разрезан». Константы LEFT_ALIGNMENT и TOP_ALIGNMENT на самом деле равны 0, RIGHT_ALIGNMENT и BOTTOM_ALIGNMENT равны 1, а CENTER_ALIGHNMENT - 0.5. Можно подставлять эти числа напрямую (хотя использование констант значительно повышает наглядность!), а можно выбрать любое другое число от 0 до 1 и настроить совершенно произвольное выравнивание.

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

Ручное размещение элементов

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

Координаты элемента можно задать одним из следующих методов:

setLocation(int x, int y) ,

setLocation(Point point)

Эти методы работают аналогично, устанавливая левый верхний угол элемента в точку с заданными координатами. Разница в способе задания точки. Можно представить точку двумя целыми числами, а можно объектом класса Point . Класс Point по сути представляет собой ту же пару чисел, его конструктор имеет вид Point(int x, int y) . Получить доступ к отдельной координате можно методами getX() и getY() .

Можно задаться вопросом: зачем использовать класс Point , если можно просто передать пару чисел? Но дело в том, что многие полезные методы возвращают результат - координаты некоторой точки - в виде объекта этого класса. Например, метод getLocation() , возвращающий координаты элемента. Предположим, нам нужно поместить элемент b в точности в то место, которое занимает элемент a . Этого легко добиться одной строкой:

B.setLocation(a.getLocation());

Размер элемента задается одним из двух методов:

setSize(int width, int height) ,

setSize(Dimension size)

Эти методы работают одинаково - разница, как и в прошлый раз, в способе передачи параметра. Класс Dimension , аналогично классу Point , просто хранит два числа, имеет конструктор с двумя параметрами: Dimension(int width, int height) и позволяет получить доступ к своим составляющим - ширине и высоте - с помощью простых методов getWidth() и getHeigth() . Для того, чтобы получить текущий размер элемента, можно воспользоваться методом getSize() , возвращающего объект класса Dimension . Элемент b можно сделать точно такого же размера, как элемент a , выполнив команду:

B.setSize(a.getSize());

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

SimpleWindow(){ super("Пробное окно" ); setDefaultCloseOperation(EXIT_ON_CLOSE); JPanel panel = new JPanel(); panel.setLayout(null ); JButton button = new JButton("Кнопка" ); button.setSize(80, 30); button.setLocation(20,20); panel.add(button); button = new JButton("Кнопка с длинной надписью" ); button.setSize(120, 40); button.setLocation(70,50); panel.add(button); setContentPane(panel); setSize(250, 150); }

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

Автоматическое определение размеров компонентов

Если у панели есть любой менеджер размещения, она игнорирует явно заданные размеры и координаты всех своих элементов. В этом легко убедиться, заменив в предыдущем примере команду panel.setLayout(null ) на panel.setLayout(new FlowLayout()) . Менеджер размещения сам определяет координаты и размеры всех элементов.

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

Мы также отмечали, что в некоторых случаях компоненты стараются заполнить все доступное им пространство. Например, всю центральную область в случае менеджера BorderLayout или всю ячейку в менеджере GridLayout . А в панели с менеджером FlowLayout , напротив, элементы никогда не пытаются выйти за определенные границы. Рассмотрим, что это за границы.

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

getMinimumSize() ,

getPreferredSize() ,

getMaximumSize() .

Методы возвращают результат типа Dimension . Они запрограммированы в соответствующем классе. Например, у кнопки минимальный размер - нулевой, максимальный размер не ограничен, а предпочтительный зависит от надписи на кнопке (вычисляется как размер текста надписи плюс размеры полей).

Менеджер FlowLayout всегда устанавливает предпочтительные размеры элементов. Менеджер BorderLayout устанавливает предпочтительную ширину правого и левого, а также предпочтительную высоту верхнего и нижнего. Остальные размеры подгоняются под доступное пространство панели. Менеджер GridLayout пытается подогнать размеры всех элементов под размер ячеек. Менеджер BoxLayout ориентируется на предпочтительные размеры.

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

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

setMinimumSize(Dimension size) ,

setPreferredSize(Dimension size) ,

setMaximumSize(Dimension size) .

Чаще всего используется простой прием, когда элементу «не рекомендуется» увеличиваться или уменьшаться относительно своих предпочтительных размеров. Это легко сделать командой:

Element.setMinimumSize(element.getPreferredSize());

«Упаковка» окна

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

Безусловно, наиболее подходящим будет вариант, при котором все элементы окна имеют предпочтительные размеры или близкие к ним* .

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

SetSize(250, 100);

на команду

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

Упражнение

Как уже отмечалось, элементом панели может быть другая панель. Создайте панель с тремя кнопками и менеджером размещения FlowLayout и панель с двумя кнопками и менеджером размещения BoxLayout (горизонтальным). Разместите обе панели в главном окне (не изменяя менеджера размещения у панели содержимого): одну в центр, а другую вдоль любой стороны окна.

Рамки

Когда панели служат не просто для размещения элементов в соответствии с алгоритмом некоторого менеджера, а для визуального отделения их друг от друга, они оформляются с помощью рамок.

Рамка панели устанавливается методом setBorder(Border border) . Параметром метода выступает рамка - объект класса Border . Это абстрактный класс, поэтому для создания рамки используются его наследники:

EmptyBorder - пустая рамка, позволяет создать отступы вокруг панели. Размеры отступов задаются в конструкторе четырьмя целыми числами.

TitledBorder - рамка с заголовком. Простейший конструктор имеет один параметр типа String (текст заголовка). Заголовок может размещаться вдоль любой стороны рамки, иметь различные начертания.

EtchedBorder - рамка с тиснением. Может быть вогнутой или выпуклой.

BevelBorder - объемная рамка (выпуклая или вогнутая). Можно настроить цвета, требуемые для получения объемных эффектов.

SoftBevelBorder - то же самое, что BevelBorder, но позволяет дополнительно скруглить углы.

LineBorder - простая рамка, нарисованная сплошной линией. Можно выбирать цвет и толщину линии, скруглить углы.

MatteBorder - рамка из повторяющегося рисунка.

CompoundBorder - объединяет две рамки, передаваемые в качестве параметров конструктору в одну новую рамку.

Все перечисленные классы описаны в пакете javax.swing.border.

Рассмотрим пример. В этом примере мы создадим шесть панелей с различными рамками и разместим их в виде таблицы. Чтобы не описывать шесть раз процедуру создания новой панели, вынесем ее в отдельный метод:

Private JPanel createPanel(Border border, String text) { JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(new JButton(text)); panel.setBorder(new CompoundBorder(new EmptyBorder(12,12,12,12), border)); return panel; }

Метод createPanel() создает панель с кнопкой во весь свой размер. В качестве параметра передается надпись на кнопке и рамка, которую необходимо добавить к панели. Рамка добавляется не напрямую, а путем композиции с пустой рамкой. Этот прием часто используется, чтобы рамка не прилипала к краю панели.

Теперь шесть раз воспользуемся этим методом в конструкторе окна программы.

SimpleWindow(){ super("Пробное окно" ); setDefaultCloseOperation(EXIT_ON_CLOSE); JPanel panel = new JPanel(); panel.setLayout(new GridLayout(2,3,5,10)); panel.add(createPanel(new TitledBorder("Рамка с заголовком" ), "TitledBorder" )); panel.add(createPanel(new EtchedBorder(), "EtchedBorder" )); panel.add(createPanel(new BevelBorder(BevelBorder.LOWERED), "BevelBorder" )); panel.add(createPanel(new SoftBevelBorder(BevelBorder.RAISED), "SoftBevelBorder" )); panel.add(createPanel(new LineBorder(Color.ORANGE, 4), "LineBorder" )); panel.add(createPanel(new MatteBorder(new ImageIcon("1.gif" )), "MatteBorder" )); setContentPane(panel); pack(); }

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

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

Класс ImageIcon описывает графическое изображение. Параметр его конструктора - это путь к файлу, из которого изображение может быть загружено. В примере используется относительное имя файла «1.gif». Чтобы объект ImageIcon был успешно создан, файл с таким именем должен быть помещен в папку проекта.

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

Я уже привел небольшой пример написания своего UI, но есть и другие варианты кастомизации интерфейса. Каждый отдельный J-компонент производит свою Lightweight-отрисовку при помощи метода paint(), который можно легко переопределить и изменить. Напрямую (не всегда, но чаще всего) его лучше не использовать (не буду вдаваться в подробности, так как это целая тема для отдельного топика). Для следующего примера используем метод paintComponent(). Рассмотрим как его можно применить поближе…

Начну с примера - текстовое поле с визуальным фидбэком при отсутствии содержимого:

JTextField field = new JTextField()
{
private boolean lostFocusOnce = false ;
private boolean incorrect = false ;

{
// Слушатели для обновления состояния проверки
addFocusListener (new FocusAdapter()
{
public void focusLost (FocusEvent e)
{
lostFocusOnce = true ;

repaint ();
}
});
addCaretListener (new CaretListener()
{
public void caretUpdate (CaretEvent e)
{
if (lostFocusOnce)
{
incorrect = getText ().trim ().equals ("" );
}
}
});
}

protected void paintComponent (Graphics g)
{
super.paintComponent (g);

// Расширенная отрисовка при некорректности данных
if (incorrect)
{
Graphics2D g2d = (Graphics2D) g;

// Включаем антиалиасинг для гладкой отрисовки
g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);

// Получаем отступы внутри поля
Insets insets;
if (getBorder () == null )
{
insets = new Insets (2, 2, 2, 2);
}
else
{
insets = getBorder ().getBorderInsets (this );
}

// Создаем фигуру в виде подчеркивания текста
GeneralPath gp = new GeneralPath (GeneralPath.WIND_EVEN_ODD);
gp.moveTo (insets.left, getHeight () - insets.bottom);
for (int i = 0; i < getWidth () - insets.right - insets.left; i += 3)
{
gp.lineTo (insets.left + i,
getHeight () - insets.bottom - ((i / 3) % 2 == 1 ? 2: 0));
}

// Отрисовываем её красным цветом
g2d.setPaint (Color.RED);
g2d.draw (gp);
}
}
};

Наличие содержимого перепроверяется при печати и потере фокуса полем. Переключившись на другой компонент мы увидим как отрисовывается наше дополнение к JTextField"у:

Полный код примера можно взять .

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

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

За основу берутся 8 изображений 16х16 - 4 состояния фона чекбокса и 4 состояния галки (5 на самом деле, но 5ое мы добавим програмно):

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

public static List BG_STATES = new ArrayList ();
public static List CHECK_STATES = new ArrayList ();

static
{
// Иконки состояния фона
for (int i = 1; i <= 4; i++)
{
BG_STATES.add (new ImageIcon (
MyCheckBox.class .getResource ("icons/states/" + i + ".png" )));
}

// Дополнительное "пустое" состояние выделения

new BufferedImage (16, 16, BufferedImage.TYPE_INT_ARGB)));

// Состояния выделения
for (int i = 1; i <= 4; i++)
{
CHECK_STATES.add (new ImageIcon (
MyCheckBox.class .getResource ("icons/states/c" + i + ".png" )));
}
}

private Map iconsCache = new HashMap ();

private synchronized void updateIcon ()
{
// Обновляем иконку чекбокса
final String key = bgIcon + "," + checkIcon;
if (iconsCache.containsKey (key))
{
// Необходимая иконка уже была ранее использована
setIcon (iconsCache.get (key));
}
else
{
// Создаем новую иконку совмещающую в себе фон и состояние поверх
BufferedImage b = new BufferedImage (BG_STATES.get (0).getIconWidth (),
BG_STATES.get (0).getIconHeight (), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = b.createGraphics ();
g2d.drawImage (BG_STATES.get (bgIcon).getImage (), 0, 0,
BG_STATES.get (bgIcon).getImageObserver ());
g2d.drawImage (CHECK_STATES.get (checkIcon).getImage (), 0, 0,
CHECK_STATES.get (checkIcon).getImageObserver ());
g2d.dispose ();

ImageIcon icon = new ImageIcon (b);
iconsCache.put (key, icon);
setIcon (icon);
}
}

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

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

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

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


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

Итак, думаю достаточно разговоров о графике - о ней более подробно я расскажу в будущих топиках, а сейчас приведу немного интересного материала, который я наработал за достаточно долгое время «общения» со Swing и Graphics2D.

DnD и GlassPane

Думаю первое - Вам всем более чем известно, как и проблемы с ним связанные. Насчет второго - вероятно Вы вскользь слышали о GlassPane или может даже видели это старинное изображение (которое до сих пор актуально, между прочем) об устройстве различных слоев стандартных фреймов. Что же тут такого и зачем я вспомнил об этом? И тем более, как связаны DnD и GlassPane спросите Вы? Вот именно о том, как их связать и что из этого может выйти я и хочу рассказать в этой главе.

Чтож, начнем по порядку - что мы знаем о DnD?
У некоторых Swing-компонентов есть готовые реализации для драга (JTree и JList к примеру) - для других можно достаточно легко дописать свою. Чтобы не бросаться словами на ветер - приведу небольшой пример DnD стринга из лэйбла:

JLabel label = new JLabel ("Небольшой текст для DnD" );
label.setTransferHandler (new TransferHandler()
{
public int getSourceActions (JComponent c)
{
return TransferHandler.COPY;
}

public boolean canImport (TransferSupport support)
{
return false ;
}

protected Transferable createTransferable (JComponent c)
{
return new StringSelection (((JLabel) c).getText ());
}
});
label.addMouseListener (new MouseAdapter()
{
public void mousePressed (MouseEvent e)
{
if (SwingUtilities.isLeftMouseButton (e))
{
JComponent c = (JComponent) e.getSource ();
TransferHandler handler = c.getTransferHandler ();
handler.exportAsDrag (c, e, TransferHandler.COPY);
}
}
});

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

Но что делать, если необходимо отследить последовательность действий пользователя при самом перетаскивании?
Для этого есть отдельная возможность повесить слушатель:

DragSourceAdapter dsa = new DragSourceAdapter()
{
public void dragEnter (DragSourceDragEvent dsde)
{
// При входе драга в область какого-либо компонента
}

public void dragExit (DragSourceEvent dse)
{
// При выходе драга в область какого-либо компонента
}

public void dropActionChanged (DragSourceDragEvent dsde)
{
// При смене действия драга
}

public void dragOver (DragSourceDragEvent dsde)
{
// При возможности корректного завершения драга
}

public void dragMouseMoved (DragSourceDragEvent dsde)
{
// При передвижении драга
}

public void dragDropEnd (DragSourceDropEvent dsde)
{
// При завершении или отмене драга
}
};
DragSource.getDefaultDragSource ().addDragSourceListener (dsa);
DragSource.getDefaultDragSource ().addDragSourceMotionListener (dsa);

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

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

Кстати говоря, есть достаточно интересная библиотека на эту тему, заодно предоставляющая дополнительный скролл-функционал и несколько других вкусностей - JXLayer (офф сайт) (описание #1 описание #2 описание #3). К сожалению проекты хостящиеся на сайте java сейчас находтся не в лучшем состоянии, поэтому приходится ссылаться на отдельные ресурсы.

Итак теперь объединим всё что я уже описал в данной главе и сделаем, наконец, что-то полноценное. К примеру - отображение драга панели с компонентами внутри окна:

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

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

AWTUtilities

Так как уже достаточно давно в JDK6 включили некоторые будущие нововведения из 7ки, не могу обойти их стороной, так как с их помощью возможно много чего сделать приложив при этом гораздо меньшие усилия.
Итак, нас интересует несколько методов из AWTUtilities:
  1. AWTUtilities.setWindowShape(Window, Shape) - позволяет установить любому окну определенную форму (будь то круг или хитрый полигон). Для корректной установки формы окно не должно быть декорировано нативным стилем (setUndecorated(true)).
  2. AWTUtilities.setWindowOpacity (Window, float) – позволяет задать прозрачность окна от 0 (полностью прозрачно) до 1 (непрозрачно). Окно при этом может быть декорировано нативным стилем.
  3. AWTUtilities.setWindowOpaque (Window, boolean) – позволяет полностью спрятать отображение фона и оформления окна, но при этом любой размещенный на нем компонент будет виден. Для корректной установки данного параметра окно также как и в п.1 не должно быть декорировано нативным стилем.

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

Если переходить к конкретике - setWindowShape на деле я никогда не использую, так как задаваемая окну форма строго обрезается по краю и не очень приятно выглядит. На помощь приходит setWindowOpaque - спрятав оформление и фон окна можно с помощью контейнера с кастомным отрисованным фоном создавать абсолютно любые окна. Приведу небольшой пример использования (в нем также есть также использованы некоторые приемы из предыдущих глав поста):

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

Единственная неприятная мелочь в использовании AWTUtilities – нестабильная работа на Linux-системах. Т.е. Не везде и не всегда корректно отрабатывает прозрачность окон. Не уверен, проблемы ли это текущей JDK или же ОС.

Создание своих интерактивных компонентов

Я уже поверхностно рассказал о том, как создавать компоненты, UI и некоторые «навороты» для интерфейса своего приложения, но что же делать, если нам нужно добавить функциональную часть к компоненту или создать свой совершенно новый компонент с некоей функциональностью и стилизацией? Стилизовать стандартные компоненты и делать из них отдельные части нового компонента достаточно долгое и нудное занятие, тем более что при малейшем изменении в одном из компонентов вся схема может поехать. Именно в таких случаях стоит сделать свой компонент «с нуля».

Итак, за основу лучше лучше всего взять JComponent и используя paint-методы отрисовать его содержимое. Фактически JСomponent сам по себе - чистый холст с некоторыми зашитыми улучшениями для отрисовками и готовыми стандартными методами setEnabled/setFont/setForeground/setBackground и т.п. Как использовать (и использовать ли их) решать Вам. Все, что Вы будете добавлять в методы отрисовки станет частью компонента и будет отображаться при добавлении его в какой-либо контейнер.

Кстати, небольшое отступление, раз уж зашла речь о контейнерах, - любой наследник и сам JComponent являются контейнерами, т.е. могут содержать в себе другие компоненты, которые будет расположены в зависимости от установленного компоненту лэйаута. Что же творится с отрисовкой чайлд-компонентов, лежащих в данном и как она связана с отрисовкой данного компонента? Ранее я не вдавался подробно в то, как устроены и связаны paint-методы Jcomponent"а, теперь же подробно опишу это…

Фактически, paint() метод содержит в себе вызовы 3ех отдельных методов - paintComponent, paintBorder и paintChildren. Конечно же дополнительно он обрабатывает некоторые «особенные» случаи отрисовки как, например печать или перерисовку отдельной области. Эти три метода всегда вызываются в показанной на изображении выше последовательности. Таким образом сперва идет отрисовка самого компонента, затем поверх рисуется бордер и далее вызывается отрисовка чайлд-компонентов, которые в свою очередь также вызывают свой paint() метод и т.д. Естественно есть еще и различные оптимизации, предотвращающие лишние отрисовки, но об этом подробнее я напишу потом.

Компонент отрисован, но статичен и представляет собой лишь изображение. Нам необходимо обработать возможность управления им мышью и различными хоткеями.
Для этого, во-первых, необходимо добавить соответствующие слушатели (MouseListener/MouseMotionListener/KeyListener) к самому компоненту и обрабатывать отдельные действия.

Чтобы не объяснять все на пальцах, приведу пример компонента, позволяющего визуально ресайзить переданный ему ImageIcon:

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

При создании данного компонента я бы выделил несколько важных моментов:

  1. Определяемся с функционалом и внешним видом компонента - в данном случае это область с размещенным на ней изображением, бордером вокруг изображения и 4мя ресайзерами по углам. Каждый из ресайзеров позволяет менять размер изображения. Также есть возможность передвигать изображение по области, «схватив» его за центр.
  2. Определяем все необходимые для работы компонента параметры - в данном случае это само изображение и его «опорные» точки (верхний левый и правый нижний углы). Также есть ряд переменных, которые понадобятся при реализации ресайза и драга изображения.
  3. Накидываем заготовку для компонента (желательно отдельный класс, если Вы собираетесь его использовать более раза) - в данном случае создаем класс ImageResizeComponent, определяем все необходимые для отрисовки параметры, переопределяем метод paintComponent() и отрисовываем содержимое. Также переопределяем метод getPreferredSize(), чтобы компонент сам мог определить свой «желаемый» размер.
  4. Реализовываем функциональную часть компонента - в данном случае нам будет достаточно своего MouseAdapter"а для реализации ресайза и передвижения. При нажатии мыши в области проверяем координаты и сверяем имх с координатами углов и самого изображения - если нажатие произошло в районе некого угла - запоминаем его и при драге изменяем его координату, ежели нажатие пришлось на изображение - запоминаем начальные его координаты и при перетаскивании меняем их. И наконец, последний штрих - в mouseMoved() меняем курсор в зависимости от контрола под мышью.
Ничего сложного, правда? С реализацией «кнопочных» частей других компонентов всё еще проще - достаточно проверять, что нажатие пришлось в область кнопки. Параллельно с отслеживанием событий можно также визуально менять отображение компонента (как сделано в данном примере на ресайзерах). В общем сделать можно всё, на что хватит фантазии.

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

Важно помнить

Есть несколько вещей, которые стоит при любой работе с итерфейсными элементами в Swing – приведу их в этой отдельной небольшой главе.
  1. Любые операции вызова перерисовки/обновления компонентов должны производится внутри Event Dispatch потока для избежания визуальных «подвисаний» интерфейса Вашего приложения. Выполнить любой код в Event Dispatch потоке достаточно легко:
    SwingUtilities.invokeLater (new Runnable()
    {
    public void run ()
    {
    // Здесь располагаете исполняемый код
    }
    });
    Важно также помнить, что события, вызываемые из различных listener"ов стандартных компонентов (ActionListener/MouseListener и пр.) исходно вызываются из этого потока и не требуют дополнительной обработки.
  2. Из любых paint-методов ни в коем случае нельзя влиять на интерфейс, так как это может привести к зацикливаниям или дедлокам в приложении.
  3. Представьте ситуацию - из метода paint Вы изменяете состояние кнопки, скажем, через setEnabled(enabled). Вызов этого метода заставляет компонент перерисоваться и заново вызвать метод paint и мы опять попадаем на этот злополучный метод. Отрисовка кнопки зациклиться и интерфейс приложения будет попросту висеть в ожидании завершения отрисовки (или как минимум съест добрую часть процессора). Это самый простой вариант подобной ошибки.
  4. Не стоит производить тяжёлые вычисления внутри Event Dispatch потока. Вы можете произвести эти вычисления в отдельном обычном потоке и затем только вызвать обновление через SwingUtilities.invokeLater().
  5. Не стоит, также, производить тяжёлые вычисления внутри методов отрисовки. По возможности стоит выносить их отдельно и кэшировать/запоминать. Это позволит ускорить отрисовку компонентов, улучшить общую отзывчиввость приложения и снизить нагрузку на Event Dispatch поток.
  6. Для обвноления внешнего вида компонентов используйте метод repaint() (или же repaint(Rectangle) – если Вам известна точная область для обновления), сам метод repaint необходимо исполнять в Event Dispatch потоке. Для обновления же расположения элементов в лэйауте используйте метод revalidate() (его нет необходимости исполнять в Event Dispatch потоке). Метод updateUI() может помочь в некоторых случаях для полного обновления элемента (например смене данных таблицы), но его нужно использовать аккуратно, так как он также отменит установленный Вами UI и возьмет UI предоставляемый текущим LaF"ом.
  7. Полная установка LaF всему приложению отменит любые ваши вручную заданные в ходе работы UI компонентов и установит поверх них те UI, которые предлагает устанавливаемый LaF. Поэтому лучше производить установку LaF при загрузке приложения до открытия каких-либо окон и показа визуальных элементов.
Следовние этим простым пунктам позволит Вам не беспокоиться о возникновении непредвиденных «тормозов» или дедлоков в интерфейсе приложения.
Думаю, что этот список можно дополнить еще несколькими пунктами, но они уже будут весьма специфичны/необязательны.

Итоги

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

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

Все примеры статьи в едином «флаконе». Из начального окна можно выбрать желаемый пример:

  • interface
  • dnd
  • customization
  • интерфейс
  • кастомизация
  • Добавить метки

    What is Swing?

    Java Swing is a lightweight Graphical User Interface (GUI) toolkit that includes a rich set of widgets. It includes package lets you make GUI components for your Java applications, and It is platform independent.

    The Swing library is built on top of the Java Abstract Widget Toolkit (AWT ), an older, platform dependent GUI toolkit. You can use the Java GUI components like button, textbox, etc. from the library and do not have to create the components from scratch.

    In this tutorial, you will learn-

    Java Swing class Hierarchy Diagram

    All components in swing are JComponent which can be added to container classes.

    What is a container class?

    Container classes are classes that can have other components on it. So for creating a GUI, we need at least one container object. There are 3 types of containers.

    1. Panel : It is a pure container and is not a window in itself. The sole purpose of a Panel is to organize the components on to a window.
    2. Frame : It is a fully functioning window with its title and icons.
    3. Dialog : It can be thought of like a pop-up window that pops out when a message has to be displayed. It is not a fully functioning window like the Frame.

    Java GUI Example

    Example : To learn to design GUI in Java
    Step 1) Copy the following code into an editor

    Import javax.swing.*; class gui{ public static void main(String args){ JFrame frame = new JFrame("My First GUI"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300,300); JButton button = new JButton("Press"); frame.getContentPane().add(button); // Adds Button to content pane of frame frame.setVisible(true); } }

    Step 2) Save, Compile, and Run the code.
    Step 3) Now let"s Add a Button to our frame. Copy following code into an editor

    Import javax.swing.*; class gui{ public static void main(String args){ JFrame frame = new JFrame("My First GUI"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300,300); JButton button1 = new JButton("Press"); frame.getContentPane().add(button1); frame.setVisible(true); } }

    Step 4) Execute the code. You will get a big button

    Step 5) How about adding two buttons? Copy the following code into an editor.

    Import javax.swing.*; class gui{ public static void main(String args){ JFrame frame = new JFrame("My First GUI"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300,300); JButton button1 = new JButton("Button 1"); JButton button2 = new JButton("Button 2"); frame.getContentPane().add(button1); frame.getContentPane().add(button2); frame.setVisible(true); } }

    Step 6) Save , Compile , and Run the program.
    Step 7) Unexpected output =? Buttons are getting overlapped.

    Java Layout Manger

    The Layout manager is used to layout (or arrange) the GUI java components inside a container.There are many layout managers, but the most frequently used are-

    Java BorderLayout

    A BorderLayout places components in up to five areas: top, bottom, left, right, and center. It is the default layout manager for every java JFrame

    Java FlowLayout

    FlowLayout is the default layout manager for every JPanel . It simply lays out components in a single row one after the other.

    Java GridBagLayout

    It is the more sophisticated of all layouts. It aligns components by placing them within a grid of cells, allowing components to span more than one cell.

    Step 8) How about creating a chat frame like below?

    Try to code yourself before looking at the program below.

    //Usually you will require both swing and awt packages // even if you are working with just swings. import javax.swing.*; import java.awt.*; class gui { public static void main(String args) { //Creating the Frame JFrame frame = new JFrame("Chat Frame"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); //Creating the MenuBar and adding components JMenuBar mb = new JMenuBar(); JMenu m1 = new JMenu("FILE"); JMenu m2 = new JMenu("Help"); mb.add(m1); mb.add(m2); JMenuItem m11 = new JMenuItem("Open"); JMenuItem m22 = new JMenuItem("Save as"); m1.add(m11); m1.add(m22); //Creating the panel at bottom and adding components JPanel panel = new JPanel(); // the panel is not visible in output JLabel label = new JLabel("Enter Text"); JTextField tf = new JTextField(10); // accepts upto 10 characters JButton send = new JButton("Send"); JButton reset = new JButton("Reset"); panel.add(label); // Components Added using Flow Layout panel.add(label); // Components Added using Flow Layout panel.add(tf); panel.add(send); panel.add(reset); // Text Area at the Center JTextArea ta = new JTextArea(); //Adding Components to the frame. frame.getContentPane().add(BorderLayout.SOUTH, panel); frame.getContentPane().add(BorderLayout.NORTH, mb); frame.getContentPane().add(BorderLayout.CENTER, ta); frame.setVisible(true); } }

    У любого разработчика начинается такое время, когда создавать консольные приложения уже не весело, а изучить что-то новое очень хочется.
    Тогда в Java есть несколько путей, для нескольких ОС. В зависимости от тех ОС которые вы используете или окружений которые в них установлены.

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

    Для начала стоило бы уделить внимание библиотеке GTK+

    Она доступна не только для Явы, но и многих других языков: C++,C и т.д.

    Написание Desktop"ных приложений с помощью GTK следует использовать если приложение рассчитано для работы под средой GNOME.

    Сама GTK+ считается одной из самых легких для освоения библиотек под Jav"у.

    Основа работы с библиотекой:

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

    import org.gnome.gdk.Event ;
    import org.gnome.gtk.Gtk ;
    import org.gnome.gtk.Widget ;
    import org.gnome.gtk.Window ;
    import org.gnome.gtk.WindowPosition ;

    Если вышла ошибка, то скорее всего у вас просто не установлена библиотека.

    Если юзаете систему типа Debian или Ubuntu, то вам поможет простая команда из терминала:
    apt-get install libjava-gnome-java

    После этого проблем быть не должно.

    public class DesktopExample extends Window {

    public DesktopExample() {

    SetTitle("Пример окна на GTK и Гноме" ) ; // Название приложения

    Connect(new Window .DeleteEvent () {
    public boolean onDeleteEvent(Widget source, Event event) {
    Gtk.mainQuit () ;
    return false ;
    }
    } ) ;

    SetDefaultSize(250 , 150 ) ; // Размер
    setPosition(WindowPosition.CENTER ) ; //Позиция при запуске, в данном случае центр
    show() ;
    }


    Gtk.init (args) ;
    new DesktopExample() ;
    Gtk.main () ;
    }
    }

    В конечном итоге получаем пустое окошко.

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

    Вот пример окна на AWT:

    // Подключаем всякие фичи, хотя swing тут лишний, это я в своем приложении прикручивал
    // Это просто обрезок кода
    import java.awt.EventQueue ;
    import javax.swing.* ;
    import java.awt.* ;
    import java.awt.event.* ;

    import javax.swing.JFrame ;

    public class Main {

    private JFrame frame;

    /**
    * Запуск приложения, что-то типо первоначальной настройки, создание окна и типа того.
    */
    public static void main(String args) {

    EventQueue .invokeLater (new Runnable () {
    public void run() {
    try {
    Main window = new Main() ;
    window.frame .setVisible (true ) ;
    } catch (Exception e) {
    e.printStackTrace () ;
    }

    }
    } ) ;
    }

    /**
    * Вызываем саму инициализацю
    */
    public Main() {

    Initialize() ;
    }

    /**
    * Инициализация окна
    */
    private void initialize() {
    frame = new JFrame ("Чистое окно сгенерированное через Eclipse" ) ; // Делаем новое окно с названием
    frame.setBounds (100 , 100 , 450 , 300 ) ;
    frame.setSize (800 , 800 ) ; // Размеры
    frame.setDefaultCloseOperation (JFrame .EXIT_ON_CLOSE ) ;

    }

    А вот нам пример окна на SWT:

    //Подключаем всякие фичи для веселой жизни
    import org.eclipse.swt.SWT ;
    import org.eclipse.swt.widgets.Display ;
    import org.eclipse.swt.widgets.Shell ;
    import org.eclipse.swt.widgets.Text ;

    public class ExampleApp {

    public static void main (String args) {
    Display display = new Display () ;
    Shell shell = new Shell(display) ;

    Shell.pack () ; //Подготовка (первичная)
    shell.open () ; //Подготавливаем
    while (! shell.isDisposed () ) { //Запускаем
    if (! display.readAndDispatch () ) display.sleep () ; //Спим до поры, до времени
    }
    display.dispose () ;
    }
    }

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

    Вот пример окна со swing"ом:

    import javax.swing.JFrame ;
    import javax.swing.SwingUtilities ;

    public class Example extends JFrame {

    public Example() {
    setTitle("Пример приложения со Свингой" ) ;
    setSize(300 , 200 ) ;
    setLocationRelativeTo(null ) ;
    setDefaultCloseOperation(EXIT_ON_CLOSE) ;
    }

    public static void main(String args) {
    SwingUtilities .invokeLater (new Runnable () {
    public void run() {
    Example ex = new Example() ;
    ex.setVisible (true ) ;
    }
    } ) ;
    }
    }

    Это был основной список графических сред на Java. Почти все представленные здесь «штуки» мультиплатформенны
    и работают на любом компьютере - Win, Linux, Mac.

    Теги: java , java библиотеки, графические интерфейсы java

    Экранной формой называется область, которая видна на экране в виде окна с различными элементами - кнопками, текстом, выпадающими списками и т.п. А сами эти элементы называются компонентами.

    Среды, позволяющие в процессе разработки приложения в интерактивном режиме размещать на формы компоненты и задавать их параметры, называются RAD-средами. RAD расшифровывается как Rapid Application Development - быстрая разработка приложений.

    В NetBeans и других современных средах разработки такой процесс основан на объектной модели компонентов, поэтому он называется Объектно-Ориентированным Дизайном (OOD – Object-Oriented Design).

    NetBeans является RAD-средой и позволяет быстро и удобно создавать приложения с развитым графическим пользовательским интерфейсом (GUI). Хотя языковые конструкции Java, позволяющие это делать, не очень просты, на начальном этапе работы с экранными формами и их элементами нет необходимости вникать в эти тонкости. Достаточно знать основные принципы работы с такими проектами.

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

    Во-вторых, такой интерфейс при решении какой-либо задачи позволяет лучше сформулировать, какие параметры надо вводить, какие действия и в какой последовательности выполнять, и что в конце концов получается. И отобразить всё это на экране: вводимым параметрам будут соответствовать пункты ввода текста, действиям – кнопки и пункты меню, результатам – пункты вывода текста.

    Пример открытия проекта с существующим исходным кодом.

    В NetBeans 5.0 имелся хороший пример GUI-приложения, однако в NetBeans 5.5 он отсутствует. Поэтому для дальнейшей работы следует скопировать аналогичный пример с сайта автора или сайта, на котором выложен данный учебный курс. Пример называется JavaApplicationGUI_example.

    Сначала следует распаковать zip-архив, и извлечь находящуюся в нём папку с файлами проекта в папку с вашими проектами (например, C:\Documents and Settings\User). Затем запустить среду NetBeans, если она не была запущена, и закрыть имеющиеся открытые проекты, чтобы они не мешали. После чего выбрать в меню File/Open Project, либо или на панели инструментов иконку с открывающейся фиолетовой папочкой, либо нажать комбинацию клавиш ++O. В открывшемся диалоге выбрать папку JavaApplicationGUI_example (лучше в неё не заходить, а просто установить выделение на эту папку), после чего нажать кнопку Open Project Folder.

    При этом, если не снимать галочку “Open as Main Project”, проект автоматически становится главным.

    В окне редактора исходного кода появится следующий текст:

    * GUI_application.java

    package java_gui_example;

    * @author Вадим Монахов

    public class GUI_application extends javax.swing.JFrame {

    * Creates new form GUI_application

    public GUI_application() {

    initComponents();

    /** This method is called from within the constructor to

    * initialize the form.

    * WARNING: Do NOT modify this code. The content of this method is

    * always regenerated by the Form Editor.

    private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt)

    * @param args the command line arguments

    public static void main(String args) {

    java.awt.EventQueue.invokeLater(new Runnable() {

    public void run() {

    new GUI_application().setVisible(true);

    // Variables declaration - do not modify

    private javax.swing.JMenuItem aboutMenuItem;

    private javax.swing.JMenuItem contentsMenuItem;

    private javax.swing.JMenuItem copyMenuItem;

    private javax.swing.JMenuItem cutMenuItem;

    private javax.swing.JMenuItem deleteMenuItem;

    private javax.swing.JMenu editMenu;

    private javax.swing.JMenuItem exitMenuItem;

    private javax.swing.JMenu fileMenu;

    private javax.swing.JMenu helpMenu;

    private javax.swing.JMenuBar menuBar;

    private javax.swing.JMenuItem openMenuItem;

    private javax.swing.JMenuItem pasteMenuItem;

    private javax.swing.JMenuItem saveAsMenuItem;

    private javax.swing.JMenuItem saveMenuItem;

    // End of variables declaration

    Поясним некоторые его части. Указание пакета java_gui_example, в котором будет располагаться код класса приложения, нам уже знакомо. Декларация самого класса GUI_application в данном случае несколько сложнее, чем раньше:

    public class GUI_application extends javax.swing.JFrame

    Она означает, что задаётся общедоступный класс GUI_application, который является наследником класса JFrame, заданного в пакете swing, вложенном в пакет javax. Слово extends переводится как “расширяет” (класс-наследник всегда расширяет возможности класса-прародителя).

    Общедоступный конструктор GUI_application()создаёт объект приложения и инициализирует все его компоненты, методом initComponents(), автоматически генерируемом средой разработки и скрываемом в исходном коде узлом +Generated Code.

    Развернув узел, можно увидеть реализацию этого метода, но изменить код нельзя. Мы не будем останавливаться на том, что в нём делается.

    private void exitMenuItemActionPerformed

    Он будет обсуждаться чуть позже. Метод

    public static void main(String args)

    нам уже знаком – это главный метод приложения. Он является методом класса нашего приложения и автоматически выполняется Java-машиной при запуске приложения. В данном примере метод создаёт экранную форму приложения и делает её видимой. Для того, чтобы понять, как это делается, потребуется изучить довольно много материала в рамках данного курса.

    Запущенное приложение. Приложение с раскрытым меню.

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

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

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

    Редактор экранных форм

    Нажмём закладку Design (“дизайн”) в левой верхней части редактора исходного кода. При этом мы переключимся из режима редактирования исходного кода (активна закладка Source – “исходный код”) в режим редактирования экранной формы, как это показано на рисунке.

    Редактирование экранной формы.

    Вместо исходного кода показывается внешний вид экранной формы и находящиеся на ней компоненты. Справа от окна, в котором показывается экранная форма в режиме редактирования, расположены окна Palette (“палитра”) палитры компонентов и окно Properties (“свойства”) показа и редактирования свойств текущего компонента.

    Свойство – это поле данных, которое после изменения значения может проделать какое-либо действие. Например, при изменении значения ширины компонента отрисовать на экране компонент с новой шириной. “Обычное” поле данных на такое не способно. Таким образом, свойство – это “умное поле данных”.

    Палитра компонентов предназначена для выбора типа компонента, который нужен программисту для размещения на экранной форме. Например, добавим на нашу форму компонент типа JButton (сокращение от Java Button – “кнопка Java”). Для этого щёлкнем мышью по пункту JButton на палитре и передвинем мышь в нужное место экранной формы. При попадании мыши в область экранной формы на ней появляется кнопка стандартного размера, которая передвигается вместе с мышью. Щелчок в нужном месте формы приводит к тому, что кнопка остаётся в этом месте. Вокруг неё показываются рамка и маленькие квадратики, обозначающие, что наш компонент является выделенным. Для него осуществляется показ и редактирование свойств в окне Properties.

    Кроме того, от выделенного компонента исходят линии, к которым идет привязка для задания положения компонента на форме.

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

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

    Введём на кнопке надпись “OK” – используем эту кнопку для выхода из программы.

    Редактирование свойств компонента

    Размер компонента задаётся мышью путём хватания за рамку и расширения или сужения по соответствующим направлениям. Установка на новое место – перетаскиванием компонента мышью.

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

    В правой части каждой ячейки имеется кнопка с надписью “…” – в современных операционных системах принято добавлять три точки в названии пунктов меню и кнопок, после нажатия на которые открывается диалоговое окно. В данном случае раскрывается окно специализированного редактора соответствующего свойства, если он существует.

    Если требуется просматривать и редактировать большое количество свойств компонента, бывает удобнее щёлкнуть правой кнопкой мыши по нужному компоненту и в появившемся всплывающем меню выбрать пункт “Properties”. В этом случае откроется отдельное окно редактирования свойств компонента. Можно держать открытыми одновременно произвольное количество таких окон.

    Булевские свойства в колонке значений свойств показываются в виде кнопок выбора checkbox – квадратиков с возможностью установки галочки внутри. Если галочки нет, значение свойства false, если есть – true.

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

    Название свойства Что оно задаёт
    background Цвет фона
    componentPopupMenu Позволяет назначать всплывающее меню, появляющееся по нажатию правой кнопкой мыши в области компонента.
    font Фонт, которым делается надпись на компоненте.
    foreground Цвет фонта, которым делается надпись на компоненте.
    icon Картинка, которая рисуется на компоненте рядом с текстом.
    text Текст (надпись) на компоненте.
    toolTipText Всплывающая подсказка, появляющаяся через некоторое время при наведении курсора мыши на компонент.
    border Тип рамки вокруг компонента.
    borderPainted Рисуется ли рамка вокруг компонента.
    contentAreaFilled Имеется ли заполнение цветом внутренней области компонента (для кнопок оно создаёт эффект трёхмерности, без заполнения кнопка выглядит плоской).
    defaultCapable Способна ли кнопка быть “кнопкой по умолчанию”: при нажатии автоматически происходит нажатие “кнопки по умолчанию” (такая кнопка на экранной форме должна быть одна).
    enabled Доступен ли компонент. По умолчанию все создаваемые на форме компоненты доступны. Недоступные компоненты рисуются более блеклыми красками.

    В качестве примера добавим всплывающую подсказку для нашей кнопки: введём текст “Эта кнопка предназначена для выхода из программы” в поле, соответствующее свойству toolTipText. К сожалению, подсказка может быть только однострочной – символы перевода на новую строку при выводе подсказки игнорируются, даже если они заданы в строке программным путём.

    Наконец, зададим действие, которое будет выполняться при нажатии на кнопку – обработчик события (event handler) нажатия на кнопку. Для этого сначала выделим кнопку, после чего щёлкнем по ней правой кнопкой мыши, и в появившемся всплывающем меню выберем пункт Events/Action/actionPerformed.

    Назначение обработчика события

    Events означает “События”, Action – “Действие”, actionPerformed – “выполненное действие”.

    После этого произойдёт автоматический переход в редактор исходного кода, и там появится заготовка обработчика события:

    // TODO add your handling code here:

    Аналогичный результат можно получить и более быстрым способом – после того, как мы выделим кнопку в окне редактирования формы (Design), в окне Navigator показывается и выделяется имя этой кнопки. Двойной щелчок по этому имени в окне навигатора приводит к созданию заготовки обработчика события.

    Рядом с обработчиком jButton1ActionPerformed будет расположен уже имеющийся обработчик события, срабатывающий при нажатии на пункт меню “Выход”:

    private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {

    Заменим в нашем обработчике события строку с комментарием на код, вызывающий выход из программы:

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {

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

    Часто встречающийся случай – показ сообщения при наступлении какого-либо события, например – нажатия на кнопку. Этом случае вызывают панель с сообщением:

    javax.swing.JOptionPane.showMessageDialog(null,"Меня нажали");

    Если классы пакета javax.swing импортированы, префикс javax.swing при вызове не нужен.

    Внешний вид приложения

    На этапе редактирования приложения внешний вид его компонентов соответствует платформе. Однако после запуска он становится совсем другим, поскольку по умолчанию все приложения Java показываются в платформо-независимом виде.:

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

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

    Для того, чтобы показать приложение в платформо-ориентированном виде (то есть в том виде, который использует компоненты и настройки операционной системы), требуется изменить код конструктора приложения, вставив перед вызовом метода initComponents задание типа пользовательского интерфейса (User’s Interface, сокращённо UI):

    import javax.swing.*;

    import java.awt.*;

    public GUI_application() {

    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

    }catch(Exception e){};

    initComponents();

    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

    Dimension frameSize = getSize();

    setLocation(new Point((screenSize.width-frameSize.width)/2,

    (screenSize.height-frameSize.width)/2)

    Внешний вид запущенного приложения с платформо-ориентированным пользовательским интерфейсом в операционной системе Windows ® XP

    Код, следующий после вызова initComponents(), предназначен для установки окна приложения в центр экрана.

    Имеется возможность задания ещё одного платформо-независимого вида приложения – в стиле Motiff, используемого в операционной системе Solaris ® . Для установки такого вида вместо вызова

    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()

    Следует написать

    UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");

    Внешний вид запущенного приложения с платформо-независимым пользовательским интерфейсом в стиле Motiff

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

    Ведение проектов

    Для того, чтобы не запутаться в разных проектах и их версиях, особенно с учётом того, что учебные проекты бывает необходимо часто переносить с одного компьютера на другой, следует серьёзно отнестись к ведению проектов. Автором в результате многолетней практики работы с разными языками и средами программирования выработана следующая система (откорректированная в применении к среде NetBeans):

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

    · При создании нового проекта среда разработки предлагает ввести имя папки, где его хранить - следует указать имя папки архива. Кроме того, предлагается ввести имя проекта. Это имя будет использовано средой NetBeans для создания папки проекта, так и для названия вашего приложения. Для того, чтобы облегчить работу с вашим приложением в разных странах, рекомендуется делать это название англоязычным. В папке проекта среда разработки автоматически создаст систему вложенных папок проекта и все его файлы. Структура папок проектов NetBeans была описана ранее.

    · Если берётся проект с существующим исходным кодом, его папка копируется в папку нашего архива либо вручную, либо выбором соответствующей последовательности действий в мастере создания проектов NetBeans.

    · При получении сколько-нибудь работоспособной версии проекта следует делать его архивную копию. Для этого в открытом проекте в окне “Projects” достаточно щелкнуть правой кнопкой мыши по имени проекта, и в появившемся всплывающем меню выбрать пункт “Copy Project”. Откроется диалоговая форма, в которой предлагается автоматически образованное имя копии – к первоначальному имени проекта добавляется подчёркивание и номер копии. Для первой копии это _1, для второй _2, и так далее. Причём головная папка архива по умолчанию остаётся той же, что и у первоначального проекта. Что очень удобно, поскольку даёт возможность создавать копию всего тремя щелчками мышки без набора чего-либо с клавиатуры.

    Создание рабочей копии проекта

    Скопированный проект автоматически возникает в окне “Projects”, но не становится главным. То есть вы продолжаете работать с прежним проектом, и все его открытые окна сохраняются. Можно сразу закрыть новый проект – правой кнопкой мыши щёлкнуть по его имени, и в появившемся всплывающем меню выбрать пункт “Close Project”.

    Для чего нужна такая система ведения проектов? Дело в том, что у начинающих программистов имеется обыкновение разрушать результаты собственного труда. Они развивают проект, не сохраняя архивов. Доводят его до почти работающего состояния, после чего ещё немного усовершенствуют, затем ещё – и всё перестаёт работать. А так как они вконец запутываются, восстановить работающую версию уже нет возможности. И им нечего предъявить преподавателю или начальнику!

    Поэтому следует приучиться копировать в архив все промежуточные версии проекта, более работоспособные, чем уже сохранённые в архив. В реальных проектах трудно запомнить все изменения, сделанные в конкретной версии, и, что важнее, все взаимосвязи, вызвавшие эти изменения. Поэтому даже опытным программистам время от времени приходится констатировать: “Ничего не получается!” И восстанавливать версию, в которой ещё не было тех нововведений, которые привели к путанице. Кроме того, часто бывает, что новая версия в каких-то ситуациях работает неправильно. И приходится возвращаться на десятки версий назад в поисках той, где не было таких “глюков”. А затем внимательно сравнивать работу двух версий, выясняя причину неправильной работы более поздней версии. Или убеждаться, что все предыдущие версии также работали неправильно, просто ошибку не замечали.

  • Внешняя политика СССР в годы войны. Ленд-лиз. Тегеранская конференция. Ялтинская и Потсдамская конференции 1945 г. Создание ООН.
  • Внешняя политика СССР в годы войны.Ленд-лиз. Тегеранская конференция. Ялтинская и Потсдамская конференция 1945г.Создание ООН.
  • Возобновелние деятельности БСГ. Создание белнац партий и организаций





  • 

    2024 © gtavrl.ru.