Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Портянкин И. Swing

.pdf
Скачиваний:
141
Добавлен:
07.10.2020
Размер:
4.63 Mб
Скачать

Контейнеры высшего уровня

145

Окно с рамкой JFrame

Окно с рамкой JFrame унаследовано от класса JWindow и представляет собой наиболее часто используемое в приложениях окно «общего назначения». В отличие от окон JWindow окна JFrame обладают рамкой (которая позволяет пользователям легко изменять их размер), заголовком с названием приложения (хотя можно оставлять этот заголовок пустым), иногда системным меню (позволяющим проводить манипуляции с этим окном) и кнопками для управления окном (чаще всего для его закрытия и свертывания). Именно класс JFrame применяется в подавляющем большинстве приложений для размещения компонентов пользовательского интерфейса. Прежде почти всегда при создании окна с рамкой приходилось заботиться о том, чтобы при щелчке пользователем на кнопке закрытия окна приложение заканчивало работу, а необходимость даже для самого скромного приложения писать слушателя событий от окна сильно раздражала. Создатели Swing услышали ропот недовольных программистов и добавили в класс JFrame специальный метод, позволяющий упростить эту операцию. Рассмотрим небольшой пример:

//FrameClosing.java

//Использование окна с рамкой import javax.swing.*;

public class FrameClosing extends JFrame { public FrameClosing() {

super("Заголовок Окна");

//операция при закрытии окна setDefaultCloseOperation(EXIT_ON_CLOSE);

//значок для окна

setIconImage(getToolkit().getImage("icon.gif")); // вывод на экран

setSize(300, 100); setVisible(true);

}

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

new Runnable() {

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

}

}

Это, наверное, самый простой пример в книге. Мы создаем в нем подкласс JFrame, указываем заголовок окна (в конструкторе базового класса, хотя можно было бы использовать и метод setTitle()) и, прежде чем задать размеры окна и вывести его на экран, вызываем метод setDefaultCloseOperation(). Он позволяет указать, какое действие будет произведено в методе предварительной обработки событий processWindowEvent() при закрытии окна. По умолчанию применяется константа HIDE_ON_CLOSE, убирающая с экрана окно при его закрытии. Мы использовали (и на протяжении книги почти всегда будем использовать) значение EXIT_ON_CLOSE, которое указывает, что при закрытии окна необходимо закончить работу приложения. Остальные варианты вы сможете найти в интерактивной документации. В книге имеется множество примеров размещения компонентов вашего пользовательского интерфейса в окне с рамкой, даже в этой главе

146

ГЛАВА 6

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

Из дополнительных возможностей окна с рамкой JFrame можно упомянуть о его способности «прятать» свои «украшения»: рамку и элементы управления окном. Делает это метод setUndecorated(true). После его вызова окно JFrame будет разительно похоже на уже знакомое нам окно без рамки. Пока непонятно, зачем нужно это делать, но вскоре мы найдем применение данному методу (правда, вызывать его нужно до того, как окно появится на экране). Также стоит упомянуть метод setExtendedState(). Он позволяет задать состояние окна, например, свернуть его, но к сожалению работает на разных платформах по-разному, так что совсем полагаться на него не стоит, но всегда можно узнать, какие операции с окном поддерживаются на данной платформе с помощью метода isFrameStateSupported() из класса Toolkit.

События окон

Окна Swing (JWindow, JFrame, а также еще не рассмотренное нами диалоговое окно JDialog) поддерживают три типа событий: первое, представленное слушателем WindowListener, позволяет узнать об изменениях в состоянии окна, второе, со слушателем WindowFocusListener, генерируется системой передачи фокуса ввода и сообщает о получении или потере компонентами окна фокуса ввода, наконец, третье, со слушателем WindowStateListener, сообщает о изменении состояния окна, например сворачивании. Полный список методов данных слушателей вы сможете найти в интерактивной документации Java, в дополнение к этому можно лишь сказать, что от событий окон немного толку. Без сомнения, вы сможете узнать обо всех изменениях в состоянии окна, но при этом у вас почти нет рычагов управления окнами, потому что они полностью принадлежат операционной системе. В результате обычно вы работаете с компонентами Swing,

аокно используете просто как площадку для их размещения.

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

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

//ConfirmClosing.java

//Подтверждение о выходе из приложения import javax.swing.*;

import java.awt.event.*;

