В предыдущих главах мы писали программы, связанные с текстовым терминалом и запускающиеся из командной строки. Такие программы называются консольными приложениями. Они разрабатываются для выполнения на серверах, там, где не требуется интерактивная связь с пользователем.
Программы, тесно взаимодействующие с пользователем, воспринимающие сигналы от клавиатуры и мыши, работают в графической среде. Каждое приложение, предназначенное для работы в графической среде, должно создать хотя бы одно окно, в котором будет происходить его работа, и зарегистрировать его в графической оболочке операционной системы, чтобы окно могло взаимодействовать с операционной системой и другими окнами: перекрываться, перемещаться, менять размеры, сворачиваться в ярлык.
Р—Р° десятилетия развития вычислительной техники создано РјРЅРѕРіРѕ различных графических систем: MS Windows, X Window System, Macintosh. Р’ каждой РёР· РЅРёС… СЃРІРѕРё правила построения РѕРєРѕРЅ Рё РёС… компонентов: меню, полей РІРІРѕРґР°, РєРЅРѕРїРѕРє, СЃРїРёСЃРєРѕРІ, полос прокрутки. Рти правила сложны Рё запутаны. Графические API, предназначенные для создания пользовательского интерфейса, содержат сотни функций.
Для облегчения создания окон и их компонентов написаны библиотеки функций и классов: MFC, Motif, OpenLook, Qt, Tk, Xview, OpenWindows, OpenGL, GTK+ и множество других. Каждый класс такой библиотеки описывает сразу целый графический компонент, управляемый методами этого и других классов.
В технологии Java дело осложняется тем, что приложения Java должны работать в любой или хотя бы во многих графических средах. Нужна библиотека классов, независимая от конкретной графической системы.
В первой версии JDK задачу решили следующим образом: были разработаны интерфейсы, содержащие методы работы с графическими объектами. Классы графической библиотеки Java реализуют эти интерфейсы для создания приложений. Приложения Java используют методы этих интерфейсов для размещения и перемещения графических объектов, изменения их размеров, взаимодействия объектов друг с другом.
С другой стороны, для работы с экраном в конкретной графической среде эти интерфейсы реализуются в каждой такой среде отдельно. В каждой графической оболочке это делается по-своему, средствами самой оболочки с помощью графических библиотек данной операционной системы.
Такие интерфейсы были названы peer-интерфейсами.
Библиотека классов Java, основанных на peer-интерфейсах, получила название AWT (Abstract Window Toolkit). Для вывода на экран объекта, созданного в приложении Java и основанного на peer-интерфейсе, создается парный ему (peer-to-peer) объект графической подсистемы операционной системы, который и отображается на экране. Поэтому графические объекты AWT в каждой графической среде имеют вид, характерный для этой среды: в MS Windows, Motif, OpenLook, OpenWindows, — везде окна, созданные в AWT, выглядят как "родные" окна этой графической среды.
Пара объектов, реализующих РѕРґРёРЅ peer-интерфейс, тесно взаимодействуют РІРѕ время работы приложения. Рзменение объекта РІ приложении Java немедленно влечет изменение объекта графической оболочки Рё меняет его РІРёРґ РЅР° экране.
Рменно РёР·-Р·Р° такой реализации peer-интерфейсов Рё РґСЂСѓРіРёС… "родных" (native) методов, написанных главным образом РЅР° языке РЎ++, приходится для каждой платформы выпускать СЃРІРѕР№ вариант JDK.
В версии JDK 1.1 библиотека AWT была переработана. В нее добавлена возможность создания компонентов, полностью написанных на Java и не зависящих от peer-интерфейсов. Такие компоненты стали называть "легкими" (lightweight), в отличие от компонентов, реализованных через peer-интерфейсы, названных "тяжелыми" (heavy).
"Легкие" компоненты везде выглядят одинаково, сохраняют заданный при их создании вид (look and feel). Более того, приложение можно разработать таким образом, чтобы после его запуска можно было выбрать какой-то определенный вид: "Motif1, "Metal", "Windows 95" или еще какой-нибудь другой, и сменить этот вид в любой момент работы.
Рта интересная особенность "легких" компонентов получила название PL&F (Pluggable Look and Feel). Рто сокращение РёРЅРѕРіРґР° записывают РІ РІРёРґРµ "plaf'.
Тогда же была создана обширная библиотека "легких" компонентов Java, названная Swing. В ней были переписаны все компоненты библиотеки AWT, так что компоненты библиотеки Swing могут использоваться самостоятельно, несмотря на то, что все классы из нее расширяют классы библиотеки AWT.
Библиотека классов Swing поставлялась как дополнение к JDK 1.1. В следующие версии Java SE JDK она включена наряду с AWT как основная графическая библиотека классов, реализующая идею "100 % Pure Java".
В Java SE библиотека AWT значительно расширена не только библиотекой Swing, но и добавлением новых средств рисования, вывода текстов и изображений, получивших название Java 2D, и средств, реализующих перемещение текста методом DnD (Drag and Drop).
Кроме того, в Java SE включены новые методы ввода/вывода Input Method Framework и средства связи с дополнительными устройствами ввода/вывода, такими как световое перо или клавиатура Брайля, названные Accessibility.
Все перечисленные средства Java SE: AWT, Swing, Java 2D, DnD, Input Method Framework и Accessibility — составили библиотеку графических средств Java, названную JFC (Java Foundation Classes).
Описание каждого из этих средств составит целую книгу, поэтому мы вынуждены ограничиться представлением только основных средств библиотек AWT и Swing.
Компонент и контейнер
РћСЃРЅРѕРІРЅРѕРµ понятие графического интерфейса пользователя (Р“РРџ) — компонент (component) графической системы. Р’ СЂСѓСЃСЃРєРѕРј языке слово "компонент" подразумевает просто составную часть, элемент чего-РЅРёР±СѓРґСЊ, РЅРѕ РІ графическом интерфейсе это понятие гораздо конкретнее. РћРЅРѕ означает отдельный, полностью определенный элемент, который можно использовать РІ графическом интерфейсе независимо РѕС‚ РґСЂСѓРіРёС… элементов. Например, поле РІРІРѕРґР°, РєРЅРѕРїРєР°, строка меню, полоса прокрутки, радиокнопка (переключатель). Само РѕРєРЅРѕ приложения — тоже его компонент. Компоненты РјРѕРіСѓС‚ быть Рё невидимыми, например панель, объединяющая компоненты, тоже является компонентом.
Р’С‹ РЅРµ удивитесь, узнав, что РІ AWT компонентом считается объект класса Component или объект РІСЃСЏРєРѕРіРѕ класса, расширяющего класс Component. Р’ классе Component собраны общие методы работы СЃ любым компонентом графического интерфейса пользователя. Ртот класс — центр библиотеки AWT.
Каждый компонент перед выводом на экран помещается в контейнер (container). Контейнер "знает", как поместить компоненты на экран. Разумеется, в языке Java контейнер — это объект класса Container или всякого его расширения. Прямой наследник этого класса — класс JComponent — вершина иерархии многих компонентов библиотеки Swing.
Создав компонент — объект класса Component или его расширения, следует добавить его к предварительно созданному объекту класса Container или его расширения одним из методов контейнера add ().
Класс Container сам является невидимым компонентом, он расширяет класс Component. Таким образом, в контейнер наряду с компонентами можно помещать контейнеры, в которых находятся какие-то другие компоненты, достигая тем самым большой гибкости расположения компонентов.
Основное окно приложения, активно взаимодействующее с операционной системой, необходимо построить по правилам ее графической системы. Оно должно перемещаться по экрану, изменять размеры, реагировать на действия мыши и клавиатуры. В окне должны быть как минимум следующие стандартные компоненты:
□ строка заголовка (title bar), с левой стороны которой необходимо поместить кнопку контекстного меню, а с правой — кнопки сворачивания и разворачивания окна и кнопку закрытия приложения;
□ окно должно быть окружено рамкой (border), реагирующей на действия мыши.
Окно с этими компонентами в готовом виде описано в классе Frame. Чтобы создать окно в библиотеке AWT, достаточно сделать свой класс расширением класса Frame, как показано в листинге 8.1. Всего восемь строк текста, и окно готово.
Листинг 8.1. Слишком простое окно приложения AWT
import java.awt.*;
class TooSimpleFrame extends Frame{
public static void main(String[] args){
Frame fr = new TooSimpleFrame(); fr.setSize(400, 150); fr.setVisible(true);
}
}
Класс TooSimpleFrame обладает всеми свойствами класса Frame, являясь его расширением. В нем создается экземпляр окна fr, и методом retsiref) устанавливаются размеры окна на экране — 400x150 пикселов. Если не задать размер окна, то на экране появится окно минимального размера — будет видна только строка заголовка. Конечно, потом окно можно растянуть с помощью мыши до любого размера.
Затем окно выводится на экран методом setVisible(true). Дело в том, что, с точки зрения библиотеки AWT, создать окно — значит, выделить область оперативной памяти, заполненную нужными пикселами, а вывести содержимое этой области на экран — уже другая задача, которую и решает метод setVisible (true ).
Конечно, такое окно непригодно для работы. Не говоря уже о том, что у него нет заголовка, окно нельзя закрыть. Хотя его можно перемещать по экрану, менять размеры, сворачивать на панель задач и раскрывать, но команду завершения приложения мы не запрограммировали. Окно нельзя закрыть ни щелчком кнопки мыши на кнопке с крестиком в правом верхнем углу окна, ни комбинацией клавиш
В листинге 8.2 к программе листинга 8.1 добавлены заголовок окна и обращение к методу, позволяющему завершить приложение.
Листинг 8.2. Простое окно приложения
import java.awt.*;
import java.awt.event.*;
class SimpleFrame extends Frame{
SimpleFrame(String s){ super(s);
setSize(400, 150); setVisible(true);
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent ev){
System.exit(0);
}
});
}
public static void main(String[] args){ new SimpleFrame(" Моя программа");
}
}
Для того чтобы показать разные варианты построения программы, в нее добавлен конструктор класса SimpleFrame, обращающийся к конструктору своего суперкласса Frame, который записывает свой аргумент s в строку заголовка окна.
Р’ конструктор перенесена установка размеров РѕРєРЅР°, вывод его РЅР° экран Рё добавлено обращение Рє методу addWindowListener (), реагирующему РЅР° действия СЃ РѕРєРЅРѕРј. Р’ качестве аргумента этому методу передается экземпляр безымянного внутреннего класса, расширяющего класс WindowAdapter. Ртот безымянный класс реализует метод windowClosing (), обрабатывающий попытку закрытия РѕРєРЅР°. Данная реализация очень проста — приложение завершается статическим методом exit () класса System. РћРєРЅРѕ РїСЂРё этом закрывается автоматически.
Все это мы подробно разберем в главе 15, а пока просто добавляйте приведенные строчки во все ваши программы, использующие библиотеку AWT, для закрытия окна и завершения работы приложения.
Ртак, РѕРєРЅРѕ готово. РќРѕ РѕРЅРѕ РїРѕРєР° пусто. Выведем РІ него, РїРѕ традиции, приветствие "Hello, XXI Century World!", правда, слегка измененное. Р’ листинге 8.3 представлена полная программа этого вывода, Р° СЂРёСЃ. 8.1 демонстрирует РѕРєРЅРѕ.
Листинг 8.3. Графическая программа с приветствием
import java.awt.*;
import java.awt.event.*;
class HelloWorldFrame extends Frame{
HelloWorldFrame(String s){ super(s);
}
public void paint(Graphics g){
g.setFont(new Font("Serif", Font.ITALIC|Font.BOLD, 30)); g.drawString("Hello, XXI Century World!", 20, 100);
}
public static void main(String[] args){
Frame f = new РќРµРРѕРогШЕгатеСЗдравствуй, РјРёСЂ XXI века!"); f. setSize(400, 150); f.setVisible(true);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent ev){
System.exit(0);
}
});
}
}
Рис. 8.1. Окно программы-приветствия |
Для вывода текста мы переопределяем метод paint () класса Component. Класс Frame всегда наследует этот метод, но с пустой реализацией.
Метод paint () получает РІ качестве аргумента экземпляр g класса Graphics, умеющего, РІ частности, выводить РЅР° экран текст методом drawString (). Р’ этом методе РєСЂРѕРјРµ текста РјС‹ указываем положение начала строки РІ РѕРєРЅРµ — 20 пикселов РѕС‚ левого края Рё 100 пикселов сверху. Рта точка — левая нижняя точка первой Р±СѓРєРІС‹ текста, H.
Кроме того, мы установили новый шрифт "Serif" большего размера — 30 пунктов, полужирный, курсив. Всякий шрифт — объект класса Font, а задается он методом
setFont ( ) класса Graphics.
Работу со шрифтами мы рассмотрим в следующей главе.
В листинге 8.3, для разнообразия, мы вынесли вызовы методов установки размеров окна, вывода его на экран и завершения программы в метод main ().
Как вы видите из этого простого примера, библиотека AWT — большая и разветвленная, в ней множество классов, взаимодействующих друг с другом. Рассмотрим иерархию некоторых наиболее часто используемых классов AWT.
Рерархия классов AWT
На рис. 8.2 показана иерархия основных классов AWT. Основу ее составляют готовые компоненты: Button, Canvas, Checkbox, Choice, Container, Label, List, Scrollbar, TextArea, TextField, MenuBar, Menu, PopupMenu, MenuItem, CheckboxMenuItem. Если этого набора не хватает, то от класса Canvas можно породить собственные "тяжелые" компоненты, а от класса Component — "легкие" компоненты.
Object
-Component — -Color -Cursor -Font -FontMetrics - Image -Polygon -BorderLayout -Card Layout -FlowLayout -GridBagLayout -GridLayout
-Button —Canvas -Checkbox — Choice —Container —r- JComponent
Label — List Scrollbar —T extComponent
t TextArea TextField
Panel-Applet
— ScrollPane
- JApplet
Window -p Dialog —p FileDialog kjWindow L JDialog L Frame — JFrame
GridBagConstaints
-Graphics -Graphics2D
Point2D-Point
—RectangularShape — Rectangle2D — Rectangle CheckboxGroup -MenuShortcut
MenuComponent-p MenuItem —Menu-PopupMenu
-Event L MenuBar I— CheckboxMenuItem
-EventObject —AWTEvent MediaTracker
Р РёСЃ. 8.2. Рерархия основных классов AWT
Основные контейнеры — это классы Panel, ScrollPane, Window, Frame, Dialog, FileDialog. Свои "тяжелые" контейнеры можно породить от класса Panel, а "легкие" — от класса
Container.
Целый набор классов помогает размещать компоненты, задавать цвет, шрифт, рисунки и изображения, реагировать на сигналы от мыши и клавиатуры.
На рис. 8.2 показаны и начальные классы иерархии библиотеки Swing — классы
JComponent, JWindow, JFrame, JDialog, JApplet.
Окно библиотеки Swing
Для получения окна с помощью средств библиотеки Swing необходимо импортировать в свою программу пакет javax.swing и расширить класс JFrame, как показано в листинге 8.4. Вместо длинного метода закрытия окна можно обратиться к методу
setDefaultCloseOperation(JFrame.EXIT ON CLOSE);
указав в нем константу EXIT_ON_CLOSE класса JFrame, предписывающую завершить работу приложения при закрытии окна. Другие константы, определенные в интерфейсе
WindowConstants, предписывают:
□ dispose_on_close — закрыть окно и освободить память, занимаемую им, но не завершать приложение;
□ do_nothing_on_close — игнорировать команду закрытия окна;
в–Ў hide_on_close — только убрать РѕРєРЅРѕ СЃ экрана. Рто значение РїРѕ умолчанию.
Фон окна Swing серый, поэтому в конструктор добавлен еще метод setBackground(Color.WHITE), устанавливающий белый цвет фона.
Листинг 8.4. Простое окно приложения Swing
import java.awt.*;
import javax.swing.*;
class SimpleFrame extends JFrame{
SimpleFrame(String s){ super(s);
setBackground(Color.WHITE); setSize(400, 150); setVisible(true);
setDefaultCloseOperation(EXIT ON CLOSE);
}
public void paint(Graphics g){
g.setFont(new Font("Serif", Font.ITALIC|Font.BOLD, 30)); g.drawString("Hello, XXI Century World!", 20, 100);
}
public static void main(String[] args){ new SimpleFrame(" Моя программа");
}
Метод paint () принадлежит классу Component, он выглядит точно так же, как в листинге 8.3. Его можно без всяких изменений перенести в программу, использующую библиотеку Swing.
Рспользование системных приложений
В большинстве операционных систем пользователь устанавливает для себя браузер по умолчанию, который открывается, когда пользователь выбирает файл с расширением html. Файлы с другими расширениями тоже часто связываются с приложениями, обрабатывающими их. Например, файл с расширением txt часто открывается в текстовом редакторе. Кроме того, пользователь может выбрать для себя почтовый клиент по умолчанию.
Рти возможности использует класс Desktop РёР· пакета java.awt. РЈ него есть методы, позволяющие запустить некоторые приложения пользователя:
□ browse(uri file) — открывает браузер по умолчанию, загружающий указанный файл
file;
□ mail () — открывает почтовый клиент по умолчанию;
□ mail (uri mailto) — открывает почтовый клиент по умолчанию, заполняя поля "To", "Cc", "Subject", "Body" значениями, взятыми из аргумента mailto;
□ edit(File file) — открывает текстовый редактор, связанный с указанным файлом file, и загружает в него файл file;
□ open(File file) — открывает указанный файл file;
□ print(File file) — печатает указанный файл file на принтере, назначенном этому файлу.
РЎРІСЏР·СЊ СЃ приложениями пользователя устанавливается через операционную систему, поэтому экземпляр класса Desktop создается РЅРµ конструктором, Р° статическим методом getDesktop (). Рту СЃРІСЏР·СЊ удается установить РЅРµ РІРѕ всех системах, поэтому предварительно следует сделать проверку статическим логическим методом isDesktopSupported(). Ртак, создание экземпляра класса Desktop выглядит так:
Desktop d = null; if (Desktop.isDesktopSupported()) d = Desktop.getDesktop();
Вызвать каждое из перечисленных ранее приложений тоже удается не всегда, поэтому перед обращением к какому-либо методу класса Desktop имеет смысл сделать проверку логическим методом
public boolean isSupported(Desktop.Action action);
Аргументом этого метода может служить одна из следующих констант вложенного перечисления Action:
□ browse — у пользователя есть браузер по умолчанию;
□ MAIL — у пользователя есть почтовый клиент по умолчанию;
□ EDIT — можно открыть текстовый редактор, связанный с файлами;
□ OPEN — можно открыть файл;
□ PRINT — можно напечатать файл.
С учетом этой проверки, обращение к методам класса Desktop будет выглядеть так:
if (d.isSupported(Desktop.Action.BROWSE))
d.browse(new URI(""));
if (d.isSupported(Desktop.Action.MAIL)) d.mail();
if (d.isSupported(Desktop.Action.EDIT))
d.edit(new File("/home/user/j ava/src/MyDesktop.j ava");
if (d.isSupported(Desktop.Action.OPEN))
d.open(new File("/home/user/j ava/src/MyDesktop.j ava");
if (d.isSupported(Desktop.Action.PRINT))
d.print new File("/home/user/java/src/MyDesktop.java");
System Tray
Графическое приложение Java может установить ярлык РІ Р·РѕРЅРµ уведомлений (notification area), называемой также системным лотком (system tray) или просто треем. Рта Р·РѕРЅР° обычно размещается РІ правом нижнем углу экрана Рё содержит часы Рё ярлыки запущенных программ. Для работы СЃ Р·РѕРЅРѕР№ уведомлений РІ пакет java.awt включен класс SystemTray. Его метод add(Trayicon icon) помещает ярлык icon РІ Р·РѕРЅСѓ уведомлений, Р° метод remove (Trayicon icon) удаляет его РёР· Р·РѕРЅС‹. Каждое приложение может поместить РІ Р·РѕРЅСѓ уведомлений несколько ярлыков.
Как видно из заголовков, параметр этих методов, ярлык- это объект класса TrayIcon.
Он создается конструктором класса. Объект активен — он может реагировать на события, такие как действия мыши, открывая, например, всплывающее меню после щелчка на ярлыке правой кнопкой мыши или меняя изображение при наведении на него курсора мыши. Обработка таких событий описана в главе 15.
Как и при действиях с приложениями пользователя, не каждая система позволяет управлять зоной уведомлений. Поэтому работа с этой зоной связана с проверками и выглядит следующим образом:
TrayIcon icon = null; if (SystemTray.isSupported()){
SystemTray tray = SystemTray.getSystemTray();
Image im = Toolkit.getDefaultToolkit.getImage("myicon.gif"); icon = new TrayIcon(im); tray.add(icon);
}
Более подробно работа с зоной уведомлений показана в документации к классу SystemTray. Она будет понятна после прочтения главы 15.
Splash Screen
Очень часто при загрузке приложения на экране вначале появляется небольшое окнозаставка (splash screen) с каким-нибудь изображением, сменяемое затем главным окном приложения. Такое окно можно открыть при запуске приложения из командной строки, указав ключ -splash. Например, если файл с изображением называется name.gif, то запустить приложение можно так:
java -splash:name.gif SimpleFrame
При запуске приложения из архива, например
java -jar SimpleFrame.jar
имя файла с изображением записывается в файл MANIFEST.MF, как показано в главе 25.
Некоторые возможности управления РѕРєРЅРѕРј-заставкой предоставляет класс SplashScreen РёР· пакета java.awt. Рто возможность менять изображение методом setImageURL(URL image) Рё возможность рисовать РІ РѕРєРЅРµ, получив ссылку РЅР° объект класса Graphics2D методом createGraphics (). После заполнения РѕРєРЅР°-заставки РѕРЅРѕ выводится РЅР° экран методом update (). Р’СЃРµ это делается РїРѕ следующей схеме:
SplashScreen splash = SplashScreen.getSplashScreen(); if (splash != null){
Graphics2D g = splash.createGraphics();
g.setPaintMode();
g.drawString("Loading...", 100, 200);
// и т. д., рисуем, как написано в главе 9. g.update();
}
Заключение
Как видите, библиотека графических классов AWT очень велика Рё детально проработана. Рто многообразие классов только отражает многообразие задач построения графического интерфейса. Стремление улучшить интерфейс безгранично.
Оно приводит к созданию все новых библиотек классов и расширению существующих. Независимыми производителями создано уже много графических библиотек Java: KL Group, JBCL, SWAT, SWT и появляются все новые и новые библиотеки. Сведения о них можно получить на сайтах, указанных во введении.
В следующих главах мы подробно рассмотрим, как можно использовать библиотеки AWT и Swing для создания собственных приложений с графическим интерфейсом пользователя, изображениями, анимацией и звуком.
Вопросы для самопроверки
1. Что такое графический интерфейс пользователя?
2. Что такое графическая библиотека классов?
3. Что называется графическим компонентом?
4. Назовите известные вам графические компоненты.
5. Что такое контейнер в графическом интерфейсе?
6. Будет ли основное окно приложения контейнером?
7. Можно ли использовать библиотеку Swing без библиотеки AWT?
8. Какая разница между компонентами AWT и компонентами Swing?
9. Можно ли совсем отказаться от компонентов библиотеки AWT?
ГЛАВА 9