Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Lab05_2012_draft_1

.pdf
Скачиваний:
44
Добавлен:
07.06.2015
Размер:
1.64 Mб
Скачать

Рис. 65. Напечатайте новое имя jGraphicPanelContainer и нажмите кнопку Refactor

Рис. 66. Изменения внесены как в объявление переменной, так и в методы

Переключение в режим Design отразит произошедшие изменения как в панели Inspector, так и в свойстве Variable Name (в панели, продемонстрированной на рис. 62).

Следующий шаг – помещение описанного нами наследника класса JPanel в этот контейнер. Сначала опишем соответствующую переменную в файле SimpleGraphicEditorView.java (рис. 67):

Рис. 67. Новая переменная jgp (можете дать ей другое имя)

Теперь нужно описать собственно добавление jgp в окно приложения. Для тех компонентов, которые Вы помещаете в приложение с помощью визуального редактора, это добавление описывается в секции generated code, которая, по сути, охватывает единственный довольно длинный метод initComponents(). Этот метод вызывается сразу же после вызова конструктора суперкласса в конструкторе SimpleGraphicEditorView (рис. 68):

Рис. 68. InitComponents() вызывается сразу после конструктора суперкласса.

Вообще говоря, было бы логично дополнить именно метод initComponent фрагментом кода, который описывает помещение jgp на панель jGraphicPanelContainer. Но NetBeans закрывает этот метод от редактирования. Поэтому все необходимые действия мы проведем после его вызова в конструкторе.

Рис. 69. Добавление объекта jgp

Прокомментируем приведенный выше код. Сначала вызывается конструктор JGraphicPanel, создающий объект jgp. Затем для него устанавливаются предпочтительные размеры, совпадающие с предпочтительными размерами его контейнера, и приподнятая граница. Далее создается менеджер расположения – представитель класса BoxLayout, в

качестве типа выкладки для него выбирается расположение «столбиком», по оси Y. Наконец, менеджер расположения устанавливается в качестве такового для контейнера, после чего в контейнер добавляется объект jgp, который (следующей строчкой) делается видимым.

Примечание.

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

java.awt.BorderLayout jGraphicPanelContainerLayout = new java.awt.BorderLayout(); jGraphicPanelContainer.setLayout(jGraphicPanelContainerLayout);

BorderLayout, задающий «полярное» расположение, помещает компонент в центр, если не указано иное. Также можно было бы использовать и FlowLayout, и другие менеджеры.

Убедиться в том, что поверх панели-контейнера теперь действительно находится еще одна панель, можно довольно простым образом. Измените цвет панели jGraphicPanelContainer на какой-либо другой, отличный от белого, – например, на темносиний (рис. 70).

Рис. 70. Изменим цвет панели-контейнера

Если теперь Вы запустите приложение, то присутствие контейнера будет выдавать разве что синяя рамка вокруг белой области (рис. 71). Рекомендуем вновь установить цвет контейнера белым (хотя, разумеется, можете выбрать любой понравившийся цвет).

Если Вы попробовали порисовать на панели вновь запущенного приложения, то заметили, что ничего не изменилось – несмотря на то, что панель, для которой были определены обработчики событий мыши, теперь перекрыта еще одной такой же панелью, рисовать по-прежнему можно. Заметим, что для компонентов JPanel (равно как и для большинства компонентов Swing) свойство opaque (непрозрачность) по умолчанию установлено в значение true. Если не вдаваться в подробности (хотя архитектура Swing и взаимодействие Swing-компонентов с AWT чрезвычайно интересная тема), то подобное поведение становится возможным потому, что «легковесные» компоненты Swing не взаимодействуют напрямую с операционной системой: эта работа – для «тяжеловесных»

компонентов AWT, которые, с одной стороны, получают от операционной системы все сообщения, а с другой – управляют компонентами Swing, для которых являются контейнерами.

Рис. 71. Темно-синяя рамка выдает присутствие контейнера