public class ConfirmClosing extends JFrame { public ConfirmClosing() {

super("Приложение");

//отключаем операцию закрытия setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

//добавляем слушателя событий от окна addWindowListener(new WindowAdapter() {

Контейнеры высшего уровня

147

@Override

public void windowClosing(WindowEvent e) { // потверждение выхода

int res = JOptionPane.

showConfirmDialog(null, "Действительно выйти?"); if ( res == JOptionPane.YES_OPTION )

System.exit(0);

}

}); // выводим окно на экран

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

}

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

new Runnable() {

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

}

}

МысоздаемнебольшоеокноJFrameидобавляемкнемуслушателясобытийWindowEvent. Чтобы не реализовывать все определенные в интерфейсе WindowListener методы, мы наследуем от адаптера WindowAdapter и переопределяем нужный нам метод windowClosing(). При закрытии окна мы спрашиваем у пользователя, действительно ли он желает закончить работу с приложением, и завершаем работу, если ответ положительный. Кстати, обратите внимание, что нам приходится указывать в методе setDefaultCloseOperation(), что при закрытии окна ничего делать не надо (передавая в него константу DO_NOTHING_ON_ CLOSE), иначе окно скроется с глаз пользователя при любой реакции последнего.

Диалоговое окно JDialog

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

Диалоговые окна в Swing реализуются классом JDialog. Данный класс позволяет создавать обычные и модальные диалоговые окна, поддерживает (так же как и класс JFrame) закрытие окна, а в остальном сходен с другими окнами Swing, потому что унаследован от базового класса окон JWindow. При создании диалоговых окон Swing необходимо указы-

148

ГЛАВА 6

вать «родительское окно», которым может быть окно с рамкой JFrame или любое другое окно, унаследованное от базового класса окон Window (для удобства есть конструктор, не требующий «родительского» окна, но использующий вспомогательное невидимое окно, о котором мы говорили в разделе об окнах без рамки JWindow). Как уже отмечалось, диалоговое окно располагается на экране с учетом положения «родительского» окна. Рассмотрим небольшой пример, где создадим пару диалоговых окон:

//DialogWindows.java

//Диалоговые окна в Swing import javax.swing.*; import java.awt.event.*; import java.awt.*;

public class DialogWindows extends JFrame { public DialogWindows() {

super("DialogWindows");

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

//пара кнопок, вызывающих создание диалоговых окон

JButton button1 = new JButton("Обычное окно"); button1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {

JDialog dialog = createDialog("Немодальное",

Dialog.ModalityType.MODELESS);

dialog.setVisible(true);

}

});

JButton button2 = new JButton("Модальное окно"); button2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {

JDialog dialog = createDialog("Модальное",

Dialog.ModalityType.APPLICATION_MODAL); dialog.setVisible(true);

}

});

//создаем панель содержимого и выводим окно на экран

JPanel contents = new JPanel(); contents.add(button1); contents.add(button2); setContentPane(contents); setSize(350, 100);

Контейнеры высшего уровня

149

setVisible(true);

}

// создает диалоговое окно

private JDialog createDialog(String title, Dialog.ModalityType modal){

JDialog dialog = new JDialog(this, title, modal); dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE); dialog.setSize(200, 60);

return dialog;

}

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

new Runnable() {

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

}

}

В этом примере мы создаем окно с рамкой JFrame, в панель содержимого которого добавляем две кнопки JButton, щелчками на которых и будут создаваться диалоговые окна. Чтобы не дублировать в слушателях похожий код, мы создали отдельный метод createDialog(), создающий простое диалоговое окно с заданным заголовком и при необходимости модальностью. Заголовок диалогового окна, его «родитель» и признак модальности указываются в конструкторе6. При закрытии диалогового окна мы использовали операцию DISPOSE_ON_CLOSE, удаляющую окно после закрытия. Первая кнопка позволяет создать немодальное диалоговое окно, а вторая — его модальный вариант. Запустив приложение, вы увидите разницу: немодальные окна не запрещают работу с основным окном приложения, и вы можете снова и снова создавать их, не закрывая уже созданные окна. Такое поведение удобно там, где информация, находящаяся в диалоговом окне, не критична, или часто меняется, и каждый раз отвлекаться на модальное окно неудобно (хорошим примером может быть диалоговое окно смены шрифта в текстовом редакторе). Модальные окна запрещают вам работу со всеми остальными окнами приложения. Мы уже отмечали, что такое поведение требуется для получения важной информации, без которой невозможно продолжение работы, например, для получения пароля, позволяющего авторизовать пользователя, или запроса на выход из приложения.

