Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

135

processMouseEvent() мы делаем следующее: выясняем, что произошло нажатие кнопки мыши, а не что-либо еще (используя идентификатор типа события) и начинаем поиск компонента, на котором пользователь щелкнул мышью. Здесь нам помогает метод класса SwingUtilities getDeepestComponentAt(), позволяющий узнать, находится ли какой-либо видимый компонент в указанной точке. Он сделает всю работу, в том числе пройдет по всем вспомогательным панелям и контейнерам. Правда, напрямую передавать в него точку, хранящуюся в событии MouseEvent, нельзя, потому что эта точка находится в пространстве координат прозрачной панели, а метод getDeepestComponentAt () мы хотим применить в пространстве координат панели содержимого. Как раз для таких ситуаций в классе SwingUtilities имеется статический метод convertMouseEvent(), создающий новый объект MouseEvent в нужной системе координат. Далее мы вызываем помощь для компонента, на котором был выполнен щелчок мыши (если такой компонент обнаруживается). В заключение мы делаем прозрачную панель невидимой, чтобы интерфейс мог снова получать события, и меняем указатель мыши на обычный. Это поможет пользователю определить, что интерфейс снова действует в обычном режиме.

Наш пример — всего лишь прототип системы помощи, однако его легко приспособить под нужды реальных приложений. Достаточно заменить класс HelpSystem, использовав вместо него более мощную справочную систему, основанную, например, на языке разметки HTML, поддерживаемом в Swing, и придумать более удобный способ сопоставления компонента и справки для него. Удачным решением может быть обращение к клиентским свойствам компонентов Swing; с их помощью вы можете задать для каждого компонента (не наследуя от него) уникальный идентификатор, который и будет задействован в системе помощи для поиска нужной информации4.

Так как пример намеренно оставлен простым для понимания и акцентируется именно на прозрачной панели, у него имеется несколько изъянов, свойственных таким «примитивным» применениям прозрачной панели. Прежде всего, не перехватываются события от клавиатуры, и даже после вызова справочной системы вы можете перемещаться и активировать кнопки с помощью клавиатуры, что, как правило, нежелательно. Более гибкое поведение предлагает новый компонент библиотеки Swing JXLayer, который мы вскоре рассмотрим.

4 Если вы думаете о приложении с мощной справочной системой, попробуйте использовать пакет JavaHelp, созданный компанией Sun; вы сможете найти его на сайте java.sun.com. Он наделен всеми возможностями современных справочных систем и легко встраивается в приложение.

136

ГЛАВА 6

Вы видите, что прозрачная панель с легкостью позволяет вам работать поверх пользовательского интерфейса программы, как бы он ни был сложен. Мы уже придумали ей несколько применений, можно еще добавить, что упрощается создание обучающих систем: вы можете временно отрезать события от большинства компонентов, рисуя подробные комментарии поверх них и позволяя пользователю постепенно знакомиться с интерфейсом программы. Это может быть очень эффектно и способно значительно поднять привлекательность вашего приложения. Сделать это довольно просто — чтобы задать прозрачной панели форму, в пределах которой она будет перехватывать события от мыши, необходимо применить метод contains(), который мы обсуждали в главе 2. Рисовать же вполне можно в пределах всего окна, особенно полупрозрачными кистями.

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

Корневая панель — итог

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

Строка

меню Корневая

панель

Многоуровневая

панель

Прозрачная панель

Панель

содержимого

Рис. 6.4. Корневая панель

Сама корневая панель JRootPane представляет собой контейнер, унаследованный от базового класса Swing JComponent. В этом контейнере действует специальный алгоритм расположения компонентов, или менеджер расположения (см. главу 7), реализованный во внутреннем классе RootPaneLayout. Как раз этот менеджер расположения и отвечает за то, чтобы все составные части корневой панели располагались так, как им следует: многослойная панель занимает все пространство окна, в ее слое FRAME_CONTENT_LAYER

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

137

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

