240 |
ГЛАВА 9 |
И это выполняется с такой фантастической простотой (достаточно вызвать один-два метода), что в творческом порыве можно забыть буквально обо всем. Рассмотрим пример, в котором будут созданы кнопки самых необычных форм и размеров, а затем обсудим, как это делается:
//ButtonStyles.java
//Изменение внешнего вида кнопок JButton
//с помощью значков, цветов, рамок и т. п. import javax.swing.*;
import java.awt.*;
public class ButtonStyles extends JFrame { public ButtonStyles() {
super("ButtonStyles"); setDefaultCloseOperation( EXIT_ON_CLOSE );
//используем последовательное расположение setLayout(
new FlowLayout( FlowLayout.LEFT, 10, 10));
//самая простая кнопка
JButton button = new JButton("Обычная кнопка"); add(button);
//кнопка со значками на все случаи жизни button = new JButton();
button.setIcon(new ImageIcon("images/b1.gif")); button.setRolloverIcon(
new ImageIcon("images/b1r.gif")); button.setPressedIcon(
new ImageIcon("images/b1p.gif")); button.setDisabledIcon(
new ImageIcon("images/b1d.gif"));
//для такой кнопки лучше убрать
//все ненужные рамки и закраску button.setBorderPainted(false); button.setFocusPainted(false); button.setContentAreaFilled(false); add(button);
//кнопка с измененным цветом и HTML-текстом button = new JButton(
"<html><h2><font color=\"yellow\">Зеленая кнопка"); button.setBackground(Color.green);
add(button);
//изменение выравнивания текста и изображения
button = new JButton("Изменение выравнивания",
Элементы управления |
241 |
new ImageIcon("images/button.gif")); button.setMargin(new Insets(10, 10, 10, 10)); button.setVerticalAlignment(SwingConstants.TOP); button.setHorizontalAlignment(SwingConstants.RIGHT); button.setHorizontalTextPosition(SwingConstants.LEFT); button.setVerticalTextPosition(SwingConstants.BOTTOM); button.setIconTextGap(10);
//сделаем кнопку большой, чтобы увидеть выравнивание button.setPreferredSize(new Dimension(300, 100)); add(button);
//отключенная кнопка
button = new JButton("Выключено"); button.setEnabled(false); add(button);
// выводим окно на экран setSize(400, 350); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new ButtonStyles(); } });
}
}
В этом примере создается небольшое окно, в котором используется менеджер последовательного расположения FlowLayout. Такое расположение гарантированно придаст кнопкам предпочтительный размер. Далее в окно помещается несколько кнопок, каждая из которых демонстрирует свой способ изменения внешнего вида.
Первой в контейнер помещается самая обычная кнопка, которая уже встречалась нам в примерах, просто для того, чтобы можно было сравнить ее с остальными кнопками. Затем следует кнопка, на которой вместо текста располагаются значки, причем на «все случаи жизни». Для задания значков применяются свойства, перечисленные в табл. 9.1.
Таблица 9.1. Свойства, позволяющие задавать значки для кнопок
Свойства |
Описание |
(и методы get/set) |
|
icon |
Используется для установки «повседневного» значка, который будет ви- |
|
ден всегда, когда кнопка доступна |
rolloverIcon |
С помощью этого свойства можно установить значок для получения |
|
эффекта «наведения мыши» — когда указатель мыши оказывается на |
|
кнопке, появляется этот значок. Обычно в этом случае используется тот |
|
же значок, только к нему добавляется эффект объема или изменяется |
|
цвет части значка (как в нашем примере) |
242 |
ГЛАВА 9 |
Таблица 9.1 (продолжение) |
|
|
|
Свойства |
Описание |
(и методы get/set) |
|
pressedIcon |
Используется для установки значка, который выводится на экран при |
|
щелчке пользователем на кнопке (значок остается на экране до тех |
|
пор, пока кнопка мыши не отпущена и указатель находится в области |
|
кнопки) |
disabledIcon |
Если вы задаете значок посредством данного свойства, то, когда кноп- |
|
ка будет отключена с помощью метода setEnabled(false), появится |
|
специальный значок. Если специального значка нет, а обычный значок |
|
есть, то в качестве значка для отключенной кнопки будет использована |
|
черно-белая копия обычного значка (аналогично поведению надписи |
|
JLabel, которую мы подробно рассмотрели в главе 6) |
Если вывести такую кнопку на экран «как есть», она будет выглядеть, мягко говоря, не очень хорошо, потому что у нее останутся все свойства, присущие обычной кнопке с текстом: будет рисоваться рамка, при наличии фокуса появится контур, а при нажатии она будет закрашиваться темным цветом. Именно для того чтобы убрать эти эффекты, в примере задействуются методы setBorderPainted(), setFocusPainted() и setContentAreaFilled(). Первый из перечисленных методов позволяет отключить прорисовку рамки (то же самое можно сделать и с помощью вызова setBorder(null), но тогда вернуть кнопке ее рамку не удастся), второй отключает прорисовку специального контура, проявляющегося, если кнопка обладает фокусом ввода, а третий дает возможность отключить закраску кнопки в нажатом состоянии. Однако работать с такой кнопкой гораздо сложнее: будет непонятно, выбрана кнопка или нет, где она начинается и т. п. Поэтому мы рекомендуем быть осторожнее и применять эти методы только в тех приложениях, в которых весь интерфейс основан на разнообразных изображениях (например, в играх).
После этого в контейнер добавляется еще одна простая кнопка с текстом, для которой методом setBackground() устанавливается необычный цвет заполнения. В некоторых ситуациях бывает полезно просто изменить цвет кнопки, и вы видите, что сделать это действительно несложно. Помните только, что цвет заполнения изменится, только если у кнопки включено свойство непрозрачности (opaque). Все стандартные внешние виды, поставляемые со Swing, делают кнопки непрозрачными, но внешние виды от стороннего производителя могут иметь на это свою «точку зрения», так что иногда при использовании стороннего внешнего вида приходится вручную устанавливать для свойства непрозрачности значение true, если вы хотите закрасить кнопку в особенный цвет.
После прочтения главы 8 мы в полной мере смогли оценить мощь и гибкость встроенного в надписи и подсказки языка HTML, кнопки в этом отношении нас также не разочаровывают. Как и все остальные компоненты библиотеки, выводящие на экран некоторый текст, они поддерживают HTML, что вместе с широчайшими возможностями по настройке расположения содержимого и по управлению всеми аспектами внешнего вида кнопок дает вам неограниченную власть над видом вашего приложения. В примере мы парой строк кода смогли без особого напряжения создать кнопку, буквально «пылающую» яркими цветами и выделяющуюся нестандартными размерами. Не обладай кнопки поддержкой встроенного языка HTML, такой эффект дался бы нам гораздо большими усилиями, а про остальные возможности HTML (сразу же можно вспомнить незаменимый тег <br>, позволяющий легко организовать в компонентах Swing многострочные надписи) и вовсе пришлось бы забыть.
Далее показано, как и какими методами можно изменить положение содержимого кнопки (значка или надписи). Правда, чтобы увидеть это, пришлось придать кнопке
Элементы управления |
243 |
неестественно большой размер, потому что если оставить тот размер, который задан по умолчанию, менеджер FlowLayout придаст кнопке оптимальный размер, и как ни меняй после этого расположение содержимого, мы все равно ничего бы не увидели. Если же кнопка занимает больше пространства, чем ей надо, вопросы положения содержимого становятся актуальными.
Для демонстрации вариантов выравнивания была создана кнопка и с текстом, и со значком1 (как вы можете видеть из кода программы, для этого случая у класса JButton даже имеется специальный конструктор). В табл. 9.2 описаны свойства, которые помогли нам добиться желаемого визуального эффекта.
Таблица 9.2. Свойства, позволяющие настроить содержимое кнопок JButton
Свойства |
Описание |
(и методы get/set) |
|
margin |
Это свойство позволяет управлять размером полей, которые от- |
|
деляют содержимое кнопки (текст и значок) от ее границ. Как мы |
|
уже отмечали в главе 5, при использовании внешнего вида Metal |
|
рекомендуются поля в 12 пикселов |
verticalAlignment |
Данная пара свойств используется для изменения позиции все- |
|
го содержимого кнопки (и значка, и текста) относительно границ |
horizontalAlignment |
кнопки. С их помощью поместить это содержимое можно в лю- |
|
бой из углов кнопки или в ее центр (по умолчанию оно там и на- |
|
ходится). В нашем примере текст и значок помещаются в верх- |
|
ний правый угол кнопки. Полный список констант, управляющих |
|
положением содержимого, можно найти в интерактивной доку- |
|
ментации для класса SwingConstants, доступной на сайте java. |
|
sun.com |
horizontalTextPosition |
С помощью этих свойств вы можете управлять положением текста |
|
кнопки относительно ее значка (если он есть, конечно) по горизон- |
verticalTextPosition |
тали и вертикали. Использование в примере первого метода по- |
|
зволило разместить надпись не справа от значка, а слева2. Второй |
|
метод позволил поместить надпись ниже значка |
iconTextGap |
Это свойство долго отсутствовало в классе AbstractButton, хотя |
|
необходимость в нем была (например, в классе JLabel, имеющем |
|
аналогичный набор свойств для выравнивания содержимого, оно |
|
появилось вместе с появления библиотеки Swing). Свойство позво- |
|
ляет изменить расстояние между значком и текстом. |
Нетрудно увидеть, что содержимое кнопок и расположение в них значка относительно текста задается набором свойств, который полностью аналогичен набору свойств для выравнивания содержимого надписи JLabel. Мы обсуждали надписи в главе 8, поэтому большая часть возможностей кнопок должна быть вам прекрасно известна.
Наконец, самой последней в контейнер добавляется еще одна кнопка без всяких изысков, она показывает, как кнопку можно отключить. Делается это методом setEnabled(), этим же методом кнопка включается.
1 Этот симпатичный значок автор взял из специальной коллекции стандартной графики для Java-приложений, выполненных во внешнем виде Metal. Эта коллекция бесплатно распространяется фирмой Sun/Oracle, найти ее можно на сайте java.sun.com.
2 Кстати, использовать константы RIGHT (справа) и LEFT (слева) для выравнивания содержимого в приложениях, которые затем может понадобиться локализовать в странах с нестандартным письмом, не рекомендуется.
244 |
ГЛАВА 9 |
Запустив программу с примером, вы сможете убедиться, что внешний вид кнопок действительно меняется.
Конечно, в рассмотренном примере нет никаких хитростей, и все очень просто. Но так и должно быть — с каждой строкой кода вы создаете что-то действительно стоящее, а не решаете в очередной раз проблему рисования значка на кнопке. Библиотеки созданы для того, чтобы упростить работу программиста, вобрав в себя наиболее востребованные и сложные операции, и в этом библиотека Swing несомненно преуспела. И не забывайте о том, что все только что рассмотренные способы изменения внешнего вида действуют на все элементы управления библиотеки Swing, которые мы будем рассматривать в этой главе.
У кнопок есть модель
Удивительно, но у таких простых компонентов как кнопки есть собственная, самая настоящая, модель, хранящая состояние кнопки (поддержка модели описана в базовом классе AbstractButton, так что модель имеется у всех обсуждаемых в этой главе элементов управления). В главе 1 при обсуждении архитектуры MVC, используемой в Swing, мы выяснили, что модель хранит данные компонента, а отображает эти данные его UI-представитель. Именно так все работает для кнопок: в модели ButtonModel хранится информация о состоянии кнопки (нажата ли она, находится ли над ней указатель мыши, в выбранном ли она положении; даже символ мнемоники и группа, к которой может принадлежать кнопка, хранятся в модели), а ее UI-представитель получает эту информацию из модели
исоответствующим образом прорисовывает кнопку. Более того, все события, которые поддерживают кнопки и другие элементы управления (мы их вскоре обсудим подробнее
ирассмотрим пример), на самом деле генерируются в модели кнопок ButtonModel (к при-
меру, если кнопка нажата, и на ней отпускается кнопка мыши, модель генерирует событие ActionEvent). С другой стороны, внешний вид кнопки (текст, значок, выравнивание) в модели не хранится, все это находится уже в конкретных классах, таких как JButton, так что модель отвечает лишь за состояние кнопки, но не за ее внешний вид.
«Зачем же кнопкам нужна отдельная модель?» — спросите вы. На самом деле, кнопки настолько просты и незатейливы, что отдельная модель кажется для них излишней роскошью. Вы в большинстве своих программ вряд ли используете эту модель отдельно от кнопок — все будет сводиться к простому созданию компонента и размещению его в контейнере. Тем не менее, у модели кнопок есть свои преимущества. Представим, что нам нужно