Панели, создаваемые на базе класса Panel, являются мощным средством организации диалогового интерфейса. Так как класс Panel произошел от класса Container, панель может содержать компоненты и другие панели. Для каждой панели можно определить режим размещения компонент, что позволяет создавать достаточно сложный пользовательский интерфейс.
В окне аплета вы можете создать несколько панелей, разделяющих его на части. В свою очередь, пространство, занимаемое панелями, также может быть разделено с использованием одного из описанных выше режимов размещения (рис. 7.1).
Рис. 7.1. Размещение нескольких панелей в окне аплета
Отдельные панели могут содержать в себе такие компоненты, как кнопки, переключатели, списки, текстовые поля и так далее.
Панель создается очень просто. Прежде всего необходимо выбрать для окна аплета схему размещения компонент, соответствующую требуему расположению панелей. Например, для создания в окне аплета двух панелей, разделяющих его по горизонтали, следует выбрать режим GridLayout:
setLayout(new GridLayout(2, 1));
Панели будут размещаться в ячейках таблицы, состоящей из одного столбца и двух строк.
Далее нужно создать объекты класса Panel:
Panel pTopPanel; pTopPanel = new Panel(); Panel pBottomPanel; pBottomPanel = new Panel();
Ссылка на панель, которая будет располагаться сверху, записывается в переменную pTopPanel, а на ту, что будет располагаться снизу - в переменную pBottomPanel.
Создав панели, вы можете добавить их в окно аплета, вызвав метод add, как это показано ниже:
add(pTopPanel); add(pBottomPanel);
Заметим, что вы можете добавлять панели в панели, указывая, для какой панели нужно вызывать метод add:
Panel pLeft; Panel pRight; pLeft = new Panel(); pRight = new Panel(); pTopPanel.setLayout(new GridLayout(1, 2)); pTopPanel.add(pLeft); pTopPanel.add(pRight);
Здесь мы создали две панели pLeft и pRight, которые по нашему замыслу должны разделить пространство панели pTopPanel на две части по вертикали. Для обеспечения вертикального размещения панелей pLeft и pRight в панели pTopPanel мы вызвали для панели pTopPanel метод setLayout. При этом мы указали, что компоненты, добавляемые в эту панель, должны размещаться в таблице, состоящей из односй строки и двух столбцов.
Затем панели pLeft и pRight были добавлены в панель pTopPanel методом add.
Для добавления компонент в панель вы должны указать, для какой панели вызывается метод add, например:
Botton btn1; Botton btn2; btn1 = new Button(); btn2 = new Button(); pBottomPanel.add(btn1); pBottomPanel.add(btn2);
Как вы знаете, для того чтобы что-нибудь нарисовать, необходимо вначале получить контекст отображения. Методу paint передается контекст отображения, связанный с окном аплета. Если в окне имеются панели, то для рисования внутри них необходимо получить контекст отображения окон панелей.
Проще всего это сделать с помощью метода getGraphics, вызвав его для объекта класса Panel:
Graphics gpDraw; gpDraw = pDraw.getGraphics();
Здесь в переменную gpDraw мы записали ссылку на контекст отображения для панели pDraw.
Получив контекст отображения, можно приступить к рисованию. Вот, например, как можно нарисовать вокруг панели тонкую рамку:
Dimension dimAppWndDimension = pDraw.size(); gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
В этом фрагменте кода мы вначале определили размеры панели, вызвав для нее метод size, а затем при помощи метода drawRect, вызванного для контекста отображения gpDraw, нарисовали рамку.
Для установки шрифта и рисования текста в окне панели вы также должны указывать ссылку на контекст отображения вашей панели:
gpDraw.setFont(new Font("Courier", Font.PLAIN, 12)); gpDraw.drawString("Текст внутри окна панели", 10, 50);
Другой способ основан на создании собственного класса на базе класса Panel и переопределения в этом классе метода paint. Мы рассмотрим его позже в разделе “Переопределение класса Panel”.
В приложении PanelDemo мы создаем две панели, расположенные горизонтально. Первая из них используется как блокнот, на каждой странице которого находится кнопка, вторая содержит две управляющие кнопки, позволяющие перебирать страницы блокнота по очереди (рис. 7.2).
Рис. 7.2. Окно аплета PanelDemo
Объемное изображение схемы расположения панелей и кнопок относительно окна аплета показано на рис. 7.3.
Рис. 7.3. Объемное изображение схемы расположения панелей и кнопок
В верхней панели друг над другом располагаются пять кнопок (как колода карт), причем видна только одна из них. В нижней панели только две кнопки, с помощью которых можно выдвигать на передний план по очереди все кнопки из верхней панели.
Нажимая на кнопки Next и Prev, попробуйте понажимать на кнопки в верхней панели. В строке состояния навигатора при этом будет отображаться название нажатой кнопки (на рис. 6.5 это не показано).
Файл исходного текста приложения PanelDemo представлен в листинге 7.1.
Листинг 7.1. Файл PanelDemo\PanelDemo.java
// ========================================================= // Работа с панелями класса Panel // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ========================================================= import java.applet.*; import java.awt.*; public class PanelDemo extends Applet { // Панель для размещения блокнота Panel pCardPanel; // Панель для размещения кнопок управления блокнотом Panel pButtonPanel; // Создаем ссылки на объекты типа Button Button btn1; Button btn2; Button btn3; Button btn4; Button btn5; Button btnNext; Button btnPrev; // Строка для записи названия нажатой кнопки String sTextLabel; // ------------------------------------------------------- // getAppletInfo // Метод, возвращающей строку информации об аплете // ------------------------------------------------------- public String getAppletInfo() { return "Name: Grid\r\n" + "Author: Alexandr Frolov\r\n" + "E-mail: frolov@glas.apc.org" + "WWW: http://www.glasnet.ru/~frolov" + "Created with Microsoft Visual J++ Version 1.1"; } // ------------------------------------------------------- // init // Метод, получающий управление при инициализации аплета // ------------------------------------------------------- public void init() { // Устанавливаем желтый цвет фона setBackground(Color.yellow); // Создаем в окне аплета две панели, разделяющие // окно по горизонтали. В верхней панели будет // находиться блокнот, // в нижней - кнопки управления блокнотом setLayout(new GridLayout(2, 1)); // Создаем кнопки блокнота btn1 = new Button("Button 1"); btn2 = new Button("Button 2"); btn3 = new Button("Button 3"); btn4 = new Button("Button 4"); btn5 = new Button("Button 5"); // Создаем панель блокнота pCardPanel = new Panel(); // Устанавливаем режим размещения для блокнота pCardPanel.setLayout(new CardLayout(5, 5)); // Добавляем кнопки в блокнот pCardPanel.add(btn1); pCardPanel.add(btn2); pCardPanel.add(btn3); pCardPanel.add(btn4); pCardPanel.add(btn5); // Добавляем панель блокнота в окно аплета add(pCardPanel); // Создаем кнопки управления блокнотом // Кнопка просмотра следующей страницы блокнота btnNext = new Button("Next"); // Кнопка просмотра предыдущей страницы блокнота btnPrev = new Button("Prev"); // Создаем панель кнопок управления блокнотом pButtonPanel = new Panel(); // Устанавливаем режим размещения для панели кнопок pButtonPanel.setLayout(new FlowLayout()); // Добавляем кнопки в панель кнопок pButtonPanel.add(btnNext); pButtonPanel.add(btnPrev); // Добавляем панель кнопок add(pButtonPanel); } // ------------------------------------------------------- // action // Метод вызывается, когда пользователь выполняет // действие над компонентами // ------------------------------------------------------- public boolean action(Event evt, Object obj) { // Ссылка на кнопку, от которой пришло сообщение Button btn; // Проверяем, что событие вызвано кнопкой, а не // другим компонентом if(evt.target instanceof Button) { // Получам ссылку на кнопку, вызвавшую событие btn = (Button)evt.target; // Получаем название кнопки sTextLabel = btn.getLabel(); // Записываем название кнопки // в строку состояния навигатора showStatus("Button (\"" + sTextLabel + "\") pressed"); // Выполняем ветвление по кнопкам. Для каждой кнопки // записываем ее название // в строку состояния навигатора if(evt.target.equals(btnNext)) { // Выбираем следующую страницу в блокноте ((CardLayout)pCardPanel.getLayout()).next(pCardPanel); } else if(evt.target.equals(btnPrev)) { // Выбираем предыдущую страницу в блокноте ((CardLayout)pCardPanel.getLayout()).previous(pCardPanel); } else if(evt.target.equals(btn1)) { showStatus("Button 1 (\"" + sTextLabel + "\") pressed"); } else if(evt.target.equals(btn2)) { showStatus("Button 2 (\"" + sTextLabel + "\") pressed"); } else if(evt.target.equals(btn3)) { showStatus("Button 3 (\"" + sTextLabel + "\") pressed"); } else if(evt.target.equals(btn4)) { showStatus("Button 4 (\"" + sTextLabel + "\") pressed"); } // Если событие возникло от неизвестной кнопки, // мы его не обрабатываем else { return false; } // Возвращаем признак того, что мы обработали событие return true; } // Если событие вызвано не кнопкой, // мы его не обрабатываем return false; } }
В листинге 7.2 вы найдете исходный текст документа HTML, созданного для размещения нашего аплета.
Листинг 7.2. Файл PanelDemo\PanelDemo.html
<html> <head> <title>PanelDemo</title> </head> <body> <hr> <applet code=PanelDemo.class id=PanelDemo width=320 height=240 > </applet> <hr> <a href="PanelDemo.java">The source.</a> </body> </html>
Приведем описание полей и методов, определенных в нашем аплете.
В поле pCardPanel хранится ссылка на панель блокнота, страницы которого содержать кнопки. Эта панель располагается в верхней части окна аплета.
Поле pButtonPanel предназначено для хранения ссылки на панель кнопок, предназначенных для перелистывания страниц блокнота pCardPanel.
Ссылки на кнопки, расположенные на страницах блокнота, хранятся в полях btn1, btn2, btn3, btn4 и btn5.
Ссылки на кнопки, предназначенные для перелистывания страниц блокнота, записываются в поля btnNext и btnPrev (соответственно, кнопка пролистывания блокнота в прямом и обратном направлении).
В поле sTextLabel хранится название нажатой кнопки. Это название отображается в строке состояния навигатора, когда пользователь нажимает какую-либо кнопку в верхней или нижней панели.
Метод getAppletInfo возвращает информацию об аплете и не имеет никаких особенностей.
Метод init создает все необходимые панели и добавляет в них компоненты.
В самом начале своей работы метод init устанавливает желтый цвет фона для окна аплета:
setBackground(Color.yellow);
После этого выбирается такой режим размещения компонентов, при котором они добавляются в таблицу, состоящую из двух строк и одного столбца:
setLayout(new GridLayout(2, 1));
Мы будем добавлять панели, поэтому первая из добавленных панелей окажется в верхней строке этой таблицы и займет верхнюю половину окна аплета, а вторая - нижнюю.
Далее метод init создает пять кнопок, которые будут добавлены на страницы блокнота, расположенного в верхней панели:
btn1 = new Button("Button 1"); btn2 = new Button("Button 2"); btn3 = new Button("Button 3"); btn4 = new Button("Button 4"); btn5 = new Button("Button 5");
После создания кнопок метод init приступает к созданию и заполнению панели блокнота. Панель создается при помощи конструктора класса Panel:
pCardPanel = new Panel();
Для этой панели устанавливается режим размещения компонент типа CardLayout, причем между границами окна панели и границами окна добавляемых компонент по вертикали и горизонтали оставлен зазор 5 пикселов:
pCardPanel.setLayout(new CardLayout(5, 5));
После установки режима добавления можно заполнять блокнот кнопками:
pCardPanel.add(btn1); pCardPanel.add(btn2); pCardPanel.add(btn3); pCardPanel.add(btn4); pCardPanel.add(btn5);
Здесь мы воспользовались известным вам методом add, вызвав его для объекта pCardPanel.
Заполненная панель блокнота при помощи все того же метода add добавляется в окно аплета:
add(pCardPanel);
Так как эта панель добавляется в окно аплета первой, она займет верхнюю половину этого окна.
Завершив с панелью блокнота, метод init приступает к формированию панели управляющих кнопок.
Вначале метод создает сами управляющие кнопки:
btnNext = new Button("Next"); btnPrev = new Button("Prev");
Первая из них перелистывает страницы блокнота в прямом направлении, а вторая - в обратном.
Затем создается панель кнопок:
pButtonPanel = new Panel();
Для панели кнопок мы выбираем режим выравнивания FlowLayout, при котором компоненты добавляются слева направо и сверху вниз:
pButtonPanel.setLayout(new FlowLayout());
После этого в панель добавляются две управляющие кнопки, преднаазначенные для перелистывания страниц блокнота:
pButtonPanel.add(btnNext); pButtonPanel.add(btnPrev);
На завершающем этапе своей работы метод init добавляет панель управляющих кнопок в окно аплета:
add(pButtonPanel);
Данная панель добавляется в окно аплета второй по счету, поэтому она будет расположена в нижней половине этого окна.
Метод action обрабатывает события, связанные с кнопками, расположенными в обеих панелях.
Вначале метод проверяет, что событие создано кнопкой. Далее идентификатор кнопки, вызвавшей событие, записывается в переменную btn:
btn = (Button)evt.target;
После этого метод action получает название кнопки, сохраняет его в строке sTextLabel, а затем отображает в строке состояния навигатора:
sTextLabel = btn.getLabel(); showStatus("Button (\"" + sTextLabel + "\") pressed");
Далее анализируется ссылка на кнопку. Если была нажата одна из управляющих кнопок, происходит перелистывание страниц блокнота, в прямом или обратном направлении:
if(evt.target.equals(btnNext)) { ((CardLayout)pCardPanel.getLayout()).next(pCardPanel); } else if(evt.target.equals(btnPrev)) { ((CardLayout)pCardPanel.getLayout()).previous(pCardPanel); }
Здесь мы с помощью метода getLayout получаем ссылку на интерфейс системы Layout Manager, установленной для панели pCardPanel, а затем, пользуясь полученной ссылкой, вызываем методы next или previous. Обратите также внимание на необходимость явного приведения типа к классу CardLayout, в котором определены указанные методы.
Обработка событий, создаваемых кнопками, которые расположены на страницах блокнота, не имеет никаких особенностей:
else if(evt.target.equals(btn1)) { showStatus("Button 1 (\"" + sTextLabel + "\") pressed"); } . . . else if(evt.target.equals(btn4)) { showStatus("Button 4 (\"" + sTextLabel + "\") pressed"); }
Название кнопки просто отображается в строке состояния навигатора.
Приложение Notebook служит в качестве более сложного примера техники работы с панелями.
В окне аплета Notebook создаются три панели, расположенные в одном столбце. В верхней панели, имеющей рамку по периметру, рисуется строка текста “Смотри на шрифт, цвет фона и текста!”. Средняя панель представляет собой блокнот, предназначенный для выбора цвета фона, цвета изображения и шрифта для верхней панели. И, наконец, нижняя панель содержит кнопки, позволяющие перелистывать страницы блокнота.
На рис. 7.4 показана страница, предназанченная для выбора цвета фона:
Рис. 7.4. Страница, предназанченная для выбора цвета фона
Нажимая кнопки Background Color, Foreground Color и Set Text Font, вы сможете выдвигать на передний план страницы блокнота, предназначенные, соответственно, для выбора цвета фона, изображения и шрифта, которым отображается текст в верхней панели.
Кнопки Next и Prev работают таким же образом, что и в предыдущем приложении, а именно: если нажать на кнопку Next, произойдет пролистывание страниц в прямом направлении, а если на кнопку Prev - в обратном направлении.
На рис. 7.5 изображена страница блокнота, предназначенная для выбора цвета изображения.
Рис. 7.5. Страница, предназанченная для выбора цвета изображения
На рис. 7.6 представлена страница, с помощью которой можно выбрать один из нескольких шрифтов для рисования текста в верхней панели.
Рис. 7.6. Страница, предназанченная для выбора шрифта
Рассмотрим исходные тексты приложения Notebook/
Исходный текст приложения Notebook приведен в листинге 7.3.
Листинг 7.3. Файл Notebook\Notebook.java
// ========================================================= // Набор диалоговых панелей // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ========================================================= import java.applet.*; import java.awt.*; public class Notebook extends Applet { // Панель для размещения блокнота Panel pCardPanel; // Панель для размещения кнопок управления блокнотом Panel pButtonPanel; // Панель для рисования Panel pDraw; // Панели отдельных страниц Panel pBackgroundColor; // страница выбора цвета фона Panel pForegroundColor; // страница выбора цвета // изображения Panel pFont; // страница выбора шрифта // Кнопки выбора сраниц блокнота Button btnNext; // следующая Button btnPrev; // предыдущая Button btnBackgroundColor; // фон Button btnForegroundColor; // изображение Button btnFont; // шрифт // Создаем ссылки на объекты класса Choice Choice chBackgroundColor; // список цветов фона Choice chForegroundColor; // список цветов изображения Choice chFont; // список шрифтов // Текстовые метки списков Label tBackgroundColor; // метка списка цветов фона Label tForegroundColor; // метка списка цветов // изображения Label tFont; // метка списка шрифтов // Строка для хранения название выбранного шрифта String sFontName; // ------------------------------------------------------- // getAppletInfo // Метод, возвращающей строку информации об аплете // ------------------------------------------------------- public String getAppletInfo() { return "Name: Grid\r\n" + "Author: Alexandr Frolov\r\n" + "E-mail: frolov@glas.apc.org" + "WWW: http://www.glasnet.ru/~frolov" + "Created with Microsoft Visual J++ Version 1.1"; } // ------------------------------------------------------- // init // Метод, получающий управление при инициализации аплета // ------------------------------------------------------- public void init() { // Устанавливаем желтый цвет фона setBackground(Color.yellow); // ---------------------------------------------------- // Создаем в окне аплета две панели, разделяющие // окно по горизонтали. В верхней панели будет // находиться блокнот, в нижней - кнопки управления // блокнотом // ---------------------------------------------------- setLayout(new GridLayout(3, 1)); // Создаем панель блокнота pCardPanel = new Panel(); // ---------------------------------------------------- // Создаем панели страниц блокнота // ---------------------------------------------------- // Панель для выбора цвета фона pBackgroundColor = new Panel(); // Панель для выбора цвета изображения pForegroundColor = new Panel(); // Панель для выбора шрифта pFont = new Panel(); // ---------------------------------------------------- // Создаем списки для выбора цвета фона и // цвета изображения // ---------------------------------------------------- // Список для выбора цвета фона chBackgroundColor = new Choice(); // Список для выбора цвета изображения chForegroundColor = new Choice(); // Список для выбора шрифта chFont = new Choice(); // ---------------------------------------------------- // Создаем метки для списков // ---------------------------------------------------- // Метка для списка цвета фона tBackgroundColor = new Label("Chose Background Color:"); // Метка для списка цвета изображения tForegroundColor = new Label("Chose Foreground Color:"); // Метка для списка шрифтов tFont = new Label("Chose Font:"); // ---------------------------------------------------- // Добавляем списки и метки в панели // ---------------------------------------------------- // Список цвета фона и метка pBackgroundColor.add(tBackgroundColor); pBackgroundColor.add(chBackgroundColor); // Список цвета изображения и метка pForegroundColor.add(tForegroundColor); pForegroundColor.add(chForegroundColor); // Список шрифтов и метка pFont.add(tFont); pFont.add(chFont); // ---------------------------------------------------- // Заполняем списки // ---------------------------------------------------- // Заполняем список цвета фона chBackgroundColor.addItem("Yellow"); chBackgroundColor.addItem("Green"); chBackgroundColor.addItem("White"); // Заполняем список цвета изображения chForegroundColor.addItem("Black"); chForegroundColor.addItem("Red"); chForegroundColor.addItem("Blue"); // Заполняем список шрифтов chFont.addItem("Helvetica"); chFont.addItem("Courier"); chFont.addItem("TimesRoman"); // Создаем панель для рисования pDraw = new Panel(); // Устанавливаем режим размещения для блокнота pCardPanel.setLayout(new CardLayout(5, 5)); // ---------------------------------------------------- // Добавляем панели страниц в блокнот // ---------------------------------------------------- // Панель выбора цвета фона pCardPanel.add("BackgroundColor", pBackgroundColor); // Панель выбора цвета изображения pCardPanel.add("ForegroundColor", pForegroundColor); // Панель выбора шрифта pCardPanel.add("Font", pFont); // Добавляем панель для рисования в окно аплета add(pDraw); // Добавляем панель блокнота в окно аплета add(pCardPanel); // ---------------------------------------------------- // Создаем кнопки управления блокнотом // ---------------------------------------------------- // Кнопка просмотра следующей страницы блокнота btnNext = new Button("Next"); // Кнопка просмотра предыдущей страницы блокнота btnPrev = new Button("Prev"); // Выбор панели цвета фона btnBackgroundColor = new Button("Background Color"); // Выбор панели цвета изображения btnForegroundColor = new Button("Foreground Color"); // Выбор панели шрифтов btnFont = new Button("Set Text Font"); // Создаем панель кнопок управления блокнотом pButtonPanel = new Panel(); // Устанавливаем режим размещения для панели кнопок pButtonPanel.setLayout(new FlowLayout()); // Добавляем кнопки в панель кнопок pButtonPanel.add(btnBackgroundColor); pButtonPanel.add(btnForegroundColor); pButtonPanel.add(btnFont); pButtonPanel.add(btnNext); pButtonPanel.add(btnPrev); // Добавляем панель кнопок add(pButtonPanel); // Выбираем шрифт по умолчанию sFontName = new String("Helvetica"); // Отображаем окно аплета show(); } // ------------------------------------------------------- // action // Метод вызывается, когда пользователь выполняет // действие над компонентами // ------------------------------------------------------- public boolean action(Event evt, Object obj) { // Проверяем, что событие вызвано кнопкой, а не // другим компонентом if(evt.target instanceof Button) { // Ссылка на кнопку, от которой пришло сообщение Button btn; // Получам ссылку на кнопку, вызвавшую событие btn = (Button)evt.target; // Выполняем ветвление по кнопкам. Для каждой кнопки // записываем ее название в строку состояния // навигатора if(evt.target.equals(btnNext)) // Выбираем следующую страницу в блокноте ((CardLayout)pCardPanel.getLayout()).next(pCardPanel); else if(evt.target.equals(btnPrev)) // Выбираем предыдущую страницу в блокноте ((CardLayout)pCardPanel.getLayout()).previous(pCardPanel); else if(evt.target.equals(btnBackgroundColor)) // Выбираем страницу цвета фона ((CardLayout)pCardPanel.getLayout()).show( pCardPanel, "BackgroundColor"); else if(evt.target.equals(btnForegroundColor)) // Выбираем страницу цвета изображения ((CardLayout)pCardPanel.getLayout()).show( pCardPanel, "ForegroundColor"); else if(evt.target.equals(btnFont)) // Выбираем страницу шрифтов ((CardLayout)pCardPanel.getLayout()).show( pCardPanel, "Font"); // Если событие возникло от неизвестной кнопки, // мы его не обрабатываем, передавая методу action // родительского класса else return super.action(evt, obj); // Перерисовываем окно панели pDraw и аплета pDraw.repaint(); repaint(); // Возвращаем признак того, что мы обработали событие return true; } // Обработка событий от списков else if(evt.target instanceof Choice) { // Переменная для хранения ссылки на список, // вызвавший событие Choice ch; // Получаем ссылку на список ch = (Choice)evt.target; // Выполняем ветвление по спискам // Список цвета фона if(evt.target.equals(chBackgroundColor)) { // Получаем номер текущего элемента списка // и устанавливаем соответствующий // цвет фона if(ch.getSelectedIndex() == 0) pDraw.setBackground(Color.yellow); else if(ch.getSelectedIndex() == 1) pDraw.setBackground(Color.green); else if(ch.getSelectedIndex() == 2) pDraw.setBackground(Color.white); } // Список цвета изображения else if(evt.target.equals(chForegroundColor)) { // Получаем номер текущего элемента списка // и устанавливаем соответствующий // цвет изображения if(ch.getSelectedIndex() == 0) pDraw.setForeground(Color.black); else if(ch.getSelectedIndex() == 1) pDraw.setForeground(Color.red); else if(ch.getSelectedIndex() == 2) pDraw.setForeground(Color.blue); } // Список шрифтов else if(evt.target.equals(chFont)) { // Получаем номер текущего элемента списка // и записываем имя соответствующего шрифта // в строку sFontName if(ch.getSelectedIndex() == 0) sFontName = "Helvetica"; else if(ch.getSelectedIndex() == 1) sFontName = "Courier"; else if(ch.getSelectedIndex() == 2) sFontName = "TimesRoman"; } // Если событие возникло от неизвестного списка, // мы его не обрабатываем, передавая методу action // родительского класса else return super.action(evt, obj); // Перерисовываем панель pDraw pDraw.repaint(); // Перерисовываем окно аплета repaint(); // Возвращаем признак того, что мы обработали событие return true; } // Вызываем метод action родительского класса return super.action(evt, obj); } // ------------------------------------------------------- // paint // Метод paint, выполняющий рисование в окне аплета // ------------------------------------------------------- public void paint(Graphics g) { Graphics gpDraw; // Получаем контекст отображения для панели рисования gpDraw = pDraw.getGraphics(); // Определяем текущие размеры Dimension dimAppWndDimension = pDraw.size(); // Рисуем рамку вокруг окна аплета gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); // Устанавливаем шрифт gpDraw.setFont(new Font(sFontName, Font.PLAIN, 12)); // Рисуем строку gpDraw.drawString( "Смотри на шрифт, цвет фона и текста!", 10, 50); // Получаем контекст отображения для панели блокнота gpDraw = pCardPanel.getGraphics(); // Определяем размеры панели блокнота dimAppWndDimension = pCardPanel.size(); // Обводим блокнот рамкой gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); } }
В листинге 7.4 вы найдете исходный текст документа HTML, созданного для размещения аплета.
Листинг 7.4. Файл Notebook\Notebook.html
<html> <head> <title>Notebook</title> </head> <body> <hr> <applet code=Notebook.class id=Notebook width=320 height=240 > </applet> <hr> <a href="Notebook.java">The source.</a> </body> </html>
В классе Notebook определено довольно много полей и переопределено несколько методов.
В полях pDraw, pCardPanel и pButtonPanel находятся ссылки, соответственно, на верхнюю, среднюю и нижнюю панели, предназначенные для рисования, размещения блокнота диалоговых панелей настроек и кнопок управления блокнотом.
В предыдущем приложении на страницах блокнота размещались кнопки. Теперь мы решили более сложную задачу - поместили на страницы блокнота три панели, по одной на каждую страницу. Первая из этих панелей содержит список для выбора цвета фона, вторая - для выбора цвета изображения и, наконец, третья, для выбора шрифта. Поля pBackgroundColor, pForegroundColor и pFont хранят ссылки на соответствующие панели настроек.
Нижняя панель содержит кнопки управления страницами блокнота. С помощью кнопок, ссылки на которые хранятся в полях btnBackgroundColor, btnForegroundColor и btnFont вы можете выбирать для отображения страницы блокнота, содержащие панели настройки цвета фона, изображения и шрифта. Таким образом, нет необходимости перебирать страницы блокнота по очереди до тех пор, пока в окне не появится нужная страница. Тем не менее, мы предусмотрели кнопки и для циклического перебора страниц блокнота. Ссылки на эти кнопки хранятся в полях btnNext и btnPrev.
На каждой панели в блокноте размещается один список и одна надпись, объясняющая назначение списка. Списки создаются как объекты класса Choice, а надписи - как объекты класса Label.
Поля chBackgroundColor, chForegroundColor и chFont хранят ссылки на списки, соответственно, цвета фона, цвета изображения и шрифтов. В полях tBackgroundColor, tForegroundColor и tFont хранятся ссылки надписей.
Поле sFontName класса String предназначено для хранения названия текущего шрифта, с использованием которого отображается текст в верхней панели.
Метод getAppletInfo возвращает информацию об аплете.
Метод init выполняет достаточно громоздкую работу по созданию и добавлению различных панелей и других компонентов. К сожалению, приложениям Java не доступны ресурсы, аналогичные ресурсам операционной системы Microsoft Windows, поэтому формирование диалоговых панелей и других элементов пользовательского интерфейса приходится выполнять чисто программными методами на этапе выполнения приложения. Средства среды разработки приложений Java Microsoft Visual J++ версии 1.1, о которых мы уже упоминали, позволяют несколько упростить этот процесс.
Свою работу метод init начинает с установки желтого цвета фона для окна аплета.
Далее устанавливается режим добавления GridLayout, разделяющий окно аплета на три части по горизонтали:
setLayout(new GridLayout(3, 1));
Соответствующая таблица, в которую будут добавляться компоненты, имеет три строки и один столбец.
Панель блокнота создается следующим образом:
pCardPanel = new Panel();
Затем создаются три панели, которые будут добавляться в панель pCardPanel:
pBackgroundColor = new Panel(); pForegroundColor = new Panel(); pFont = new Panel();
Эти панели предназначены для размещения компонент, с помощью которых можно будет выбирать цвет фона и изображения, а также шрифт.
На следующем этапе создаются три списка, которые будут размещаться по одному на указанных панелях:
chBackgroundColor = new Choice(); chForegroundColor = new Choice(); chFont = new Choice();
Каждый такой список снабжается надписью, поясняющей его назначение. Надписи создаются следующим образом:
tBackgroundColor = new Label("Chose Background Color:"); tForegroundColor = new Label("Chose Foreground Color:"); tFont = new Label("Chose Font:");
Созданные метки и списки добавляются в панели, расположенные на страницах блокнота:
pBackgroundColor.add(tBackgroundColor); pBackgroundColor.add(chBackgroundColor); pForegroundColor.add(tForegroundColor); pForegroundColor.add(chForegroundColor); pFont.add(tFont); pFont.add(chFont);
Вначале мы добавляем надпись, затем список. Поэтому надпись будет расположена слева от списка.
Заполнение списков выполняется с помощью метода addItem, который вызывается по очереди для каждого списка и для каждого добавляемого элемента списка:
chBackgroundColor.addItem("Yellow"); chBackgroundColor.addItem("Green"); chBackgroundColor.addItem("White"); chForegroundColor.addItem("Black"); chForegroundColor.addItem("Red"); chForegroundColor.addItem("Blue"); chFont.addItem("Helvetica"); chFont.addItem("Courier"); chFont.addItem("TimesRoman");
Далее метод init приступает к формированию верхенй панели, предназначенной для отображения текстовой строки.
Панель создается следующим образом:
pDraw = new Panel();
Затем метод init начинает наполнение страниц блокнота.
Прежде всего он устанавливает режим добавления CardLayout, оставляя зазор по вертикали и горизонтали в 5 пикселов:
pCardPanel.setLayout(new CardLayout(5, 5));
Панели добавляются методом add, причем мы выбрали вариант этого метода, допускающий присваивание имен добавляемым компонентам:
pCardPanel.add("BackgroundColor", pBackgroundColor); pCardPanel.add("ForegroundColor", pForegroundColor); pCardPanel.add("Font", pFont);
Имена, которые передаются через первый параметр, необходимы для отображения страниц блокнота с помощью кнопок btnBackgroundColor, btnForegroundColor и btnFont.
После заполнения страниц блокнота метод init добавляет верхнюю и среднюю панели в окно аплета:
add(pDraw); add(pCardPanel);
В процессе формирования нижней панели метд init создает кнопки, предназначенные для управления блокнотом:
btnNext = new Button("Next"); btnPrev = new Button("Prev"); btnBackgroundColor = new Button("Background Color"); btnForegroundColor = new Button("Foreground Color"); btnFont = new Button("Set Text Font"); pButtonPanel = new Panel();
Перед добавлением кнопок в нижней панели устанавливается режим размещения FlowLayout:
pButtonPanel.setLayout(new FlowLayout());
Затем кнопки добавляются в панель вызовом метода add:
pButtonPanel.add(btnBackgroundColor); pButtonPanel.add(btnForegroundColor); pButtonPanel.add(btnFont); pButtonPanel.add(btnNext); pButtonPanel.add(btnPrev);
После формирования панели кнопок эта панель добавляется в окно аплета, располагаясь в его нижней части:
add(pButtonPanel);
В поле sFontName записывается имя шрифта, выбранного по умолчанию:
sFontName = new String("Helvetica");
На завершающем этапе метод init выполняет принудительную перерисовку и отображение панелей, вызывая специально предназначенный для этого метод show:
show();
Метод action выполняет раздельную обработку событий, вызванных кнопками и списками.
Если событие было вызвано кнопками, выполняется переключение страниц блокнота:
if(evt.target.equals(btnNext)) ((CardLayout)pCardPanel.getLayout()).next(pCardPanel); else if(evt.target.equals(btnPrev)) ((CardLayout)pCardPanel.getLayout()).previous(pCardPanel); else if(evt.target.equals(btnBackgroundColor)) ((CardLayout)pCardPanel.getLayout()).show( pCardPanel, "BackgroundColor"); else if(evt.target.equals(btnForegroundColor)) ((CardLayout)pCardPanel.getLayout()).show( pCardPanel, "ForegroundColor"); else if(evt.target.equals(btnFont)) ((CardLayout)pCardPanel.getLayout()).show( pCardPanel, "Font");
Для выбора следующей и предыдущей страницы здесь использованы методы next и previous.
Выбор конкретной страницы по ее имени осуществляется с помощью метода show. В качестве параметров этому методу передается ссылка на панель блокнота и имя страницы.
Обратите также внимание на способ обработки событий, не имеющих отношения к нашим компонентам:
return super.action(evt, obj);
Здесь мы вызываем метод action из базового класса, который после соответствующей обработки события вернет значение true или false.
Если событие вызвано кнопками управления блокнотом, мы перерисовываем окно верхней панели, окно всего аплета и затем возвращаем признак успешной обработки события:
pDraw.repaint(); repaint(); return true;
Как вы увидите дальше, в процессе перерисовки окна всего аплета метод paint выполнит рисование в окне верхней панели.
События, связанные с выбором нового цвета фона и изображения обрабатываются следующим образом:
if(evt.target.equals(chBackgroundColor)) { if(ch.getSelectedIndex() == 0) pDraw.setBackground(Color.yellow); else if(ch.getSelectedIndex() == 1) pDraw.setBackground(Color.green); else if(ch.getSelectedIndex() == 2) pDraw.setBackground(Color.white); } else if(evt.target.equals(chForegroundColor)) { if(ch.getSelectedIndex() == 0) pDraw.setForeground(Color.black); else if(ch.getSelectedIndex() == 1) pDraw.setForeground(Color.red); else if(ch.getSelectedIndex() == 2) pDraw.setForeground(Color.blue); }
Здесь методы setBackground и setForeground устанавливают цвет фона и изображения для панели pDraw.
Если событие вызвано списком шрифтов, то мы получаем номер элемента списка и записываем название выбранного шрифта в текстовую строку sFontName:
else if(evt.target.equals(chFont)) { if(ch.getSelectedIndex() == 0) sFontName = "Helvetica"; else if(ch.getSelectedIndex() == 1) sFontName = "Courier"; else if(ch.getSelectedIndex() == 2) sFontName = "TimesRoman"; }
После обработки события, вызванного списками, мы перерисовываем окно панели рисования pDraw и окно аплета:
pDraw.repaint(); repaint();
Метод paint рисует в окне панели pDraw, а не в главном окне аплета. Для этого метод paint получает контекст отображения этой панели, вызывая для этого метод getGraphics:
Graphics gpDraw; gpDraw = pDraw.getGraphics();
Далее метод paint определяет размеры панели pDraw и рисует рамку вокруг окна этой панели:
Dimension dimAppWndDimension = pDraw.size(); gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
После того как рамка будет нарисована, метод paint устанавливает в панели pDraw шрифт, название которого хранится в староке sFontName. Для этого используется контекст отображения gpDraw, полученный нами ранее для панели pDraw:
gpDraw.setFont(new Font(sFontName, Font.PLAIN, 12));
Текстовая строка отображается с использованием текущего цвета изображения и текущего шрифта, установленного в контексте отображения gpDraw, при помощи метода drawString:
gpDraw.drawString("Смотри на шрифт, цвет фона и текста!", 10, 50);
Затем метод paint обводит панель блокнота рамкой. Вначале он получает контекст отображения для панели блокнота, как это показано ниже:
gpDraw = pCardPanel.getGraphics();
После получения контекста отображения метод paint определяет размеры панели блокнота и рисует рамку вокруг его окна:
dimAppWndDimension = pCardPanel.size(); gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
Если ваш аплет создает много панелей, техника рисования в окнах этих панелей, использованная в только что рассмотренном приложении Notebook, может привести к усложнению исходного текста приложения. Так как рисование в окнах панелей выполняется в методе paint класса аплета, вам придется получать контекст отображения для каждой панели.
Намного проще создать несколько дочерних классов от класса Panel, переопределив в каждом из них метод paint. В этом случае для каждой панели вы можете создать свой метода paint, которому будет автоматически передаваться контекст отображения, связанный с окном соответствующей панели.
Эта техника использована в приложении Panel2, которое мы рассмотрим в следующем разделе.
В окне аплета Panel2 мы создали две панели, одна из которых занимает верхнюю половину окна, а другая - нижнюю (рис. 7.7).
Рис. 7.7. Окно аплета Panel2 с двумя панелями
Для каждой панели мы создали два отдельных класса на базе класса Panel. В созданных нами классах переопределен метод paint, который рисует одну текстовую строку в окне своей панели. В верхней панели рисуется строка “Первая панель”, в нижней - “Вторая панель”.
Кроме того, метод paint класса нашего аплета рисует две строки в окнах обеих панелей, получая контекст отображения для панелей и указывая ссылку на этот контекст явным образом.
В процессе инициализации метод init класса нашего аплета устанавливает различный цвет фона и изображения для панелей, поэтому рамки вокруг окон панелей и текст получаются нарисованными различным цветом независимо от способа их рисования.
Файл исходных текстов приложения приведен в листинге 7.5.
Листинг 7.5. Файл Panel2\Panel2.java
// ========================================================= // Работа с панелями класса Panel // Наследование от класса Panel // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ========================================================= import java.applet.*; import java.awt.*; // ========================================================= // Класс Panel2 // Это наш аплет // ========================================================= public class Panel2 extends Applet { // Первая панель FirstPanel pPanel1; // Вторая панель SecondPanel pPanel2; // ------------------------------------------------------- // getAppletInfo // Метод, возвращающей строку информации об аплете // ------------------------------------------------------- public String getAppletInfo() { return "Name: Panel2\r\n" + "Author: Alexandr Frolov\r\n" + "E-mail: frolov@glas.apc.org" + "WWW: http://www.glasnet.ru/~frolov" + "Created with Microsoft Visual J++ Version 1.0"; } // ------------------------------------------------------- // init // Метод, получающий управление при инициализации аплета // ------------------------------------------------------- public void init() { // Создаем в окне аплета две панели, разделяющие // окно по горизонтали setLayout(new GridLayout(2, 1)); // Создаем первую панель pPanel1 = new FirstPanel(); // Добавляем первую панель в окно аплета add(pPanel1); // Создаем вторую панель pPanel2 = new SecondPanel(); // Добавляем вторую панель add(pPanel2); // Устанавливаем желтый цвет фона для первой панели pPanel1.setBackground(Color.yellow); // Устанавливаем черный цвет изображения // для первой панели pPanel1.setForeground(Color.black); // Устанавливаем белый цвет фона для второй панели pPanel2.setBackground(Color.white); // Устанавливаем красный цвет изображения // для второй панели pPanel2.setForeground(Color.red); // Инициируем вызов метода paint repaint(); } // ------------------------------------------------------- // paint // Метод paint, выполняющий рисование в окне аплета // ------------------------------------------------------- public void paint(Graphics g) { Graphics gPanel1; Graphics gPanel2; // Получаем контекст отображения для первой панели gPanel1 = pPanel1.getGraphics(); // Рисуем строку в окне первой панели gPanel1.drawString("Нарисовано в первой панели", 10, 80); // Получаем контекст отображения для второй панели gPanel2 = pPanel2.getGraphics(); // Рисуем строку в окне второй панели gPanel2.drawString("Нарисовано во второй панели", 10, 80); } } // ========================================================= // Класс FirstPanel // Первая панель // ========================================================= class FirstPanel extends Panel { // ------------------------------------------------------- // paint // Метод paint, выполняющий рисование в окне аплета // ------------------------------------------------------- public void paint(Graphics g) { // Определяем текущие размеры Dimension dimAppWndDimension = size(); // Рисуем рамку вокруг окна аплета g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); // Устанавливаем шрифт g.setFont(new Font("TimesRoman", Font.PLAIN, 12)); // Рисуем строку g.drawString("Первая панель", 10, 50); // Вызываем метод paint родительского класса super.paint(g); } } // ========================================================= // Класс SecondPanel // Вторая панель // ========================================================= class SecondPanel extends Panel { // ------------------------------------------------------- // paint // Метод paint, выполняющий рисование в окне аплета // ------------------------------------------------------- public void paint(Graphics g) { // Определяем текущие размеры Dimension dimAppWndDimension = size(); // Рисуем рамку вокруг окна аплета g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); // Устанавливаем шрифт g.setFont(new Font("Helvetica", Font.PLAIN, 12)); // Рисуем строку g.drawString("Вторая панель", 10, 50); // Вызываем метод paint родительского класса super.paint(g); } }
Исходный текст документа HTML, предназначенного для размещения нашего аплета, представлен в листинге 7.6.
Листинг 7.6. Файл Panel2\Panel2.html
<html> <head> <title>Panel2</title> </head> <body> <hr> <applet code=Panel2.class id=Panel2 width=320 height=240 > </applet> <hr> <a href="Panel2.java">The source.</a> </body> </html>
Как мы уже говорили, в приложении Panel2 мы создали два класса, взяв для них в качестве базового класс Panel. Имена этих классов - FirstPanel и SecondPanel. После трансляции проекта системой Microsoft Visual J++ получаются три двоичных файла с именами Panel2.class, FirstPanel.class и SecondPanel.class - по одному для каждого класса.
В классе Panel2 определено два поля с именами pPanel1 и pPanel2 класса Panel. Первое из них предназначено для хранения ссылки на верхюю панель, второе - на нижнюю (в соответствии с их расположением в окне аплета).
Метод getAppletInfo возвращает информацию об аплете.
Прежде всего метод init устанавливает для окна панели режим добавления компонент GridLayout, определяя таблицу из двух строк и одного столбца.
Первая панель создается на базе класса FirstPanel, определенного в нашем приложении:
pPanel1 = new FirstPanel();
Этот класс мы рассмотрим позже.
Созданная панель добавляется в окно аплета методом add:
add(pPanel1);
Аналогично мы создаем и вторую панель, на этот раз как объект класса SecondPanel:
pPanel2 = new SecondPanel();
Вторая панель добавляется в окно аплета точно также, как и первая:
add(pPanel2);
Для того чтобы выделить панели на фоне окна аплета, мы устанавливаем для них разные цвета фона и изображения. Для первой панели устанавливается желтый цвет фона и черный цвет изображения:
pPanel1.setBackground(Color.yellow); pPanel1.setForeground(Color.black);
Для второй панели мы устанавливаем белый цвет фона и красный цвет изображения:
pPanel2.setBackground(Color.white); pPanel2.setForeground(Color.red);
В результате цвета рамки окна и текста первой и второй панели будут разными.
На последнем этапе инициализации мы инициируем вызов метода paint класса Panel2, вызывая для этого метод repaint:
repaint();
Это нужно для того, чтобы сразу после отображения окна аплета выполнить рисование текстовых строк внутри окон панелей.
Метод paint класса Panel2 рисует две строки в окнах панелей, расположенных в окне аплета. Для этого он получает контекст отображения каждой панели и вызывает для этого контекста метод drawGraphics:
Graphics gPanel1; Graphics gPanel2; gPanel1 = pPanel1.getGraphics(); gPanel1.drawString("Нарисовано в первой панели", 10, 80); gPanel2 = pPanel2.getGraphics(); gPanel2.drawString("Нарисовано во второй панели", 10, 80);
Хотя при рисовании строк мы указали одинаковые координаты начала строки (10, 80), наложения строк не произойдет. Это потому, что эти строки рисуются в разных графических контекстах, которые относятся к окнам разных панелей.
Цвет рисуемого текста и фона, на котором он будет нарисован, также получится разный, так как для наших панелей установлен разный цвет изображения и фона.
Класс FirstPanel создан на базе класса Panel:
class FirstPanel extends Panel { public void paint(Graphics g) { . . . super.paint(g); } }
В нашем приложении мы создаем верхнюю панель как объект этого класса. Единственный метод, переопределенный в классе FirstPanel - это метод paint.
Задачей метода paint класса FirstPanel является рисование рамки вокруг первой панели и текстовой строки в окне этой панели. В качестве параметра метод paint класса FirstPanel получает ссылку на контекст отображения для окна первой панели. Мы можем использовать этот контекст для того чтобы нарисовать что-нибудь внутри первой панели.
Процедура рисования не имеет никаких особенностей:
Dimension dimAppWndDimension = size(); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); g.setFont(new Font("TimesRoman", Font.PLAIN, 12)); g.drawString("Первая панель", 10, 50);
Заметим, однако, что после завершения рисования мы вызываем метод paint из базового класса, позволяя этому классу выполнить свою обработку:
super.paint(g);
Класс SecondPanel создан также на базе класса Panel:
class SecondPanel extends Panel { public void paint(Graphics g) { . . . super.paint(g); } }
С использованием этого класса создается нижняя панель. В классе SecondPanel, как и в классе FirstPanel, переопределен метод paint.
Метода paint класса SecondPanel выполняет те же самые операции, что и метод paint класса FirstPanel, однако ему передается контекст отображения второй панели. Поэтому он нарисует рамку и текстовую строку во второй панели, а не в первой или где-нибудь еще.