«Отключим» события мыши, обеспечивающие рисование, для панели-контейнера. Это можно сделать следующим образом. Переключитесь на вкладку Events для панели jGraphicPanelContainer, найдите событие mouseDragged и нажмите на кнопку с многоточием возле этого события. Появится диалоговое окно Handlers for mouseDragged (рис. 72). В этом окне указан единственный обработчик события перетаскивания мыши jGraphicPanelContainerMouseDragged, который и следует удалить, нажав в диалоговом окне кнопку Remove. После этого обработчик события исчезнет из диалогового окна, а после нажатия клавиши OK в нем – и из списка событий панели-контейнера. Точно таким же способом удалите обработчик события mousePressed. В результате список событий панели-контейнера должен стать пустым (рис. 73). Можете вновь запустить приложение на выполнение и удостовериться, что теперь рисовать на панели невозможно.

Следующий шаг будет состоять в определении событий мыши для панели jgp. Теперь нам придется проделать вручную все то, что делалось NetBeans при добавлении событий.

Рис. 72. Диалоговое окно для удаления обработчика события mouseDragged

Рис. 73. События, обеспечивавшие рисование, удалены из панели-контейнера.

§ 7. Добавление обработчиков событий

В начале § 5 рассказывалось, как организована обработка событий в Java, в том числе была упомянута возможность как самостоятельной реализации интерфейса слушателя, так и использование класса-адаптера. Продемонстрируем оба подхода.

Наиболее распространенный подход – реализация обработки событий во внутренних классах. Он удобен тем, что внутренние классы существуют в контексте класса внешнего – класса того компонента, в котором и происходит событие. Однако нельзя не отметить, что при этом происходит «смешивание» программной логики и

пользовательского интерфейса (особенно нежелательное в крупных и «долгоживущих» проектах). И, хотя реализация слушателей с помощью внутренних классов является наиболее частым, «классическим» приемом, иногда имеет смысл рассмотреть технику так называемой «фабрики слушателей» (см., например, [Портянкин], гл. 2). Впрочем, сейчас мы ограничимся классическим подходом.

Сначала напишем класс GP_MouseMotionListener, который непосредственно реализует интерфейс движения мыши. После описания заголовка будет выдано предупреждение, что данный класс не является абстрактным и не перекрывает абстрактный метод mouseMoved, и предложение реализовать все абстрактные методы (рис. 74):

Рис. 74. Класс, реализующий интерфейс, должен реализовать все его методы.

С предложением следует согласиться, и NetBeans сгенерирует следующий код (рис. 75):

Рис. 75. Заготовки методов событий перемещения мыши

Как можно видеть, среда поместила в заготовку класса методы не с пустым телом,

а с оператором throw, генерирующим исключение «неподдерживаемая операция». Разумеется, в окончательной версии этих операторов не будет. Однако, прежде чем мы удалим их, проведем небольшой эксперимент и посмотрим, как генерируются исключения. Для этого следует дописать в конструкторе еще одну строчку – добавление слушателя событий движения мыши (рис. 76).

Рис. 76. Добавление слушателя событий перемещения мыши

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

Рис. 77. Вывод сообщений о возникшей исключительной ситуации

Примечание.

При желании Вы можете открыть последовательно все ссылки и просмотреть «путь»

объекта исключительной ситуации по коду JDK.

Опишем теперь, что должно происходить при перемещении мыши. Если мышь перемещается не с нажатой клавишей, то никаких действий происходить не должно. Поэтому просто удалим оператор throw из метода mouseMoved, оставив его пустым. Если же мышь перемещается с нажатой клавишей, нужно будет написать код, в значительной степени повторяющий приведенный на рис. 44. Важным дополнением к этому коду будет параллельное рисование на «буферизированном изображении» (рис. 78).

Рис. 78. Измененные обработчики событий мыши

