Про swing - часть 1

Материал из Dom.

Перейти к: навигация, поиск

Содержание

[править] Введение в разработку приложений с графическим интерфейсом для java

Для разработки приложений с графическим интерфейсом под java существует несколько библиотек, лишь две из них: awt и swing были официально признаны и по сути разработаны авторами java – компанией sun. Еще известна библиотека swt – разработка IBM, также ведутся разработки множеством крупных компаний, а также существуют общественные движения, наиболее известен проект java desktop. Данная часть методического пособия будет посвящен использованию библиотеки swing, ее философии (а она есть и довольно отлична от знакомой вам по MFC), также будет уделено достаточное внимание созданию собственных элементов управления. JFC – это сокращение от Java Foundation Classes, впервые JFC появился в 1997 и некоторое время поставлялся отдельным пакетом от java core language api. В версиях jdk начиная от 1.2 jfc является уже интегрированным.

Врезка: история развития графической подсистемы java: В ранних - 1.0.x - версиях Java Development Kit активно использовались "тяжелые" компоненты AWT, тесно связанные конкретной аппаратной платформой на которой выполнялось приложение. Дальнейшее развертывание концепции "write once, run everywhere" (написать однажды, запускать везде) привело к тому, что в версии 1.1.x наметился переход к универсальным компонентам. Тогда появились "легкие" интерфейсные классы, в которых любой компонент на экране создается средствами Java с помощью графических классов и методов. Такого рода классы компонентов были объединены в библиотеку под названием Swing и доступны разработчикам в составе как JDK, так и отдельного продукта JFC (Java Foundation Classes). Причем для совместимости со старыми версиями JDK старые компоненты из AWT остались нетронутыми, хотя компания JavaSoft -, отвечающая за выпуск JDK, - рекомендует не смешивать в одной и той же программе старые и новые компоненты.


[править] Основные возможности и концепция JFC

ВозможностьОписание
Swing GUI ComponentsДля построения интерфейса разработано достаточно большое количество элементов управления: кнопки, списки, наборы закладок …
Pluggable Look-and-Feel SupportПриложения, которые используют Swing component-ы предлагают возможные стратегии того как будет выглять кнопка или текстовое поле а также то как она будет себя вести во взаимодействии с пользователем. В стандартной поставке jdk1.3 были стили для отображения интерфейса под windows/motif/metal и вы сами можете разработать стиль отображения: например в 10 версии borland jbuilder разработчики создали собственный стиль.
MVC – Model View ControllerМногие компоненты Swing реализованы по технологии MVC. Это означает, что компонент условно разбит на три части: модель (model), вид (view) и контроллер (controller). Каждая из этих частей строго и только свою задачу. Модель хранит важные данные компонента и обеспечивает программный интерфейс к ним. Вид занимается внешним видом изображения компонента и тесно связан с системой настройки интерфейса L&F. Контроллер же управляет компонентом в целом, получая сигналы от вида и уведомляя об изменениях модель компонента. Такое разграничение важно при смене L&F. Реализуя свой компонент для Swing, естественно будет сделать его пригодным для разных интерфейсов L&F. Технология MVC - это все, что вам нужно. Достаточно написать различные виды для Metal L&F, Motif L&F, Windows L&F и т. п. При этом модель и контроллер компонента меняются.
Java 2D APIСредства для рисования 2d графики с возможностью отправки изображения на печать, сохранения в файле.
Drag-and-Drop SupportПоддержка D&D как внутри java приложения так и между несколькими приложениями: java и java, java и созданные с помощью иных языков программы.
InternationalizationВозможность создавать приложения с поддержкой нескольких языков.

Все классы и интерфейсы swing расположены в пакете javax.swing или вложенных в состав его. Предыдущая версия библиотеки awt а также ряд классов носящих вспомогательный характер или не изменившихся находятся в пакете java.awt. так, что в начале вашего проекта всегда подключайте пакеты:

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

Примечание: способы поставки и распространения программ с использованием swing. Т.к. библиотека swing входит в стандартную поставку jre (java runtime environment), то особых проблем здесь нет. Другое дело, если ваше приложение использует посторонние библиотеки и их достаточно много, особенно явно эта проблема возникает при создании веб-приложений. Sun разработала технологию java web start (необходимые для ее работы компоненты устанавливаются вместе с jre). Идея заключается в том, что пользователь в интернете скачивает и запускает на выполнение маленький текстовой файлик с расширением jnlp, тут же подсистема java web start выкачивает с сайта sun или иных поставщиков необходимые библиотеки. Создает ярлык для запуска - приложение готово для работы.

[править] java 2d api

