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

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

Вывод вспомогательной информации

235

всех рисующих операциях Swing) и настраиваем нужные нам параметры: создаем перо BasicStroke подходящей толщины, включаем сглаживание графики и для удобства немного изменяем переданные нам в метод paintBorder() координаты. Затем мы дважды рисуем четыре кривых Безье: один раз цветом тени, другой раз с небольшим смещением координат основным цветом. Чтобы не повторять идентичный код, мы использовали цикл из двух итераций. Кривые Безье рисуются с помощью класса CubicCurve2D из пакета java.awt.geom, для их прорисовки необходимо указать координаты четырех точек: начала и конца кривой и двух контрольных точек, которые будут отвечать за «искривление» кривой. Контрольные точки мы размещаем на расстоянии одной трети и двух третей от начала кривой, в пяти пикселах влево для первой точки и в пяти пикселах вправо для второй. Это позволяет получить небольшое эффектное искривление. Всего создаются четыре кривые Безье — по одной для каждой из сторон рамки. После завершения рисования необходимо удалить созданный нами объект Graphics методом dispose(), сборщик мусора здесь не поможет, потому что в объекте Graphics используются ресурсы операционной системы, время жизни которых определяем мы сами7.

Для проверки работы нашей новой рамки мы добавили в класс BezierBorder метод main(). В нем (конечно, в потоке рассылки событий) мы создаем окно JFrame, в панель содержимого которого добавляем еще одну панель и устанавливаем для нее рамку с заголовком в качестве основы для нашей новой рамки (заметьте, что новая рамка легко совмещается со стандартными рамками Swing, в том числе и с использованной в примере рамкой с заголовком). В качестве параметров для BezierBorder мы указали зеленый цвет для рамки, темно-серый для тени, а толщину кривых установили равной трем пикселам. Чтобы удостовериться в том, что компоненты Swing легко совмещаются с нашей новой рамкой, мы добавили внутрь панели текстовую область JTextArea. Запустив программу с примером, вы увидите результаты наших трудов.

7 Можно предложить применять интересный вариант кода, позволяющий всегда гарантировать удаление созданного объекта Graphics. Как мы все знаем, всегда вызывается секция finally. Так как исключений при рисовании практически не возникает, секцию catch можно не указывать:

try {

создаем новый объект Graphics, рисуем…

}finally { объект.dispose()

}

236

ГЛАВА 8

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

Рамки и разработка собственных компонентов

Как мы уже обсудили в главе 4, в компонентах Swing нет специального места для рамок, рисующие механизмы библиотеки, скрытые в классе JComponent, прорисовывают рамку прямо на компоненте, вызывая метод paintBorder() после метода paintComponent(). Поэтому, если вы создаете компонент с собственной процедурой прорисовки и не прочь использовать для него рамки Swing, придется учитывать то место, которое занимает рамка (если она есть) и не рисовать на нем. Определить место, занимаемое рамкой, можно и вручную — мы уже обсудили интерфейс Border и знаем, что размеры рамки возвращает метод getBorderInsets(). Получив эти размеры и вычислив, где находится рамка, вы сможете не рисовать на занятом ею месте. Есть и еще один, более простой, способ определить прямоугольник, в котором можно рисовать без риска повредить рамку. Подобный прямоугольник возвращает статический метод getInteriorRectangle() класса AbstractBorder. Рассмотрим небольшой пример и увидим, на что способен этот метод:

//PaintingWithBorders.java

//Рисование компонента с учетом рамки import javax.swing.*;

import javax.swing.border.AbstractBorder; import java.awt.*;

public class PaintingWithBorders extends JFrame { public PaintingWithBorders() {

super("PaintingWithBorders"); setDefaultCloseOperation(EXIT_ON_CLOSE);

//добавим к нашему компоненту рамку

CustomComponent cc = new CustomComponent(); cc.setBorder(BorderFactory.

createTitledBorder("Рамка!"));

//добавим компонент в окно getContentPane().add(cc); setSize(400, 300); setVisible(true);

}

// компонент со своей собственной процедурой прорисовки class CustomComponent extends JComponent {

public void paintComponent(Graphics g) { // получаем подходящий прямоугольник

Rectangle rect = AbstractBorder. getInteriorRectangle(this, getBorder(),

Вывод вспомогательной информации

237

0, 0, getWidth(), getHeight()); // рисуем в нем g.setColor(Color.white);

g.fillRect(

rect.x, rect.y, rect.width, rect.height);

}

}

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

new Runnable() {

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

}

}

В примере мы создаем небольшое окно, в котором размещается специально созданный компонент CustomComponent со своей процедурой прорисовки. Процедура прорисовки, размещенная согласно правилам рисующих механизмов Swing в методе paintComponent(), не делает ничего экстраординарного: она просто закрашивает белым цветом область, занимаемую компонентом. Интересней увидеть, как учитывается место для рамки, которая, как мы знаем, может быть у каждого компонента, унаследованного от базового класса JComponent. Нам помогает метод getInteriorRectangle() класса AbstractBorder: он возвращает прямоугольник Rectangle, в котором можно рисовать, не опасаясь того, что вызванный после paintComponent() метод paintBorder() начнет рисовать рамку прямо на только что прорисованном нами компоненте. Метод getInteriorRectangle() вычисляет, какое пространство занимает рамка (вызывая ее метод getBorderInsets()) и на основе данных, передаваемых ему в качестве параметров (компонент, его рамка, координаты начала области, в которой вы намереваетесь рисовать, а также длина и высота этой области), определяет прямоугольник, прорисовка в котором не затронет рамку. Если рамки у компонента нет, то метод getInteriorRectangle() возвращает размеры компонента без изменений.

Мы устанавливаем для нашего компонента рамку с заголовком TitledBorder, и, запустив программу с примером, вы увидите, что рамка не затрагивает область, залитую белым цветом.

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

238

ГЛАВА 8

следует делать то же самое. Это не слишком элегантно и вносит в процесс прорисовки компонента определенные сложности, но другого пути нет — так уже реализована поддержка рамок в Swing.

Резюме

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

Глава 9. Элементы управления

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

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

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

Кнопки JButton

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

вних практически нет ничего уникального — все в них унаследовано от класса AbstractButton, и поэтому все, что верно для них, будет верно и для остальных элементов управления.

Чаще всего использование этого компонента сводится к его созданию, размещению

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

JButton button = new JButton("Кнопка"); button.addActionListener(new ButtonAction());

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

Внешний вид кнопок

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

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