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

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

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

Меню и панели инструментов

265

}

public void actionPerformed(ActionEvent e) { System.exit(0);

}

}

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

new Runnable() {

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

}

}

Итак, в этом примере создается относительно простая система меню. Как мы уже отмечали, элементы меню — это фактически кнопки, собранные в список. Списки элементов меню, или правильнее выпадающие меню (drop-down menus), реализованы в Swing классом JMenu. Именно они создаются методами createFileMenu() и createWhoMenu(), внутри которых в выпадающие меню методом add() добавляются разнообразные элементы меню, в том числе элементы-флажки и элементыпереключатели. После этого остается вывести созданные выпадающие меню на экран, для чего служит так называемая строка меню (menu bar), создать которую позволяет класс JMenuBar. В нем также есть метод add(), только не для элементов меню, а для выпадающих меню JMenu. Разместив все выпадающие меню своего приложения в строке меню, вы можете поместить ее в окно, вызвав предназначенный для этого метод setJMenuBar().

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

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

266

ГЛАВА 10

Строка меню JMenuBar

Главное, что нужно помнить при создании строки меню JMenuBar в своей программе, — это самый обыкновенный контейнер, ничем не отличающийся от панели JPanel и обладающий теми же самыми свойствами. Не нужно думать, что строка меню рождена только для работы с выпадающими меню. Вы можете смело добавлять в нее всевозможные компоненты, например надписи со значками или раскрывающиеся списки. Беззастенчиво заглянув внутрь класса JMenuBar, можно увидеть, что даже менеджер расположения у строки меню не какой-то экзотический, а прекрасно знакомый нам менеджер BoxLayout с расположением компонентов по горизонтали. Так что создание с виду совершенно новаторского меню оказывается весьма простым делом. Например, можно сделать со строкой меню следующее:

//TrickyMenuBar.java

//Полоска меню JMenuBar может многое import javax.swing.*;

import java.awt.*;