Начнем с простого: создадим приложение использующего средства 2d графики. Прежде всего необходимо создать основу – “холст” для рисования для этого следует создать диалоговое окно. Вам следует использовать либо класс Jframe (для немодальных окон) или Jdialog (соответствнно для модальных).

public static void main(String[] args) {
   JFrame jf = new JFrame("Пример диалогового окна");
   jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   jf.setSize(300 , 300); или так     jf.setSize(new Dimension (300 , 300));
   jf.setVisible(true);
}

Здесь создается объект "диалоговое окна". В качестве параметра конструктора указывается тест заголовока окна. Затем определяется что должно произойти при закрытии этого окна (когда нажали на маленький крестик в верхнем правом углу окошка). Возможны следующие варианты (все эти константы определены в классе WindowConstants):

DO_NOTHING_ON_CLOSE - ничего не делать. Окно не закроется и ничего не произойдет.

HIDE_ON_CLOSE – окно будет спрятано.

DISPOSE_ON_CLOSE – окно как и в предыдущем случае будет спрятано, но при этом и уничтожено (не насовсем, просто часть ресурсов операционной системы, которые использовались для отображения окна будут "освобождены"). Повторно окно можно показать с помощью вызова setVisible (хотя это может занять чуть больше времени чем "просто спрятать и не освобождать никаких ресурсов" - HIDE_ON_CLOSE).

EXIT_ON_CLOSE - по закрытию формы приложение завершается используя вызов System.exit(). Затем я установил размеры, используя при вызове setSize либо явно заданные размеры – ширина или высота или обертку в виде объекта Dimension (запомните этот класс, многие методы графической подсистемы указывают размеры чего-либо с помощью объектов этого класса).

Начнем с простого: на форме должна рисоваться некая геометрическая «композиция» при создании которой будет использовано максимальное количество методов.

Важно: модель обработки событий в стиле MFC с картами событий в swing не используется – вместо этого вы должны либо перекрывать методы классов-предков, либо использовать концепцию “слушателей” (начнем с первого способа).

JFrame jf = new JFrame("Пример диалогового окна"){
       public void paint(Graphics g) {
          super.paint(g);
          g.setColor(Color.BLUE); // устанавливаем текущий цвет
          g.drawLine(0,0 ,getSize().width , getSize().height);// рисуем линию через весь размер экрана
          g.setColor(new Color (128,128,128));
          g.drawOval(getSize().width/2 , getSize().height/2, 100, 50);
          g.setColor(new Color (0xFFFF00A0, true));
          g.drawPolygon(
                new Polygon(new int []{0,100,200,300}, new int []{0 , 200, 300, 100}, 4)
          );
 
          g.drawRoundRect(60 , 60 , 210 , 210 , 50 , 10);
          g.drawString("Hello From Java 2D", 50 , 50);
          g.fillRect(20 , 20 , 60 , 60);
       }
};

• При событии перерисовки вызывается виртуальный метод paint, в качестве единственного параметра ему передается значение объекта Graphics, который подобен контексту устройства CDC в стиле MFC.

• В примере используется метод getSize, возвращающий объект Dimension хранящий размеры окна.

• При задании цвета с помощью вызова

g.setColor(new Color (0xFFFF00A0, true));

цвет задается цвет в формате RGBA. Где последняя компонента – прозрачность. А второй параметр конструктора Color указывает на то, есть ли эта прозрачность или нет. Alpha компонент занимает биты с 24-31, красный компонент 16-23, зеленый - 8-15, и синий, соответственно, - 0-7. В общем случае, в составе класса Color есть варианты конструктора, в котором все вышеозначенные компоненты задаются явно и по отдельности:

Color(int r, int g, int b, int a).

При задании цвета учитывайте, то переменная какого типа используется. Если переменная целая, то значение должно быть в диапазоне от 0 до 255, если вещественная, то 0-1.

• При вызове метода fillRect выполняется закраска площади прямоугольника текущим цветом – фактически и рамка объекта и фон одинаковы.

Далее приводится справочное пособие из методов класса Graphics:

abstract  void	clearRect(int x, int y, int width, int height)

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

abstract  void	clipRect(int x, int y, int width, int height)

Изменяется текущая область отсечения.

abstract  void	copyArea(int x, int y, int width, int height, int dx, int dy)

Копирует область с указанными координатами (первый 4-е параметра) в указанную точку.

abstract  Graphics	create()

Создает новый контекст, являющейся копией существующего.

abstract  void	dispose()

Уничтожает и освобождает ресурсы контекста.

void	draw3DRect(int x, int y, int width, int height, boolean raised)

Рисуется прямоугольник с трехмерной рамкой. Raised – указывает на то, будет ли данный прямоугольник визуально "приподнят" или "утоплен".

abstract  void	drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)