Признак модальности задается с помощью перечисления (enum) из класса AWT Dialog, значения которого могут быть следующими:

Таблица 6.3. Виды модальности диалогов Swing

Вид модальности

Описание

 

 

MODELESS

Диалоговое окно не препятствует работе остальных окон, в том

 

числе и родительского, но всегда находится выше родительского

 

окна

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

150

ГЛАВА 6

Таблица 6.3 (продолжение)

 

 

 

Вид модальности

Описание

 

 

APPLICATION_MODAL

При создании и выводе такого диалогового окна все окна, уже соз-

 

данные приложением (формально, тем же экземпляром виртуаль-

 

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

 

диалоги и окна, создаваемые модальным диалогом, доступны

 

пользователю

DOCUMENT_MODAL

Блокирует работу окон, принадлежащих к одному родительскому

 

окну или иерархии окон, создавших друг друга, от самого первого

 

родителя. Другие окна не из этой иерархии не блокируются.

TOOLKIT_MODAL

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

 

плетами из одной страницы, даже если сами апплеты раздельные

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

Специальное оформление окон

Окна Swing предоставляют вам возможность настраивать так называемое визуальное «оформление» окон: их рамку, элементы управления (такие как кнопки закрытия или свертывания), системное меню. Необходимость этого ощущалась с самых первых выпусков Swing после появления механизма подключаемых внешнего вида и поведения. Какой бы внешний вид и поведение для своей программы вы не использовали, уверенности в том, что она будет выглядеть и вести себя одинаково на любой платформе не было, потому что окна, в которых размещался интерфейс, находился вне вашей власти, и их внешний вид мог довольно сильно контрастировать с вашим интерфейсом. Особенно это было заметно при выборе внешнего вида, который не имитировал какую-либо известную платформу, такую как Windows или Macintosh, а применялся только для Swing (например, используемый по умолчанию внешний вид Metal). Внешний вид, имитирующий ту или иную платформу, чаще всего именно на этой платформе и использовался, поэтому окна у приложения были соответствующие. Но вот другие внешние виды не обеспечивали нужного оформления окон.

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

Чтобы не принуждать вас для оформления окон каждый раз настраивать корневую панель и убирать с окон имеющиеся «украшения», в классы JFrame и JDialog (окну JWindow оформление не нужно) был добавлен статический метод setDefaultLookAndFeelDecorated(), обеспечивающий возможность оформления всех создаваемых окон. Единственное, что

Контейнеры высшего уровня

151

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

//WindowDecorations.java

//Специальное оформление окон Swing import javax.swing.*;

public class WindowDecorations {

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

new Runnable() { public void run() {

//включим украшения для окон

JFrame.setDefaultLookAndFeelDecorated(true);

JDialog.setDefaultLookAndFeelDecorated(true);

//окно с рамкой

JFrame frame = new JFrame("Окно с рамкой"); frame.setDefaultCloseOperation(

JFrame.EXIT_ON_CLOSE); frame.setSize(200, 200); frame.setVisible(true); // диалоговое окно

JDialog dialog = new JDialog(frame, "Диалог"); dialog.setDefaultCloseOperation(

JDialog.DISPOSE_ON_CLOSE); dialog.setSize(150, 100);

// так можно задавать тип оформления окна dialog.getRootPane().setWindowDecorationStyle(

JRootPane.INFORMATION_DIALOG); dialog.setVisible(true);

}

});

}

}

Вэтом примере мы прямо в методе main(), ни от чего не наследуя, создаем простое окно

срамкой и диалоговое окно. Но еще до их создания вызывается метод setDefaultLookAndFeelD ecorated(), означающий, что для создаваемых окон JFrame и JDialog потребуется специальное

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

152

ГЛАВА 6

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

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

Прозрачность и произвольные формы

При разговоре об окнах все время закрадывается мысль, что и говорить особенно не о чем, настолько они просты и настолько вспомогательные функции выполняют. Однако в новой версии JDK 1.7 (или если угодно, Java 7), окна наделены новыми впечатляющими возможностями, способными придать Java-приложениям авангардный внешний вид, конечно, если само приложение подразумевает подобный пользовательский интерфейс.

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

//ShapedWindows.java

//Полупрозрачные окна произвольных форм import javax.swing.*;

import java.awt.FlowLayout;

import java.awt.geom.RoundRectangle2D;