public class TrickyMenuBar extends JFrame { public TrickyMenuBar() {

super("TrickyMenuBar"); setDefaultCloseOperation( EXIT_ON_CLOSE );

//создаем главную полоску меню

JMenuBar menuBar = new JMenuBar();

//добавляем в нее выпадающие меню menuBar.add(new JMenu("Файл")); menuBar.add(new JMenu("Правка"));

//мы знаем, что используется блочное

//расположение, так что заполнитель

//вполне уместен menuBar.add(Box.createHorizontalGlue());

//теперь поместим в полоску меню

//не выпадающее меню, а надпись со значком

JLabel icon = new JLabel(

new ImageIcon("images/download.gif")); icon.setBorder(

BorderFactory.createLoweredBevelBorder());

menuBar.add(icon);

//помещаем меню в наше окно setJMenuBar(menuBar);

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

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

}

public static void main(String[] args) {

Меню и панели инструментов

267

SwingUtilities.invokeLater( new Runnable() {

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

}

}

В примере мы сначала добавили в строку меню традиционные выпадающие меню (они, правда, для экономии места были оставлены пустыми), а затем, предварительно использовав заполнитель, поместили в строку меню самую обычную надпись со значком, установив для нее тисненую рамку. Мы специально использовали анимированный GIF-файл, чтобы пример произвел еще более сильный эффект. Не правда ли, напоминает современные браузеры? И все, что мы сделали для этого, — это добавили два компонента.

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

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

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

Выпадающие меню JMenu и разделители JSeparator

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

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

268

ГЛАВА 10

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

//CascadedMenus.java

//Создание вложенных меню любой сложности import javax.swing.*;

import java.awt.*;

public class CascadedMenus extends JFrame { public CascadedMenus() {

super("CascadedMenus"); setDefaultCloseOperation( EXIT_ON_CLOSE );

//создаем строку главного меню

JMenuBar menuBar = new JMenuBar();

//создаем выпадающее меню

JMenu text = new JMenu("Текст");

//и несколько вложенных меню

JMenu style = new JMenu("Стиль"); JMenuItem bold = new JMenuItem("Жирный");

JMenuItem italic = new JMenuItem("Курсив"); JMenu font = new JMenu("Шрифт");

JMenuItem arial = new JMenuItem("Arial"); JMenuItem times = new JMenuItem("Times"); font.add(arial);

font.add(times);

//размещаем все в нужном порядке style.add(bold); style.add(italic); style.addSeparator(); style.add(font);

text.add(style);

menuBar.add(text);

//помещаем меню в окно setJMenuBar(menuBar);

//разделитель может быть полезен не только в меню

((JComponent)getContentPane()).setBorder( BorderFactory.createEmptyBorder(0, 5, 0, 0));

add(new JSeparator(SwingConstants.VERTICAL), "West");

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

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

}

Меню и панели инструментов

269

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

new Runnable() {

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

}

}

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

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

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

Клавиатурные сокращения и мнемоники

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

270

ГЛАВА 10

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

Если с мнемониками все более или менее ясно (мы уже встречались с ними в главах 8 и 9), то клавиатурные сокращения — привилегия элементов меню, где они чрезвычайно полезны. Клавиатурное сокращение — это произвольная комбинация всевозможных управляющих клавиш (Shift, Ctrl, Alt) с клавишей некоторого латинского символа, нажатие которой эквивалентно выбору элемента меню, что освобождает пользователя от необходимости разыскивать этот элемент в системе меню.

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

//GoodMenu.java

//Клавиатурные комбинации и мнемоники для меню Swing import javax.swing.*;

import java.awt.event.*; import java.awt.*;

public class GoodMenu extends JFrame { public GoodMenu() {

super("GoodMenu"); setDefaultCloseOperation( EXIT_ON_CLOSE );

//создаем строку главного меню

JMenuBar menuBar = new JMenuBar();

//некоторые весьма часто встречающиеся

//выпадающие меню menuBar.add(createFileMenu()); menuBar.add(createEditMenu());

//поместим меню в наше окно setJMenuBar(menuBar);

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

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

}

// создает меню "Файл"

private JMenu createFileMenu() { // выпадающее меню

JMenu file = new JMenu("Файл"); file.setMnemonic('Ф');

// пункт меню "Открыть"

Меню и панели инструментов

271

JMenuItem open = new JMenuItem("Открыть"); open.setMnemonic('О'); // русская буква

//установим клавишу быстрого доступа (латинская буква) open.setAccelerator(

KeyStroke.getKeyStroke('O', KeyEvent.CTRL_MASK));

//пункт меню "Сохранить"

JMenuItem save = new JMenuItem("Сохранить"); save.setMnemonic('С');

save.setAccelerator(

KeyStroke.getKeyStroke('S', KeyEvent.CTRL_MASK)); // добавим все в меню

file.add(open); file.add(save); return file;

}

// создает меню "Правка" private JMenu createEditMenu() {

// выпадающее меню

JMenu edit = new JMenu("Правка"); edit.setMnemonic('П');

// пункт меню "Вырезать"

JMenuItem cut = new JMenuItem("Вырезать"); cut.setMnemonic('В');

cut.setAccelerator(

KeyStroke.getKeyStroke('X', KeyEvent.CTRL_MASK)); // пункт меню "Копировать"

JMenuItem copy = new JMenuItem("Копировать"); copy.setMnemonic('К');

//клавишу быстрого доступа можно создать и так copy.setAccelerator(KeyStroke.getKeyStroke("ctrl C"));

//готово

edit.add(cut);

edit.add(copy); return edit;

}

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

new Runnable() {

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

}

}

272

ГЛАВА 10

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

Вы можете видеть, что мнемоники устанавливаются хорошо знакомым нам методом setMnemonic(), а вот клавиши быстрого доступа для элементов меню создаются с помощью класса KeyStroke. В этом классе определено несколько перегруженных статических методов вида getKeyStroke(), которые возвращают экземпляр нужной клавиатурной комбинации. В примере демонстрируются два варианта этого метода: один требует указания символа для клавиши быстрого доступа и набора управляющих клавиш (вы можете указать несколько управляющих клавиш, используя соответствующие константы из класса KeyEvent, связав их операцией поразрядного «ИЛИ»), а другой позволяет получить нужное клавиатурное сокращение с помощью строки, в которой сначала указывают псевдонимы управляющих клавиш, а затем символ клавиши быстрого доступа.

Запустив программу с примером, вы столкнетесь с досадной проблемой мнемоник. Если вы уже находитесь в меню, то есть оно получило фокус ввода, мнемоники с русскими символами работают. Но если вы хотите получить доступ к одному из выпадающих меню первый раз (меню не обладает фокусом ввода), мнемоники не работают (меню файл не активируется с помощью клавиш Alt-Ф). Какие бы сочетания клавиш мы не нажимали, это не поможет, и меню так и не получит фокус ввода. Это -- большой недостаток, и избежать его можно, либо включив в русские названия элементов меню латинские буквы, либо заранее предупредив пользователя о том, что строка меню в Swing получает фокус ввода c помощью клавиши F10 (по сути, она считается стандартной клавишей перехода на строку меню, но часто ли ее используют? Я не припомню, чтобы мне приходилось нажимать ее вместо мнемоники).