Рисуем дугу элипса указанного размера. Дуга должна иметь начальный угол (startAngle) и само значение угла (arcAngle).

void	drawChars(char[] data, int offset, int length, int x, int y)

Рисуем текст.

abstract  boolean	drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) 
 abstract  boolean	drawImage(Image img, int x, int y, ImageObserver observer) 
 abstract  boolean	drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) 
 abstract  boolean	drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) 
 abstract  boolean	drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, 
int sx2, int sy2, Color bgcolor, ImageObserver observer)

Рисует изображение в указанной точке и указанного размера. Переменные начинающиеся на “d” ("destination") - приемник, а "s" (“source”), соотвественно, – источник изображения. Также задается фоновый цвет и объект извещатель. Незнакомая вам по MFC концепция объектов, которые заинтересованы в получении извещения о том, что какая-то операция была успешно выполнена. При необходимости выполняется масштабирование изображения.

abstract  void	drawLine(int x1, int y1, int x2, int y2)

Рисуем линию

abstract  void	drawOval(int x, int y, int width, int height)

Рисуем эллипс

abstract  void	drawPolygon(int[] xPoints, int[] yPoints, int nPoints)

Рисуем полигон. В качестве параметра метода drawPolygon указывается массив точек по координатам "x" и "y".

void	drawPolygon(Polygon p)

Еще один полигон

abstract  void	drawPolyline(int[] xPoints, int[] yPoints, int nPoints)

Полилиния – похоже на полигон. Но помните, что полигон всегда замкнут - полилиния же не обязательно.

void	drawRect(int x, int y, int width, int height)

Прямоугольник

abstract  void	drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)

Прямоугольник с закругленными краями:

Изображение:swing_1_1.png

abstract  void	drawString(AttributedCharacterIterator iterator, int x, int y)

Рисуем текст

abstract  void	drawString(String str, int x, int y)

Рисуем текст

void	fill3DRect(int x, int y, int width, int height, boolean raised)

Трехмерный прямоугольник с закругленными краями.


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

abstract  Shape	getClip()

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

abstract  Rectangle	getClipBounds()
Получаем область отсечения, но при этом берем ее не истинный размер, а “описывающий” прямоугольник.

abstract  Color	getColor()

Получаем значение текущего цвета.

abstract  Font	getFont()

Текущий шрифт.

FontMetrics	getFontMetrics()

Метрики шрифта. Объект FontMetrixs используется для получения характеристик шрифта.

Изображение:swing_1_2.png

g.setColor (Color.BLUE);
GraphicsEnvironment gEnv =
  GraphicsEnvironment.getLocalGraphicsEnvironment();
String envfonts[] = gEnv.getAvailableFontFamilyNames();
setTitle("Общее количество шрифтов: " + envfonts.length);
int w = getSize().width, h = getSize().height;
int start_h = 0;
for (int i = 0; i < envfonts.length; i++){
  Font f =  new Font (envfonts[i] , Font.ITALIC, 12);
  g.setFont(f);
 
  FontMetrics metrics = g.getFontMetrics();
  int width = metrics.stringWidth( envfonts[i] );
  int height = metrics.getLeading() + metrics.getHeight();
  g.drawString( envfonts[i], w/2-width/2, start_h + height );
  start_h += height;
}

abstract  FontMetrics	getFontMetrics(Font f)

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

abstract  void	setClip(int x, int y, int width, int height)

Устанавливаем текущую область отсечения.

abstract  void	setClip(Shape clip)

Тоже, что и предыдущий метод. Но область отсечения задается с помощью Shape.

abstract  void	setColor(Color c)

Установить значение текущего цвета.

abstract  void	setFont(Font font)

Установить значение текущего шрифта.

abstract  void	setPaintMode()

Устанавливаем режим рисования – то какая операция будет производиться при совмещении пикселя старого и нового цвета. Здесь режим затирания старого цвета на новый.

abstract  void	setXORMode(Color c1)

Метод подобен предыдущему, но здесь используется режим XOR, и в качестве цвета XOR режима используется явно задаваемый с1.

abstract  void	translate(int x, int y)

Перенести систему координат (точнее ее начало)T в точку x,y в текущей системе координат разумеется.

g.drawRect(10 , 10 , 100 , 100); // рисуем квадрат со стороной 100 px.
 g.translate(10 , 10); // перемещаем систему координат в точку 10, 10
 g.drawRect(0,0,100,100);// рисуем квадрат совпадающий с ранее нарисованным

В практике объект Graphics уже давно не используется, и новых версиях jdk всегда, в качестве параметра для метода paint, передается объект типа Graphics2D являющийся наследником от Graphics, но обладающим рядом полезных возможностей. В общем случае, вы в коде paint должны преобразовать Graphics к нужному типу.