public class ShapedWindows extends JFrame { public ShapedWindows() {

super("ShapedWindows"); setDefaultCloseOperation(EXIT_ON_CLOSE);

//размер окна setSize(300, 200);

//несколько компонентов в ряд

Контейнеры высшего уровня

153

setLayout(new FlowLayout()); add(new JLabel("Текст:")); add(new JTextField(15));

//задаем округлую форму

RoundRectangle2D.Float roundedShape =

new RoundRectangle2D.Float(0, 0, 300, 200, 70, 70); setShape(roundedShape);

//задаем прозрачность

setOpacity(0.7f);

setVisible(true);

}

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

public void run() { JFrame.setDefaultLookAndFeelDecorated(true); new ShapedWindows();

}

});

}

}

В примере мы сразу же, еще до создания нашего окна с рамкой, указываем, что у всех окон должно быть специальное оформление, так мы лучше увидим особую форму нашего окна. В конструкторе в центр окна для наглядности добавляются надпись с небольшим текстовым полем, так что запустив пример, вы убедитесь что ввод текста работает несмотря ни на какие формы и прозрачность. Как мы видим, форма окна задается с помощью свойства shape, которое принимает объекты типа Shape из пакета java.awt.geom, описывающего разнообразные геометрические формы. Мы используем форму прямоугольника с закругленными краями. Прозрачность задается еще проще, нужно задать свойство opacity, в виде дробного числа от 0 (полностью прозрачное окно) до 1 (непрозрачное). У нас окно будет прозрачным примерно на 30%. Запустив пример, вы увидите какую форму принимает окно и что все его элементы функционируют как всегда. На иллюстрации окно находится над редактором с текстом книги.

154

ГЛАВА 6

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

И еще одно — полупрозрачные окна и окна произвольных форм могут не поддерживаться на некоторых платформах, и вызов рассмотренных нами методов приведет к исключениям. Если вы рассчитываете, что ваше приложение будет исполняться на самых разнообразных платформах, имеет смысл еще перед вызовом методов удостовериться в том, что они работают. Это можно сделать с помощью класса AWT GraphicsDevice, который позволяет узнать конфигурацию текущей графической системы.

Кратко об апплетах класс JApplet

В этой книге мы говорим о платформе Java и библиотеках Java Foundation Classes как о прекрасном средстве создания мощных и переносимых между платформами приложений. Однако в первых выпусках Java все средства разработки пользовательских интерфейсов в основном были направлены на создание апплетов (applets): небольших приложений, написанных в соответствии с простым программным интерфейсом и ориентированных на выполнение в браузере при просмотре HTML-страниц. Апплеты на самом деле были революционным шагом: они не зависели от браузеров и выполнялись на любой платформе с установленной виртуальной машиной Java, позволяя задействовать всю мощь Java внутри обычной HTML-страницы. Начиная с Java 2 (JDK 1.2), Sun предложила для выполнения апплетов концепцию модуля расширения (plug-in) Java. Этот модуль надо загрузить с официального сайта java.sun.com, что гарантирует получение самых свежих версий.

Однако нельзя сказать, что новая модель загрузки апплетов, предложенная Sun, повысила их популярность. Модуль расширения Java довольно объемен, и загружать его для выполнения некого апплета захочет не каждый пользователь. С другой стороны, возросли возможности и потенциал языков сценариев, встроенных в HTML-страницы, возможности самого языка HTML, чрезвычайно возросла популярность динамических страниц (AJAX), содержимое которых генерируется на сервере в зависимости от того, что хочет увидеть и получить пользователь и обновляется частями, без перезагрузки страницы. Все это получает название Web 2.0. Поэтому мы рассматриваем в книге создание отдельных Java-приложений, не ограниченных системой безопасности и браузером, к тому же приложения эти теперь можно легко распространять через Web (используя технологию Java Web Start) вместе с виртуальной машиной Java. Тем не менее, остаются ситуации, в которых использование апплетов с новыми возможностями оправдано, к примеру, встраивание в апплет уже готового мощного Swing-приложения для доступа к нему через Web. Рассмотрим вкратце процесс создания апплетов с использованием Swing.

Любой апплет (небольшое приложение, работающее в составе HTML-страницы), предполагающий задействовать компоненты Swing, должен быть унаследован от базового класса JApplet. Класс JApplet унаследован от класса Applet, описывающего инфраструктуру всех апплетов, и отличается от него лишь тем, что имеет в своем составе корневую панель, в которой вы размещаете свои компоненты. Класс JApplet, так же как и окна Swing, представляет собой тяжеловесный контейнер высшего уровня, именно он затем