Но, как бы там ни было, клавиши быстрого доступа работают исправно, и довольно значительно повышают привлекательность приложения, так что не забывайте про них. Главное, помните о том, что в мире интерфейсов устоялись свои стандарты, пусть и молодые, и не стоит заново изобретать альтернативу старым добрым Control-S и Control-С, пусть они и набили уже всем оскомину. Даже на русском языке эти сочетания остаются собой. Для всех важных и частых операций своего приложения продумывайте наличие эффективных клавиш быстрого доступа1.

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

Меню и панели инструментов

273

Всплывающие меню JPopupMenu

Меню не обязательно относятся ко всему приложению, они могут пригодиться и для частей этого приложения (например, для текстового поля в редакторе). В таком случае меню появляется на экране только по желанию пользователя, поэтому их называют всплывающими (popup menu), или контекстными (context-sensitive menu). Пользователь вызывает это меню, чтобы получить список команд для того объекта приложения, с которым он работает, не тратя время на поиск этих команд в главном меню.

Всплывающие меню в Swing реализованы классом JPopupMenu. На самом деле вы уже видели этот класс «в деле» — именно он применяется в выпадающем меню JMenu для вывода его элементов. Но использовать его можно и отдельно, в чем мы сейчас убедимся:

//PopupMenus.java

//Работа с всплывающими меню import javax.swing.*;

import java.awt.*;

public class PopupMenus extends JFrame { public PopupMenus() {

super("PopupMenus"); setDefaultCloseOperation( EXIT_ON_CLOSE );

//получаем всплывающее меню

JPopupMenu popup = createPopupMenu();

//и привязываем к нашей панели содержимого

((JComponent)getContentPane()). setComponentPopupMenu(popup);

//"прозрачная" для меню кнопка

JButton button = new JButton("Проба пера"); button.setInheritsPopupMenu(true); add(button, "South");

// выводим окно на экран setSize(300, 200); setVisible(true);

}

// создаем наше всплывающее меню private JPopupMenu createPopupMenu() {

//создаем само всплывающее меню

JPopupMenu pm = new JPopupMenu();

//создаем его пункты

JMenuItem good = new JMenuItem("Отлично");

JMenuItem excellent = new JMenuItem("Замечательно"); // и добавдяем все тем же методом add() pm.add(good);

274

ГЛАВА 10

 

pm.add(excellent);

 

return pm;

 

}

 

public static void main(String[] args) {

 

SwingUtilities.invokeLater(

 

new Runnable() {

 

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

}

}

 

В этом примере мы создаем простейшее всплывающее меню из двух элементов; это делает метод createPopupMenu(). Как видно, все действия аналогичны тем, что мы делали при создании выпадающих меню JMenu. После того как всплывающее меню подготовлено к работе, необходимо определить, где и при каком условии нужно вывести его на экран. Так как на различных платформах способ вызова всплывающего меню может быть разным, момент, когда это необходимо сделать, определяет в Swing текущий менеджер внешнего вида и поведения2. Ну а само меню задается для конкретного компонента, методом setComponentPopupMenu(). В примере мы задаем меню сразу же для всей панели содержимого нашего окна. Данный метод находится в базовом классе библиотеки JComponent, и таким образом доступен для всех компонентов. Есть еще один интересный метод для всплывающих меню с названием setInheritsPopupMenu(). Это булево свойство, по умолчанию отключенное (равное false). Означает оно, что компонент будет унаследовать всплывающее меню от своего компонента-предка (как правило контейнера). В нашем примере кнопка унаследует всплывающее меню от панели содержимого, в чем вы сможете легко убедиться, запустив программу.

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

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

Реализовано всплывающее меню в Swing достаточно разумно. В обычной ситуации оно представляет собой легковесный компонент, унаследованный от базового класса

2 Для большинства платформ это щелчок правой кнопкой мыши или сочетание клавиш ShiftF10. Интересно, что сочетание клавиш вызовет меню для компонента, обладающего фокусом ввода, а иногда требуется вызывать его и для не способных обладать фокусом компонентов.