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

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

100

ГЛАВА 4

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

//RotatedUI.java

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

import java.awt.*;

public class RotatedUI extends JFrame { public RotatedUI() {

super("RotatedUI");

//выход при закрытии окна setDefaultCloseOperation(EXIT_ON_CLOSE);

//добавляем особую панель

RotatingPanel rp = new RotatingPanel(); add(rp);

//добавляем в панель компоненты rp.add(new JButton("Привет!")); rp.add(new JTextField(20));

//устанавливаем свой RepaintManager RepaintManager.setCurrentManager(

new RotatingRepaintManager());

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

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

}

// компонент, который поворачивает всех потомков class RotatingPanel extends JPanel {

// отвечает за прорисовку потомков protected void paintChildren(Graphics g) {

Graphics2D g2 = (Graphics2D) g; g2.translate(50, 200);

//поворот на 45 градусов g2.rotate(-Math.PI/4);

//небольшое растяжение g2.shear(-0.1, -0.1);

//обычное рисование предков super.paintChildren(g);

Рисование в Swing

101

}

}

// особый тип RepaintManager

class RotatingRepaintManager extends RepaintManager { // все запросы на перерисовку попадают сюда

public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {

// ищем нужного предка

Container parent = c;

while (! (parent instanceof RotatingPanel)) { parent = parent.getParent();

if ( parent == null ) {

// мы не нашли нашего предка, сброс parent = c;

break;

}

}

// перерисовываем весь компонент полностью super.addDirtyRegion((JComponent) parent,

0, 0, parent.getWidth(), parent.getHeight());

}

}

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

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

}

}

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

Если остановиться на этом и запустить пример, то поначалу интерфейс будет прокручен и растянут, однако любое обновление кнопки или поля приведет к тому, что они будут рисовать себя привычным образом. Замена стандартного объекта RepaintManager на собственный позволяет нам перехватывать желания кнопки и поля. Наш объект унаследован от стандартного и переопределяет метод addDirtyRegion(), который вызывается при любом запросе любого компонента Swing на перерисовку. Мы смотрим, не находится ли компонент в нашей «крутящей» панели, и, если да, просто полностью перерисовываем ее, а если нет, позволяем рисовать оригинальному «просителю». Производительность перерисовки при таком грубом подходе конечно упадет, но это просто пример. Запустив его, вы убедитесь, что интерфейс выглядит более чем авангардно.

102

ГЛАВА 4

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

Как мы видим, власть у RepaintManager имеется порядочная. Однако не стоит забывать, что на все Swing-приложение имеется лишь один объект RepaintManager, и он может быть уже заменен такой библиотекой, как SwingX или даже сторонним внешним видом. Использовать свой собственный объект RepaintManager стоит в случае крайней необходимости и при этом тщательно проверять, в полном ли объеме работают все дополнительные библиотеки, компоненты и инструменты. Стандартные же компоненты Swing без труда переносят замену объекта RepaintManager.

Отладка графики

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

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

6 На языке шаблонов проектирования класс DebugGraphics представляет собой не что иное, как типичный декоратор.

Рисование в Swing

103

в его конструктор ссылку на стандартный объект Graphics, после чего производить рисование полученным объектом. Указать при этом, какой способ отладки графики вы предпочитаете, позволяет метод setDebugGraphicsOptions(). Однако базовый класс JComponent имеет встроенную поддержку класса DebugGraphics, и, чтобы включить для компонента Swing режим отладки графики, надо всего лишь указать способ отладки, вызвав тот же метод setDebugGraphicsOptions(), но на этот раз уже для самого компонента. После этого компонент и все его потомки будут использовать для рисования объект DebugGraphics. В качестве параметра методу setDebugGraphicsOptions() передается целое число, представляющее собой одну из констант, определенных в классе DebugGraphics, или комбинацию этих констант, составленную с помощью операции логического «ИЛИ» (табл. 4.1).

Таблица 4.1. Параметры метода setDebugGraphicsOptions()

Константа из класса

Действие

DebugGraphics

 

NONE_OPTION

Отключает режим отладки графики для используемого объекта

 

DebugGraphics

LOG_OPTION

Позволяет выводить диагностические сообщения обо всех про-

 

изводимых графических операциях (то есть обо всех вызы-

 

ваемых методах). По умолчанию сообщения выводятся в стан-

 

дартный поток вывода System.out, изменить поток вывода со-

 

общений позволяет статический метод класса DebugGraphics

 

setLogStream()

FLASH_OPTION

Установка этого флага приводит к тому, что все появляющееся на

 

экране рисуется в замедленном режиме и выделяется особым цве-

 

том, так что вы своими глазами можете проследить, как и где про-

 

исходит рисование. Настроить данный режим позволяют три стати-

 

ческих метода класса DebugGraphics: метод setFlashColor() устанав-

 

ливает цвет, которым выделяются все операции рисования, метод

 

setFlashCount() позволяет задать количество «мерцаний» при рисо-

 

вании, наконец, метод setFlashTime() используется для того, чтобы

 

указать задержку после каждой операции рисования. Имейте в виду,

 

данный режим отладки доступен только при отключении двойной бу-

 

феризации

BUFFERED_OPTION

В данном режиме операции рисования будут дополнительно вы-

 

водиться в отдельное окно, создаваемое классом DebugGraphics.

 

При этом учитываются остальные режимы отладки — они будут

 

действовать в созданном окне

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

//DebugPainting.java

//Демонстрация возможностей отладки графики в Swing import java.awt.*;

import javax.swing.*;

public class DebugPainting extends JFrame { public DebugPainting() {

super("DebugPainting");

104

ГЛАВА 4

//выход при закрытии окна setDefaultCloseOperation(EXIT_ON_CLOSE);

//добавляем рисующий компонент

PaintingComponent pc = new PaintingComponent(); add(pc);

//включаем для него отладку графики

RepaintManager.currentManager(pc). setDoubleBufferingEnabled(false);

pc.setDebugGraphicsOptions(DebugGraphics.LOG_OPTION | DebugGraphics.FLASH_OPTION);

DebugGraphics.setFlashTime(50);

DebugGraphics.setFlashCount(3);

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

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

}

// компонент, который что-то рисует class PaintingComponent extends JPanel {

public void paintComponent(Graphics g) { super.paintComponent(g);

// три простые фигуры g.setColor(Color.orange); g.fillRect(10, 10, 100, 100); g.setColor(Color.green); g.drawOval(50, 50, 50, 50); g.setColor(Color.blue); g.fillOval(100, 20, 50, 50);

}

}

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

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

}

}

В примере мы создаем небольшое окно, в которое добавляем собственный компонент, унаследованный от класса JPanel. Интересно, что если бы мы унаследовали компонент обычным образом, непосредственно от класса JComponent, система отладки графики работать бы с ним не стала. Связано это с тем, что у базового класса библиотеки нет собственного UI-представителя — ведь это абстрактный класс, его нельзя добавить в окно, и UIпредставитель ему просто не нужен. В обычных программах это не причиняет неудобств, однако система отладки графики отказывается работать с компонентом, у которого нет

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