public void Paint (Graphics g) {
  Graphics2D g2 = (Graphics2D) g;
   // что-то рисуем
}

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

Изображение:swing_1_3.pngПараметр стиль карандаша – все о том, как будет рисоваться граница линии объекта. Вы можете управлять толщиной линии, и ее стилем. Например, будет ли она сплошной или точечной.
Изображение:swing_1_4.pngПараметр fill управляет тем, как будет выполняться заливка фона фигуры. Вы можете выбрать будет ли заливка сплошной, или же использован шаблон.
Изображение:swing_1_5.pngСтиль композиции – то, что будет происходить при наложении изображения поверх существующего.
Изображение:swing_1_6.pngПараметр трансформации transform – управляет тем? какие преобразования будут выполняться при отображении объекта. Вы можете задать здесь смещение системы координат, ее вращение, масштабирование.
Изображение:swing_1_7.pngClip – отсечение, какая часть Graphics2D будет доступна для рисования.
Изображение:swing_1_8.pngШрифт
Изображение:swing_1_9.pngПараметры рендеринга. При формирования итогового изображения, например, есть возможность управлять сглаживанием (antialiasing).

Для того чтобы изменять все эти перечисленные атрибуты следует использовать методы Graphics2D:

•	setStroke 
•	setPaint 
•	setComposite 
•	setTransform 
•	setClip 
•	setFont 
•	setRenderingHints 

Примеры: в которых рисуются различные параметры для атрибутов рисования:

Изображение:swing_1_10.png

g.setColor(Color.BLUE);
 BasicStroke dashed = new BasicStroke(5.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
    10.0f, new float []{10.0f, 20.f, 50.0f}, 0.0f);
 Graphics2D g2 = (Graphics2D) g;
 g2.setStroke(dashed);
 g2.draw(new Ellipse2D.Double(100 , 100 , 200 , 200));

Здесь используется полная версия конструктора BasicStroke: первый параметр – толщина линии. Второй параметр - стиль линии на окончании.

Изображение:swing_1_11.png

Третий параметр – тип линии используемой при соединении двух фрагментов.

Изображение:swing_1_12.png

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

Следующий параметр - начальное состояние линии штриховки.

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

g2.setPaint(Color.red);
 g2.fill(new Rectangle2D.Double(20, 20,100, 100));
 // и второй – с градиентом
 GradientPaint redtowhite = new GradientPaint(20,20,Color.RED,20+100, 20+100,Color.WHITE);
 g2.setPaint(redtowhite);
 g2.fill(new Rectangle2D.Double(20, 20,100, 100));

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

BufferedImage bum = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
 Image img = getToolkit().getImage("C:\\CA_LIC\\boat.gif");
 MediaTracker tracker = new MediaTracker(this);
 tracker.addImage(img, 0);
 try {
  tracker.waitForID(0 , 2000);
 } 
 catch (InterruptedException e) {
    e.printStackTrace();
 }
 Image img2 = img.getScaledInstance(100 , -100 , Image.SCALE_SMOOTH) ;
 tracker.addImage(img2 , 1);
 try {
   tracker.waitForID(1 , 2000);
 }
 catch (InterruptedException e) {
   e.printStackTrace();
 }
 
 bum.getGraphics().drawImage(img2, 0 , 0 , this );
 TexturePaint tpaint = new TexturePaint(bum , new Rectangle2D.Double(10, 10, 20 , 20));
 Graphics2D g2 = (Graphics2D )g;
 g2.setPaint(tpaint);
 g2.fill(new RoundRectangle2D.Double(50, 50, 200, 200, 10, 10));

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

Так как исторически java разрабатывалась для того, чтобы работать в среде intenet, то предполагалось, что загрузка изображения может занимать значительное время и даже быть неуспешной. Поэтому, когда мы работаем с изображениями возникает вопрос о том, загружено оно или нет. Базовым классом для всех изображений служит класс Image. Для изображений которые уже загружены используется класс BufferedImage. Однако перед тем, как создать данное изображение, необходимо дождаться его загрузки. Для этого используется объект типа MediaTracker. При создании которого в качестве параметра конструтора следует указать любой визуальный элемент управления. Затем необходимо добавить изображение в список ожидающих загрузки, давая при этому ему порядковый учетный номер, и затем ожидаем загрузки. Следующий шаг (более для демонстрации, чем для необходимости) – масштабирование изображения. Для этого используем вызов:

img.getScaledInstance(100 , -100 , Image.SCALE_SMOOTH);

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

Следующим шагом будет управление параметрами рисования – рендеринга. Все использованные параметры (константы) задаются в классе RenderingHints.

Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(