Корневая панель позволяет получить или изменить все свои составляющие части, для этого у нее имеется набор методов get/set (например, уже использованные нами методы getContentPane() и setContentPane()). В любом контейнере высшего уровня Swing вы можете получить задействованную в нем корневую панель, вызвав метод getRootPane(). Для удобства в контейнерах высшего уровня также имеются методы get/set, позволяющие напрямую получать или изменять составные части корневой панели. Кроме контейнеров высшего уровня, которые мы уже упоминали, корневая панель также применяется во внутренних окнах JInternalFrame, создаваемых в многодокументных приложениях и располагающихся на «рабочем столе» JDesktopPane. Это позволяет забыть про то, что данные «окна» представляют собой обычные легковесные компоненты, и работать с ними как с настоящими контейнерами высшего уровня, а также обеспечивает их правильное поведение.

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

//LayeredPaneEffects.java

//Создание эффектов для интерфейса

//с помощью многослойной панели import javax.swing.*;

import java.awt.*;

public class LayeredPaneEffects extends JFrame { public LayeredPaneEffects() {

super("LayeredPaneEffects"); setDefaultCloseOperation(EXIT_ON_CLOSE);

//несколько обычных кнопок и текстовое поле

JPanel buttons = new JPanel(); buttons.add(new JButton("Применить")); buttons.add(new JButton("Записать")); buttons.add(new JTextField(20));

//добавляем в панель содержимого getContentPane().add(buttons);

//добавляем компонент с анимацией в слой PALETTE Animation an = new Animation();

an.setBounds(50, 10, anim.getWidth(this), anim.getHeight(this));

getLayeredPane().add(an, JLayeredPane.PALETTE_LAYER);

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

138

ГЛАВА 6

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

}

//изображение private Image anim =

new ImageIcon("anim.gif").getImage();

//компонент, рисующий анимированное изображение class Animation extends JComponent {

public Animation() { setOpaque(false);

}

public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D)g;

//полупрозрачность

g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.3f));

//рисуем изображение

g2.drawImage(anim, 0, 0, this);

}

// мы никогда не получаем событий от мыши public boolean contains(int x, int y) {

return false;

}

}

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

new Runnable() {

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

}

}

В примере мы создаем окно JFrame, в которое добавляем несколько обычных элементов пользовательского интерфейса: пару кнопок и текстовое поле. Затем в слое PALETTE многослойной панели размещается специальный компонент Animation, который является прозрачным (мы устанавливаем его свойство непрозрачности равным false) и занимается тем, что прорисовывает на экране анимированное изображение (в формате GIF, который полностью поддерживается Java). В методе paintComponent() этого компонента мы используем возможности библиотеки Java2D, чтобы сделать изображение полупрозрачным (иначе оно слишком сильно будет загораживать обычный интерфейс). Обратите внимание на метод contains(), который определяет, попадает ли указанная точка экрана в область компонента. Так как у нас прозрачный компонент, необходимо всегда говорить системе событий что ни одна точка в компонент не попадает, а иначе у нас будут проблемы, например, с курсором в текстовом поле, так как курсор будет использоваться от нашего декоративного компонента, а не текстовый.

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

139

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

Может возникнуть справедливый вопрос, почему же мы не поместили изображение в прозрачную панель. Конечно, это возможно, однако многослойная панель может быть более гибкой. Мы поместили наш анимационный компонент в слой PALETTE, который находится ниже, чем слой POPUP, предназначенный для всплывающих меню. Если мы присоединим к текстовому полю такое меню, оно будет показано над нашей анимацией, что более логично. Тоже самое относится и к операциям перетаскивания, также отображаемых в многослойной панели. А используй мы прозрачную панель, которая «нависает» над всем окном, у нас не было бы простой возможности решить, какие компоненты и операции анимация перекрывает, а какие нет. Более того, мы уже знаем что прозрачная панель является общим ресурсом и представляет собой один единственный компонент, в то время как в многоуровневую панель можно добавить сколь угодно много компонентов подобных тем что мы создали в примере. Перефразируя, можно сказать, что если есть возможность не применять прозрачную панель, лучше этого не делать, и мы здесь последовали этому примеру.

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

Расширение границ — J(X)Layer

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

Специально, чтобы избавиться от всех этих ограничений и наделить Swingприложения расширенными возможностями, Александр Поточкин из команды разработчиков Swing создал инструмент под названием JXLayer. Поначалу этот инструмент развивался отдельно, а потом стал постепенно переводиться в библиотеку Swing в версии JDK 1.7 под названием JLayer. На момент написания книги отдельный

Источник: https://studfile.net/preview/15935977/