Java. Полное руководство. 8-е издание. С. Н. Тригуб Перевод с английского и редакция
Скачать 25.04 Mb.
|
import javax.swing.*; class SwingDemo { SwingDemo() { // Создание нового контейнера JFrame. JFrame jfrm = new JFrame("A Simple Swing Application"); // JFrame jfrm = new JF r a m e (Простое приложение Swing"); // Задаем фрейму исходный размер jfrm.setSize(275, 100); // Прекращаем работу программы, если пользователь // закрывает приложение frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Создаем метку с текстом jlab = new J L a b e l ("Swing means powerful GUIs."); // JLabel jlab = new J L a b e l ("Swing означает мощный GUI."); // Добавляем метку на панель содержимого j frm.add(jlab); // Отображаем фрейм, Глава 30. Введение в библиотеку Swing 9 5 9 public static void main(String a r g s []) { 9 6 0 Часть III. Разработка программного обеспечения с использованием Java // Создаем фрейм в потоке диспетчеризации событий SwingUtllities.invokeLater(new Runnable() { public void run() { new SwingDemo(); } }) Программы Swing компилируются и выполняются точно таким же способом, как и остальные приложения Java. Поэтому чтобы скомпилировать эту программу, можно воспользоваться следующей командной строкой avac SwingDemo.j Чтобы запустить программу, нужно выполнить следующую команду Когда программа начнет работу, она создаст окно, показанное на рис. Поскольку программа SwingDemo иллюстрирует несколько основополагающих концепций библиотеки Swing, рассмотрим ее тщательно, строка за строкой. Программа начинается с импорта пакета j avax. swing. Как уже упоминалось, этот пакет содержит компоненты, определяемые библиотекой Swing. Например, пакет j avax. swing определяет классы, реализующие метки, кнопки, текстовые элементы управления и меню. Он будет включен вовсе программы, использующие библиотеку Swing. A S im ple S w in g A p p lic a tio n | _ Swing means powerful Рис. 30.1. Окно, созданное программой Затем объявляется класс SwingDemo и конструктор для этого класса. Конструктор — это метод, в котором выполняется большинство действий программы. Он начинается с создания экземпляра класса JFrame с помощью следующей строки кода jfrm = new JFrame("A Simple Swing В результате будет создан контейнер j f rm, определяющий прямоугольное окно со строкой заголовка, кнопками закрытия, свертывания, развертывания и восстановления, а также с системным меню. Таким образом, программа создает стандартное окно верхнего уровня. Конструктору передается заголовок окна. Затем задаются размеры окна с помощью следующего оператора, Метод setSize () (он наследуется классом JFrame от класса AWT Component) задает размеры окна, определяемые в пикселях. Он имеет такую форму setSize(int ширина высота) В этом примере ширина окна ширина равняется 275 пикселям, а высота высота - 1 0 0 По умолчанию, когда закрывается окно верхнего уровня (например, когда пользователь щелкает на кнопке закрытия, окно удаляется с экрана, но работа приложения не прекращается. Несмотря на то что это'поведение в некоторых си Глава 30. Введение в библиотеку Swing 9 61 туациях является полезным, для большинства приложений оно не подходит. Чаще всего при закрытии окна верхнего уровня нужно будет просто прекращать работу всего приложения. Это можно сделать двумя способами. Самый простой из них — вызов метода setDef aultCloseOperat ion () , что и делается в программе Теперь работа всего приложения будет прекращаться при закрытии окна. Общая форма метода setDef aultCloseOperation () выглядит так setDefaultCloseOperation(int ч т о) Значение параметра что определяет, что происходит при закрытии окна. Помимо значения JFrame . EXIT__0N_CL0SE, доступно еще несколько значений В их именах отражены выполняемые действия. Эти константы объявлены в классе WindowConstants, который является интерфейсом, объявленным в пакете javax. swing, реализуемым контейнером класса JFrame. JLabel jlab = new JL a b e l ("Swing means powerful Класс JLabel — самый простой и легкий в использовании компонент, так как он не принимает от пользователя входных данных. Он просто отображает информацию, которая может представлять текст, значок или их комбинацию. Метка, созданная программой, содержит только текст, который передается ее конструктору. Следующая строка кода добавляет метку в панель содержимого фрейма. j Как было сказано ранее, все контейнеры верхнего уровня имеют панель содержимого, в которой размещаются компоненты. Таким образом, чтобы добавить компонент во фрейм, нужно добавить его в панель содержимого фрейма. Это можно сделать, вызвав метод add () для ссылки на экземпляр класса JFrame в данном случае контейнер j f rm). Общая форма метода add () показана ниже add(Component компаратор) Метод add () наследуется классом JFrame от класса Container библиотеки По умолчанию панель содержимого, связанная с компонентом класса JFrame, использует граничную компоновку. Только что показанный вариант метода add () добавляет метку и помещает ее в центре. Другие варианты метода add () позволяют задать одну из граничных областей. Когда компонент добавляется в центр, его размер подгоняется автоматически таким образом, чтобы компонент смог уместиться в центре. Прежде чем продолжить, нужно сделать одно важное замечание. До выхода комплекта JDK 5 при добавлении компонента на панель содержимого нельзя было вызывать метод add () непосредственно для экземпляра класса JFrame. Вместо этого нужно было вызывать метод add () для панели содержимого объекта класса JFrame. Панель содержимого можно было получить в результате вызова метода getContentPane () для экземпляра класса JFrame. Метод getContentPane () показан ниже Класс Container получает ссылку на окно содержимого. После этого осуществляется вызов метода add () по этой ссылке для добавления компонента на панель содержимого. Таким образом, чтобы добавить метку j lab в контейнер j f rm, раньше нужно было использовать следующий оператор d d (jlab); // старый стиль 3ak 3030 9 6 2 Часть III. Разработка программного обеспечения с использованием Здесь метод get Cont ent Pane () сначала получает ссылку на панель содержимого, после чего метод add С) добавляет компонент в контейнер, присоединенный к этому окну. Эту же процедуру нужно было выполнять для вызова метода remove () , когда требовалось удалить компонент, и метода setLayout () , чтобы задать диспетчер компоновки для окна содержимого. В коде, написанном на языке Java до версии 5.0, нередко встречаются вызовы метода get Cont ent Pane (). Однако теперь использовать этот метод больше ненужно. Можно просто вызывать методы add ( ) , remove () и setLayout () непосредственно для объекта класса JFrame, так как они были изменены специально для того, чтобы автоматически работать с окном содержимого. Последний оператор в конструкторе класса SwingDemo нужен для того, чтобы сделать окно видимым. jfrm.setVisible(true); Метод setVisible () наследуется от класса библиотеки AWT Component. Если его аргумент будет равен true, то окно будет отображаться. В противном случае оно будет скрыто. По умолчанию контейнер класса JFrame является невидимым, поэтому, чтобы показать его, нужно вызвать метод setVisible (Внутри метода main () создается объект класса SwingDemo, который отображает окно и метку. Обратите внимание на то, что конструктор класса SwingDemo вызывается с помощью следующих трех строк кода Runnable() { public void r u n () { new SwingDemo(); } }) Выполнение этой последовательности кода приводит к созданию объекта класса SwingDemo в потоке диспетчеризации событий, а не в главном потоке приложения. И вот почему. В общем случае программы Swing управляются событиями. Например, когда пользователь взаимодействует с компонентом, происходит событие. Извещение о событии передается в приложение вызовом обработчика событий, определенного в приложении. Однако обработчик выполняется в потоке диспетчеризации событий, поддерживаемом библиотекой Swing, а не в главном потоке приложения. Таким образом, хотя обработчики событий и определены в программе, они вызываются в потоке, который не был создан вашей программой. Чтобы избежать этой проблемы (включая вероятность возникновения взаимной блокировки, все компоненты GUI библиотеки Swing нужно создавать и обновлять из потока диспетчеризации событий, а не из главного потока приложения. А метод add () выполняется в главном потоке. Таким образом, метод main () не может напрямую наследовать объект класса SwingDemo. Наоборот, он должен создать объект класса, реализующего интерфейс Runnable, который выполняется в потоке диспетчеризации событий, и заставить этот объект создать Чтобы код интерфейса GUI можно было создать в потоке диспетчеризации событий, необходимо использовать один из двух методов, определенных в классе SwingUt ilities. Это методы invokeLater ( ) и invokeAndWait (). static void invokeLater(Runnable объект void invokeAndWait(Runnable объект InterruptedException, Здесь объект — это объект класса, реализующего интерфейс Runnable, метод run () которого будет вызываться потоком диспетчеризации событий. Единственное различие между этими двумя методами заключается в том, что метод invokeLater () возвращает результат немедленно, а метод invokeAndWait () ожидает возврата результата метода obj run (). Вы можете использовать эти методы для вызова ме Глава 30. Введение в библиотеку Swing 9 6 3 тода, создающего GUI для вашего приложения Swing, или использовать их каждый раз, когда вам нужно будет изменить состояние GUI из кода, не выполняющегося в потоке диспетчеризации событий. Как правило, вам нужно будет использовать метод in v o k e L a te r (), как это было в предыдущей программе. А при создании исходного GUI для аплета вам понадобится метод invokeA ndW ait (Обработка событий В предыдущем примере была показана базовая форма программы Swing, однако в ней не хватает одной важной части — обработки событий. Поскольку метка JLabel не принимает входных данных от пользователя, она не создает извещений о событиях, поэтому и обработка событий не была нужна. Однако остальные компоненты библиотеки Swing реагируют на вводимые пользователем данные, вследствие чего возникает необходимость в обработке событий, которые возникают в результате таких взаимодействий. Например, событие происходит тогда, когда таймер завершает отсчет. В любом случае обработка событий является большой частью любого приложения, построенного на основе библиотеки Механизм обработки событий, используемый в библиотеке Swing, ничем не отличается от механизма, применяемого в библиотеке AWT. Этот подход называется моделью делегирования событий, ион рассматривался в главе 23. Во многих случаях библиотека Swing использует те же события, что и библиотека AWT, и эти события определены в пакете java . awt. event. События, являющиеся специфическими для библиотеки Swing, определены в пакете j avax. swing. Несмотря на то что события обрабатываются в библиотеке Swing точно также, как ив библиотеке AWT, будет полезно рассмотреть небольшой и простой пример. В следующей программе обрабатывается событие, созданное кнопкой из класса библиотеки Swing. Результат показан на рис. 30.2. ШЩ м Рис. 30.2. Результат выполнения программы EventDemo // Обработка события в программе Swing. import j a v a .a w t .*; import j a v a .a w t .event.*; import javax.swing.*; class EventDemo { JLabel jlab; EventDemo() { // Создание нового контейнера JFrame. JFrame jfrm = new JFrame("An Event Example"); // JFrame jfrm = new JF r a m e (Пример обработки событий 9 6 4 Часть III. Разработка программного обеспечения с использованием Java // Определение класса FlowLayout для диспетчера компоновки j frm.setLayout(new FlowLayout()); // Установка исходных размеров фрейма, jfrm.setSize(220, 90); // Прекращение работы программы, если пользователь // закрывает приложение frm.setDefaultCloseOperation(JFrame.EXIT_0N_CL0SE); // Создаем две кнопки jbtnAlpha = new JButton("Alpha"); JButton jbtnBeta = new JButton("Beta"); // Добавляем слушатель действий для Alpha. jbtnAlpha.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { jlab.setText("Alpha was pressed."); // Нажата кнопка Alpha."); } }) ; // Добавляем слушатель действий для Beta. jbtnBeta.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { j lab.setText("Beta was pressed."); // Нажата кнопка Beta."); } }) ; // Добавляем кнопки в панель содержимого jfrm.add(jbtnAlpha); jfrm.add(jbtnBeta) ; // Создаем текстовую метку = new JLab e l ("Press a button."); // jlab = new JLab e l (Нажмите кнопку Добавляем метку в панель содержимого j frm.add(jlab); // Отображаем фрейм, jfrm.setVisible(true); } public static void main(String a r g s []) { // Создаем фрейм в потоке диспетчеризации событий Runnable() { public void run() { new EventDemo(); } }) Для начала обратите внимание на то, что программа теперь импортирует пакеты j a v a . a w t и j a v a . a w t . event. Пакет j a v a . a w t нужен потому, что в нем содержится класс F l o w L a y o u t , поддерживающий стандартный диспетчер компоновки потоков, который используется для размещения компонентов во фрейме. (Диспетчеры компоновки рассматривались в главе 25.) Пакет j a v a . a w t . e v e n t необходим потому, что он определяет интерфейс A c t i o n L i s t e n e r и класс A c t i o n E v e n t . Глава 30. Введение в библиотеку Swing 9 6 Конструктор класса EventDemo начинает работу с создания контейнера класса JFrame по имени jfrm. Затем ой устанавливает диспетчер компоновки класса FlowLayout для панели содержимого контейнера jfrm. Вспомните, что по умолчанию панель содержимого использует диспетчер компоновки класса BorderLayout. Однако для данного примера удобнее применить именно класс FlowLayout. Обратите внимание на то, что диспетчер класса FlowLayout назначается с помощью следующего оператора frm.setLayout(new F l o w L a y o u t (Как уже упоминалось, раньше приходилось явным образом вызывать метод getContentPane () , чтобы задать диспетчер компоновки для окна содержимого. После выхода комплекта JDK 5 этого делать не нужно. После определения размеров и стандартной операции при закрытии метод EventDemo () создает две кнопки, как показано ниже Button jbtnAlpha = new J B u t t o n ("Al p h a "); JButton jbtnBeta = new J B u t t o n ("Bet a " Первая кнопка будет содержать текста вторая — "Beta". Кнопки библиотеки Swing являются экземплярами класса JButton. Класс JButton предлагает несколько конструкторов. Одним из используемых здесь конструкторов является следующий B u t t o n (String сообщение) Параметр сообщение определяет строку, которая будет отображаться внутри кнопки. П ри щелчке на кнопке происходит событие класса A c tio n E v e n t. Таким образом, класс J B u t to n предлагает метод a d d A c t i o n L i s t e n e r ( ), который используется для добавления слушателя событий. (Класс J B u t to n предлагает также метод r e m o v e A c ti o n L i s te n e r () для удаления слушателя, однако он в этой программе не используется) Как было сказано в главе 23, интерфейс A c t i o n L i s t e n e r определяет только один метод — a c t i o n P e r fo rm e d (). Чтобы вам было легче его вспомнить, приведем его еще раз o i d a c t i onPerformed(ActionEvent Этот метод вызывается в результате щелчка на кнопке. Другими словами, это обработчик события, который вызывается в случае события при щелчке на кнопке. После этого показанный ниже код добавляет слушатели для событий действий с кнопками Добавляем слушатель событий действий для кнопки Alpha. j b t n A l p h a .addActi o n L i s t e n e r ( n e w A c t i o n L i s t e n e r () { public void a c t i o nPerformed(ActionEvent ae) { j l a b .s e t T e x t ("Alpha was pressed."); } }) ; // Добавляем слушатель событий действий для кнопки Beta. j b t n B e t a .addActionL i s t e n e r ( n e w A c t i o n L i s t e n e r () { public void a c t i o nPerformed(ActionEvent ae) { j l a b .s e t T e x t ("Beta was pressed."); } }) Здесь анонимные внутренние классы используются для того, чтобы предоставить обработчики событий двум кнопкам. Всякий раз при щелчке на кнопке строка, отображенная в метке j lab, изменяется в зависимости оттого, на какой кнопке был произведен щелчок. Затем кнопки добавляются в панель содержимого 9 6 6 Часть III. Разработка программного обеспечения с использованием И наконец, в панель содержимого добавляется метка j la b , и окно становится видимым. Когда вы запустите программу, то при каждом щелчке на кнопке в метке будет отображаться сообщение, указывающее, на какой из кнопок был щелчок. И еще одно замечание не забывайте о том, что все обработчики событий наподобие метода a c t i o n P e r f orm ed () вызываются в потоке диспетчеризации событий. Таким образом, обработчик событий должен быстро дать результат, чтобы не допустить замедления работы приложения. Если вашему приложению нужно сделать что-то такое, для чего потребуется много времени и что будет расцениваться как событие, то оно должно использовать отдельный поток. Еще одним типом программы, которая обычно использует классы библиотеки Swing, является аплет. Аплеты, созданные на основе библиотеки Swing, подобны аплетам, созданным на основе библиотеки AWT, ноу них есть одно существенное отличие — аплет Swing расширяет класса не класс Applet. Таким образом, класс JApplet включает все функциональные возможности класса Applet и добавляет поддержку библиотеки Swing. Класс JApplet является контейнером библиотеки Swing верхнего уровня это значит, что он не является наследником класса JComponent. Так как класс JApplet является контейнером верхнего уровня, он включает различные панели, описанные ранее. Это означает, что все компоненты добавляются в панель содержимого класса JApplet точно также, как и компоненты, добавляемые в панель содержимого JFrame. Аплеты Swing используют те же методы обеспечения жизненного цикла, которые были описаны в главе 22, — init(), start (), stop() и destroy (). Естественно, вам нужно переопределять только те методы, которые будут нужны вашему аплету. Процесс рисования в библиотеке Swing осуществляется иначе, чем в библиотеке AWT, и аплет Swing обычно не переопределяет метод paint (). О рисовании с использованием библиотеки Swing поговорим далее в этой главе.) Еще один нюанс все взаимодействия с компонентами в библиотеке Swing должны производиться в потоке диспетчеризации событий, о чем было сказано в предыдущем разделе. Это относится ко всем программам Ниже представлен пример аплета Swing. Он снабжен теми же функциями, что и предыдущее приложение, но только является аплетом. На рис. 30.3 показан аплет, выполняемый в приложении Создание аплета Swing Applet Alpha; Beta Alpha was pressed. Applet Рис. 30.3. Результат работы аплета Swing Глава 30. Введение в библиотеку Swing 9 6 7 // Простой аплет, основанный на библиотеке Swing import javax.swing.*; import java.awt.*; import j a v a .a w t Этот код HTML можно использовать для запуска аплета: */ public class MySwingApplet extends JApplet { JButton jbtnAlpha; JButton jbtnBeta; JLabel jlab; // Инициализация аплета, public void i n i t () { try { SwingUtilities.invokeAndWait(new Runnable () { public void r u n () { m a keGUI(); // инициализация GUI } }) ; } catch(Exception exc) { System.out.println("Can't create because of " + e x c ) ; I I Невозможно создать из-за "+ e x c ) ; } } // Этому аплету ненужно переопределять методы sta r t (), stop() // или destroy(). // Настройка и инициализация GUI. private void m a k eGUI() { // Настройка аплета для использования компоновки потоков setLayout(new FlowLayout()); // Создание двух кнопок jbtnAlpha = new JButton("Alpha"); jbtnBeta = new JButton("Beta"); // Добавление слушателя событий действия для кнопки Alpha. jbtnAlpha.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent le) { jlab.setText("Alpha was pressed."); // Нажата кнопка Alpha."); } }) ; // Добавление слушателя событий действия для кнопки Beta. jbtnBeta.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent le) { jlab.setText("Beta was pressed."); // Нажата кнопка Beta."); }) ; } 9 6 8 Часть III. Разработка программного обеспечения с использованием Java // Добавление кнопок в панель содержимого a d d (jbtnAlpha); add(jbtnBeta); // Создание текстовой метки = new JLa b e l ("Press a button."); // jlab = new JLa b e l (Нажмите кнопку Добавление метки в панель содержимого Необходимо сделать пару важных замечаний касательно аплетов. Во-первых, класс MySwingApplet расширяет класс JApplet. Как мы уже говорили, все аплеты, основанные на библиотеке Swing, расширяют класса не класс Applet. Во-вторых, метод init () инициализирует компоненты библиотеки Swing в потоке диспетчеризации событий, устанавливая вызов метода makeGUI () . Обратите внимание на то, что для этого используется метода не in vokeLater (). Аплеты должны использовать метод invokeAndWait () потому, что метод init () не должен возвращать результат до тех пор, пока не будет выполнен весь процесс инициализации. По сути, метод start () нельзя вызывать прежДе, чем закончится инициализация это значит, что нужно полностью создать Внутри метода makeGUI () создаются две кнопки и метка, а к кнопкам добавляются слушатели событий действия. Наконец, компоненты добавляются в панель содержимого. Несмотря на то что пример является довольно простым, этот подход нужно применять при создании любого GUI библиотеки Swing, который будет использован аплетом. Рисование с использованием библиотеки Несмотря на то что компоненты библиотеки Swing очень функциональны, вы не ограничены только их использованием, поскольку библиотека Swing позволяет также выводить информацию непосредственно в область отображения фрейма, панели или одного из компонентов библиотеки Swing, такого как метка класса JLabel. Хотя во многих случаях использования библиотеки Swing не производится рисование прямо на поверхности компонента, это можно делать в приложениях, где подобное необходимо. Чтобы вывести данные прямо на поверхность компонента, нужно использовать один или несколько методов рисования, определенных в библиотеке AWT, вроде drawLine ( ) или drawRect () . Таким образом, большинство технологий и методов, описанных в главе 24, можно применять и к библиотеке Swing. С другой стороны, между ними есть очень важные отличия, поэтому обо всем этом мы и поговорим подробно в текущем разделе. Основы рисования Подход к рисованию с использованием библиотеки Swing базируется наори гинальном механизме, построенном на основе библиотеки AWT, однако библиотека Swing позволяет более качественно управлять этим процессом. Прежде чем приступить к изучению специфики рисования в библиотеке Swing, будет полезно пересмотреть механизм рисования в библиотеке AWT. Глава 30. Введение в библиотеку Swing 9 6 Класс Component библиотеки AWT предлагает метод paint () , который используется для рисования выходных данных прямо на поверхности компонента. Как правило, метод paint () не вызывается программой. (В действительности, вызывать этот метод написанной вами программой придется в очень редких случаях) Вместо этого метод paint () вызывается средой выполнения в процессе визуализации компонента. Такая ситуация может возникнуть в силу ряда причин. Например, окно, в котором отображается компонент, может быть перекрыто другим окном, а затем вновь появиться на экране. Или же окно может быть свернуто, а затем восстановлено. Метод paint () вызывается также в тех случаях, когда программа начинает свою работу. При написании кода, основанного на библиотеке AWT, приложение будет переопределять метод paint ( ), когда ему будет необходимо вывести данные прямо на поверхности компонента. Поскольку класс JComponent унаследован от класса Component, все облегченные компоненты библиотеки Swing получают метод paint (). Однако вам не придется переопределять его, чтобы выводить информацию непосредственно на поверхности компонента. Дело в том, что при рисовании с использованием библиотеки Swing применяется более изощренный цодход, который включает три различных метода paintComponent ( ) , paint Border () и paint Children ( Эти методы рисуют заданную часть компонента и делят процесс рисования натри разных логических действия. В облегченном компоненте исходный метод paint () библиотеки AWT просто выполняет вызовы этих методов в показанном только что порядке. Чтобы нарисовать поверхность компонента библиотеки Swing, вы должны будете создать подкласс компонента, а затем переопределить его метод paint Component () . Этот метод отвечает за прорисовку внутренней части компонента. Как правило, остальные два метода рисования вы переопределять не будете. Переопределяя метод paintComponent () , вы сначала должны вызвать метод super paintComponent ( ), чтобы задействовать часть суперкласса процесса рисования. (Этого делать ненужно лишь в том случае, если вы вручную управляете способом отображения компонента) После этого можно вывести данные, которые вы хотите отобразить. Ниже показан метод paintComponent (). protected void paintComponent(Graphics В параметре g указывается графическое содержимое выходных данных. Чтобы программно нарисовать компонент, нужно вызвать метод repaint (). Он работает в библиотеке Swing точно также, как в библиотеке AWT. Метод repaint () определен в классе Component. Если его вызвать, система вызывает метод paint () сразу же, как только представляется для этого возможность. Поскольку процесс рисования отнимает довольно много времени, этот механизм позволяет системе времени выполнения мгновенно задерживать рисование до тех пор, пока, например, не завершится выполнение другой задачи, имеющей более высокий приоритет. Естественно, в библиотеке Swing вызов метода paint () приводит к вызову метода paintComponent () . Таким образом, чтобы вывести данные на поверхность компонента, ваша программа будет хранить эти данные до тех пор, пока не будет вызван метод paintComponent (). Внутри переопределенного метода paintComponent () выбудете рисовать хранимые данные. Вычисление области рисования Во время рисования на поверхности компонента нужно тщательно ограничить область рисования выходных данных, находящуюся внутри границ компонента. Хотя библиотека Swing автоматически отсекает любые выходные данные, выходящие за 9 7 0 Часть III. Разработка программного обеспечения с использованием границы компонента, может получиться так, что рисование будет выполняться прямо на границе, которая затем будет заменена при перерисовке. Чтобы не допустить этого, следует вычислить область рисования в пределах компонента. Эта область определяется так берется текущий размер компонента и из него вычитается пространство, занятое его границами. Таким образом, прежде чем нарисовать компонент, следует сначала узнать ширину границы, а затем уже подгонять область рисования. Чтобы узнать ширину границы, вызовите метод getlnsets(). Insets Этот метод определен в классе Container и переопределяется в классе JComponent. Он возвращает объект класса Insets, содержащий размеры границ. Значения можно получить с помощью следующих полей top; int bottom; int left; int Впоследствии эти значения применяются для вычисления области рисования с учетом ширины и высоты компонента. Ширину и высоту компонента можно узнать, обратившись к методами Вычитая значения границ, можно получить ширину и высоту области, в которой будет выполняться рисование. Пример рисования Сейчас рассмотрим программу, в которой будут реализованы рассмотренные методы. Она создает класс Paint Panel, расширяющий класс JPanel. Программа использует объект данного класса для отображения линий, конечные точки которых создаются случайным образом. Результат выполнения программы показан на рис. Рис. 30.4. Результат выполнения программы PaintPanel // Рисование линий в панели j a v a .a w t .*; import j a v a .a w t .event.*; import javax.swing.*; import java.util.*; // Этот класс расширяет класс JPanel. Он переопределяет // метод paintComponent(), чтобы выводить // в панели случайные линии Глава 30. Введение в библиотеку Swing 9 71 class PaintPanel extends JPanel { Insets ins; // хранит размеры внутренней части панели rand; // используется для создания случайных чисел Создаем панель) { // Помещаем рамку вокруг панели (создаем ее границы setBorder( BorderFactory.createLineBorder(Color.R E D , 5)); rand = new Rand o m (); } // Переопределяем метод paintComponent(). protected void paintComponent(Graphics g) { // Первым всегда вызывается метод суперкласса, super.paintComponent(g) ; int x, у, x 2 , y 2 ; // Получаем высоту и ширину компонента int height = getHeight(); int width = getWidthO; // Получаем размеры внутренней части ins = getlnsets(); // Рисуем десять линий, конечные точки которых // создаются случайным образом for(int i=0; i < 10; i++) { // Получаем случайные координаты, определяющие // конечные точки каждой линии х = r a n d . next Int (width-ms . left) у = rand.nextlnt(height-ins.bottom); x2 = ra n d .nextlnt(width-ins.left); y2 = r a n d .nextlnt(height-ins.bottom); // Рисуем линию .drawLine(x, y, x2 , y 2 ); } } } // Иллюстрация рисования непосредственно в панели class PaintDemo { JLabel jlab; PaintPanel pp; PaintDemo() { // Создаем новый контейнер JFrame. JFrame jfrm = new JF r a m e ("Paint Demo"); // Присваиваем фрейму исходные размеры jfrm.setSize(200, 150); // Прекращаем работу программы, если пользователь // закрывает приложение frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Создаем панель, которую будем рисовать pp = new PaintPanel(); // Добавляем панель на панель содержимого. Так как 9 7 2 Часть III. Разработка программного обеспечения с использованием Java // используется компоновка BorderLayout, размеры панели будут // подбираться таким образом, чтобы она заняла центральную // часть области j frm.add(pp); // Отображаем фрейм, j frm.setVisible(true); } public static void main(String args[]) { // Создаем фрейм в потоке диспетчеризации событий SwingUtilities.invokeLater(new Runnable() { public void r u n () { new PaintDemo(); } }) А теперь обсудим программу. Класс PaintPanel расширяет класс JPanel, который является одним из облегченных контейнеров библиотеки Swing, темы получаем компонент, который можно добавлять в панель содержимого JFrame. Для обработки процесса рисования класс PaintPanel переопределяет метод paint - Component () . Это позволяет классу PaintPanel рисовать прямо на поверхности компонента. Размеры панели не определены, поскольку программа по умолчанию использует компоновку класса BorderLayout, а панель добавляется в центр области. В результате этого панель получает такие размеры, которые позволяют ей уместиться в центре. При изменении размеров окна будут соответствующим образом подгоняться и размеры панели. О братите внимание на то, что конструктор также определяет красную рамку границу) толщиной в 5 пикселей. Для этого используется метод setBorder (), показанный ниже setBorder(Border рамка) И нтерфейс Border библиотеки Swing инкапсулирует рамку. Рамку можно получить, вызвав один из методов, определенных в классе BorderFactory. В этой программе применяется один из таких методов — createLineBorder () . Он создает простую линейную рамку Border createLineBorder(Color c l r , int ширина) Здесь параметр c l r определяет цвет рамки, а ширина — ее ширину в пикселях. Внутри переопределения метода paint Component () следует обратить внимание на то, что он сначала вызывает метод super paintComponent ( ). Как уже было сказано, это необходимо для обеспечения надлежащего рисования. Затем вычисляется ширина и высоты панели, а также внутренней части. Эти значения используются для того, чтобы рисуемые линии находились внутри области рисования панели. Область рисования — это общая ширина и высота компонентами нус ширина рамки. Вычисления реализованы так, чтобы можно было оперировать различными размерами области рисования и границ. Чтобы убедиться в этом, попробуйте изменить размеры окна. Линии по-прежнему будут рисоваться внутри границ панели. Класс Paint Demo создает класса затем добавляет панель в панель содержимого. Вовремя первого отображения приложения вызывается переопределенный метод paintComponent () и рисуются линии. Каждый раз, когда будете изменять размеры окна или сворачивать и восстанавливать его, будет рисоваться новый набор линий. Во всех классах линии будут находиться внутри заданной области рисования |