Протестировать работу этих обработчиков пока не получится, поскольку «буферизованное изображение» еще не создано (если Вы запустите приложение и попробуете «порисовать» на панели, то получите во вкладке output множество сообщений об ошибках, подобных изображенным на рис. 77 (однако исключение будет другое – NullPointerException). Действительно, переменная bufImage объявлена, но память для нее не выделена. Разумеется, это будет сделано, но сначала создадим обработчик еще одного события – mousePressed, нажатия на клавишу мыши.

Для этого определим еще один внутренний по отношению к JGraphicPanel класс – GP_MouseListener, который будет наследником класса MouseAdapter. Класс MouseAdapter объявлен как реализующий интерфейс MouseListener, он содержит только методы MouseListener, и каждый из этих методов имеет пустое тело. Удобство такого подхода, в частности, состоит в том, что в интерфейсе MouseAdapter описано 8 методов, из которых нам потребуется только один. Конечно, можно поступить с «лишними» 7 методами так, как было сделано выше для метода mouseMoved, а можно воспользоваться «заготовкой», предусмотренной разработчиками Java. Класс MouseAdapter нужно будет импортировать

(т.е. согласиться с предложением среды сделать это) (рис. 79).

Рис. 79. Предложения импорта. Если позволить среде добавлять их автоматически, пакеты и классы будут упорядочены по алфавиту.

После добавления заголовка метода среда предложит пометить его как override (переопределенный по отношению к базовому классу) (рис. 80). Это нужно делать, поскольку, в отличие от интерфейса, класс-адаптер не накладывает на программиста обязательств переопределить уже имеющиеся в нем «пустые» методы. Поэтому не так уж сложно допустить опечатку в названии метода (например, написать mousepressed) и потом тратить время (порой достаточно большое) на поиски причины невыполнения записанного в этом методе кода. Аннотация override позволяет контролировать «смысловую» правильность использования адаптеров.

Рис. 80. С предложением пометить метод как перекрытый, следует согласиться.

Тело метода mousePressed в классе JGraphicPanel будет таким же, как и в ранее определявшемся для объекта jDrawingPanel (который теперь переименован и служит контейнером для разработанной нами графической панели) (рис. 81). В задачу этого обработчика входит установка «начальной точки» рисунка.

Также нужно добавить слушателя событий мыши к классу JGraphicPanel. Как и для слушателя событий перемещения мыши, выполним добавление в конструкторе (рис. 82). Заметим, что при необходимости слушатели могут добавляться и удаляться «динамически», но в нашем случае поведение графической панели предполагается одинаковым в течение всего времени работы приложения. Поэтому слушатель добавляется при создании экземпляра класса и никогда не удаляется.

Рис. 81. Тело метода mousePressed

Рис. 82. Слушатель событий мыши добавлен

Вся работа по отрисовке swing-компонентов выполняется в методе paintComponent (для awt-компонентов – в методе paint). Каждый раз, когда изображение компонента необходимо обновить (когда он был перекрыт каким-то другим окном или приложение было свернуто), метод update обращается (в том числе) к методу paintComponent (оба эти метода определены в классе JComponent). Именно этот метод и будет переопределен таким образом, чтобы изображение «восстанавливалось» из буфера bufImage. Сам же объект bufImage будет создаваться при первом обращении к методу перерисовки компонента. После создания для bufImage следует выполнить заливку цветом фона. Однако такая операция может потребоваться и в других случаях (когда требуется очистить область изображения, например).

void preferImage(Graphics g){ g.setColor(backgroundColor); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(foregroundColor);

}

@Override

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

if (bufImage == null){ bufImage=(BufferedImage)createImage(getWidth(), getHeight()); preferImage(bufImage.getGraphics());

}

g.drawImage(bufImage, 0, 0, null);

}

Заливка bufImage вынесена в отдельный метод, чтобы при выполнении заданий (например, очистке экрана) не было необходимости писать этот фрагмент кода в другом

Соседние файлы в предмете Программирование на Java