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

Секреты программирования для Internet на Java

.pdf
Скачиваний:
181
Добавлен:
02.05.2014
Размер:
3.59 Mб
Скачать

Выяснение размера для текущего расположения

Разработав и отладив расположение своих элементов интерфейса, вы можете выяснить, какой размер будет иметь окно с компонентами, размещенными таким образом. Для получения этой информации можно использовать любой из следующих методов, возвращающих размер контейнера:

preferredLayoutSize: public abstract Dimension preferredLayoutSize(Container parent).

Этот метод вычисляет естественные ширину и высоту для указанной панели с учетом остальных компонентов в родительском контейнере.

minimumLayoutSize: public abstract Dimension minimumLayoutSize(Container parent).

Этот метод вычисляет минимальные ширину и высоту для указанной панели с учетом остальных компонентов в родительском контейнере.

Примеры

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

Дизайн с использованием фреймов: FlowLayout

Давайте попробуем использовать в апплете пересчета денежных сумм интерфейс, построенный с помощью фреймов. Оказывается, те же самые функции можно реализовать с помощью этого нового интерфейса, введя в исходный текст лишь очень незначительные изменения. Главное изменение состоит в том, что весь апплет теперь будет находиться в отдельном фрейме, а не в области окна броузера Web. В этом примере мы будем пользоваться менеджером FlowLayout, поскольку для нас сейчас главное - быстро получить результат, не тратя много усилий на точное задание расположения компонентов. Эта версия нашего апплета показана на рис. 8-2.

Рис. 8.2.

Пример 8-1a. Апплет, использующий фреймы. import java.awt.*;

import java.applet.Applet;

public class Window1 extends Applet { public void init(){

new Frame1();

}

} // конец Window1

Прежде всего нам нужно импортировать библиотеки, в которых находятся нужные нам классы. Обратите внимание, что класс Frame входит в пакет AWT, который импортируется в первой же строке нашей программы. Помните, что имя файла с исходным текстом апплета должно совпадать с именем класса апплета, объявление которого содержится в вышеприведенном примере (Window1). Первое, что делает наш апплет (как и те примеры, с которыми вы познакомились в предыдущей главе), - это вызов метода init. В примере 8-1a создается новый экземпляр класса

Ⱦɚɧɧɚɹ ɜɟɪɫɢɹ ɤɧɢɝɢ ɜɵɩɭɳɟɧɚ ɷɥɟɤɬɪɨɧɧɵɦ ɢɡɞɚɬɟɥɶɫɬɜɨɦ %RRNV VKRS Ɋɚɫɩɪɨɫɬɪɚɧɟɧɢɟ ɩɪɨɞɚɠɚ ɩɟɪɟɡɚɩɢɫɶ ɞɚɧɧɨɣ ɤɧɢɝɢ ɢɥɢ ɟɟ ɱɚɫɬɟɣ ɁȺɉɊȿɓȿɇɕ Ɉ ɜɫɟɯ ɧɚɪɭɲɟɧɢɹɯ ɩɪɨɫɶɛɚ ɫɨɨɛɳɚɬɶ ɩɨ ɚɞɪɟɫɭ piracy@books-shop.com

Frame1, определение которого приведено ниже. Расширяя этот класс, мы можем определять другие классы, включая в них соответствующее содержимое (в том числе код для обработки событий, происходящих во фрейме). Таким образом можно значительно упростить обработку событий в большом апплете, так как при этом события низкого уровня могут перехватываться и обрабатываться локально, то есть в пределах своей панели, фрейма или диалогового окна.

Пример 8-1b. Апплет, использующий фреймы. class Frame1 extends Frame {

float conversion_ratio = 1.5f; TextField field1 = new TextField(20); TextArea field2 = new TextArea(4,20); TextField ratioField = new TextField(5);

Scrollbar ratioScrollBar = new Scrollbar( Scrollbar.HORIZONTAL, 150, 25, 50, 250 );

//объявляем переменные и компоненты AWT,

//которые будут использоваться в Frame1 public Frame1() {

setLayout(new FlowLayout());

setFont(new Font("Helvetica", Font.PLAIN, 16)); setBackground(Color.gray); field1.setEditable(true); field2.setEditable(false);

add(ratioField);

add(ratioScrollBar);

add(field1);

add(field2);

ratioField.setText("1.50");

resize(field1.preferredSize());

resize(field2.preferredSize());

resize(300,250);

pack();

show();

} // конец Frame1

Поскольку в данном случае мы добавляем новое содержимое к стандартному классу фрейма, мы используем ключевое слово extends. Возможно, вам не совсем понятно, зачем это нужно делать, если мы уже определили класс Frame1. Вспомните, однако, что каждый апплет мы начинаем с объявления "...AppletName extends Applet". Как и в том случае, здесь мы создаем специализированный конструктор. Таким образом, мы получаем свой собственный компонент - в данном случае фрейм, - который затем можно использовать многократно и на основе которого можно создавать другие классы.

Оставшаяся часть примера почти идентична последнему примеру предыдущей главы. Метод setLayout задает менеджер размещения, используемый в созданном фрейме. Метод setFont устанавливает шрифт для вывода текста во фрейме. Аналогичным образом можно установить шрифт для ввода любого из компонентов. После того как мы создали класс Frame1, все компоненты, которые мы будем создавать в нашем апплете, будут попадать во фрейм Frame1, а не в область апплета в броузере Web. Установив размеры фрейма 300 на 250 пикселов, мы вызываем метод pack, который пытается как можно более тесно расположить компоненты в контейнере в пределах ограничений, налагаемых менеджером размещения. Наконец, мы вызываем метод show для вывода фрейма на экран.

Пример 8-1c. Апплет, использующий фреймы. public void convert() {

float currency1, currency2;

String InputString = field1.getText(); field1.setText("");

currency1 = Float.valueOf(InputString).floatValue(); currency2 = conversion_ratio * currency1;

String OutputString = "$" + InputString + " = " + "#" + Float.toString(currency2) + "\n";

field2.appendText(OutputString); } // конец convert

public boolean handleEvent(Event evt) { if (evt.target == ratioScrollBar)

www.books-shop.com

{

int in;

in = ratioScrollBar.getValue(); conversion_ratio = in/100f;

ratioField.setText(Float.toString(conversion_ratio));

}

if (evt.target == field1)

{

char c=(char)evt.key; if (c == '\n')

{

convert();

}

}

return false;

}

} // конец апплета

Метод convert в этом примере ничем не отличается от одноименного метода в примере апплета, с которым мы работали в предыдущей главе. То же самое можно сказать и о методе handleEvent, что особенно интересно, если учесть, что теперь этому методу приходится работать с фреймом. Это объясняется тем, что создаваемый фрейм является расширением и код, отвечающий за обработчику событий, добавлен непосредственно в свойства фрейма. При этом переопределяется только метод handleEvent, принадлежащий классу Frame, а одноименный метод в других классах (например, в базовом классе апплета Window1) остается нетронутым.

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

Диалоговый апплет: BorderLayout

На рис. 8-3 показан апплет, пользующийся простейшим диалоговым окном с одной кнопкой. Этот пример поможет нам разобраться в том, как с помощью диалоговых окон привлечь внимание пользователя и заставить его отреагировать на вопрос или сообщение. В этом примере мы не будем делать еще одну версию апплета пересчета денежных сумм, поскольку диалоговые окна обычно используются для одноразовых запросов, а не для интерактивной работы. Здесь мы будем пользоваться менеджером BorderLayout, который позволит разместить компоненты интерфейса в соответствующих местах окна: текстовая надпись должна быть вверху, инструкция пользователю - в середине, а кнопка Continue - внизу.

Рис. 8.3.

Пример 8-2a. Апплет с диалоговым окном. import java.awt.*;

import java.applet.Applet;

public class Show_Dialog extends Applet { Frame f;

www.books-shop.com

Intro_Dialog Hello_Dialog; public void init(){

f = new Frame(); f.resize(50,50); f.pack();

Hello_Dialog = new Intro_Dialog(f); add(new Button("Show Me!")));

}

public boolean action (Event evn, Object obj)

{

if (obj == "Show Me!"_ Hello_Dialog.show(); return true;

}

return false;

}

} // конец апплета Show_Dialog

Зачем в этом примере определяется класс Frame? Причина проста: каждое диалоговое окно должно иметь в качестве родителя фрейм. Intro_Dialog - это новый класс, определенный как расширение класса Dialog, а Hello_Dialog - экземпляр этого класса.

СОВЕТ Помните, что любое диалоговое окно должно иметь фрейм в качестве родителя. Поэтому вам придется объявить фрейм, даже если он не выполняет никаких других функций.

В следующем фрагменте кода создается экземпляр класса Frame, хотя выводиться на экран созданный фрейм не будет. Инициализация размера и упаковка фрейма (методом pack) - это не более чем меры безопасности, поскольку, если фрейм останется неинициализированным, а диалоговое окно будет на него ссылаться, может произойти исключение NullPointerException.

Затем мы создаем новый экземпляр класса Intro_Dialog и передаем только что созданный фрейм, делая его родителем созданного диалогового окна Hello_Dialog. Обратите внимание, что кнопка в этом случае добавляется к апплету, а не к фрейму или диалоговому окну, так как эта кнопка используется для вызова на экран диалогового окна. Обработка событий, закодированная выше, относится только к самому апплету, так как все события, происходящие в диалоговом окне, будут обрабатываться с помощью кода, включенного прямо в объявление класса, приведенное ниже. Пока что мы ждем нажатия кнопки "Show Me!" и, перехватив это событие, выводим на экран Hello_Dialog.

Пример 8-2b. Апплет с диалоговым окном, использующий экземпляр фрейма. class Intro_Dialog extends Dialog {

public Intro_Dialog(Frame parent) { super(parent, "Hello", true); setLayout(new BorderLayout());

setFont(new Font("Helvetica", Font.PLAIN, 12)); setBackground(Color.gray);

add("North", new Label("Hello!", Label.CENTER)); add("Center", new Label("Click _Continue_ to close this

Dialog", Label.CENTER));

add("South", new Button("Continue")); resize(250,250);

}

public boolean handleEvent(Event evt)

{

if (evt.id == Event.ACTION_EVENT )

{

if("Continue".equals(evt.arg))

{

dispose(); return true;

}

}

return false;

www.books-shop.com

}

} // конец апплета

При инициализации диалогового окна ему могут быть переданы несколько параметров. Поскольку мы используем конструктор Intro_Dialog, принадлежащий к классу Intro_Dialog, эти параметры нужно передать в вышестоящий класс Show_Dialog. Оператор setLayout в действительности можно опустить, потому что менеджер BorderLayout и без того является менеджером по умолчанию для фреймов и диалоговых окон. Мы включили эту строку в пример в качестве напоминания о том, что вызов метода add(newComponent) не будет работать, если не указать дополнительный параметр расположения, - например, add("Center", newComponent). Затем мы добавляем две текстовые надписи и кнопку с надписью "Continue". Метод handleEvent ждет нажатия этой кнопки; когда оно происходит, имя кнопки передается в параметре evt.arg. Проверив значение этого параметра, метод закрывает диалоговое окно. Как и в предыдущих примерах, это можно было бы сделать также с помощью обработчика событий mouse_click или action, однако использование handleEvent позволяет собрать обработку всех событий в одном месте, что весьма удобно.

Апплет с панелями: BorderLayout

Панель - это простейший из всех существующих в AWT контейнеров. Панели удобно использовать для группирования надписей, кнопок и других элементов интерфейса апплета, работающего в окне броузера. Однако можно применять панели и в апплетах, создающих собственные фреймы или диалоговые окна. В этом примере мы также будем пользоваться менеджером BorderLayout для размещения компонентов панели.

В этом разделе мы познакомимся с использованием панелей. Однако мы не будем приводить полностью код всего апплета, а ограничимся лишь частью, имеющей отношение к панелям. Сначала мы создадим две панели, в одной из которых будет размещаться полоса прокрутки и текстовое поле для вывода коэффициента пересчета, а в другой - два текстовых поля для ввода и вывода численных значений. Первая панель будет называться LeftPanel, вторая - RightPanel. Как только панели будут добавлены к апплету, все четыре элемента интерфейса станут доступными (рис. 8-4).

Рис. 8.4.

Пример 8-3a. Апплет, использующий панели. import java.awt.*;

import java.applet.Applet;

public class Panel1 extends Applet { public void init() {

new Frame1();

}

} // конец Panel1

class Panel_n_Frame extends Panel_n_Frame { float conversion_ratio = 1.5f; TextField field1 = new TextField(20); TextArea field2 = new TextArea(4,20); TextField ratioField = new TextField(5);

Scrollbar ratioScrollBar = new Scrollbar( Scrollbar.VERTICAL, 150, 25, 50, 250 );

public Panel_n_Frame() {

Panel RightPanel = new Panel(); Panel LeftPanel = new Panel(); setLayout(new BorderLayout());

www.books-shop.com

setFont(new Font("Helvetica", Font.PLAIN, 16)); setBackground(Color.gray);

field1.setEditable(true);

field2.setEditable(false); LeftPanel.setLayout(new BorderLayout()); LeftPanel.add("Center", ratioField); LeftPanel.add("North", ratioScrollBar); RightPanel.setLayout(new BorderLayout()); RightPanel.add("North", field1); RightPanel.add("South", field2); ratioField.setText("1.50"); resize(field1.preferredSize()); resize(field2.preferredSize()); resize(300,250);

add("East", RightPanel); add("West", LeftPanel); pack();

show(); } // конец init

Здесь для разнообразия мы пользуемся вертикальной полосой прокрутки. Создав панели RightPanel и LeftPanel, мы добавляем к ним компоненты интерфейса. Обратите внимание, что методы setLayout и setFont принадлежат к классу Panel_n_Frame, а не RightPanel и LeftPanel. Если бы интерфейс апплета располагался в окне Web-броузера, а не в отдельном окне, мы бы тем самым меняли свойства той части окна, которая отведена для работы апплета.

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

BorderLayout требует указания одного из параметров - North, South, East, West или Center.

Аргумент Center, помимо центрирования, имеет также побочный эффект - компонент, указанный с этим параметром, растягивается насколько возможно, заполняя все свободное пространство контейнера. Так, если указать для ratioScrollbar параметр Center вместо West, результат будет довольно забавным - полоса прокрутки увеличится в размерах и займет все свободное место на левой панели. С другой стороны, поле редактирования или холст можно добавлять с этим параметром, так как природа этих элементов допускает свободное масштабирование без искажений. Добавив к панели все необходимые элементы, мы должны будем добавить сами панели в апплет - имеющиеся в панелях компоненты при этом будут добавлены автоматически.

Пример 8-3b. Апплет, использующий панели. public void convert() {

float currency1, currency2;

String InputString = field1.getText(); field1.setText("");

currency1 = Float.valueOf(InputString).floatValue(); currency2 = conversion_ratio * currency1;

String OutputString = "$" + InputString + " = " + "#" + Float.toString(currency2) + "\n";

field2.appendText(OutputString); } // конец convert

public boolean handleEvent(Event evt) { if (evt.target == ratioScrollBar)

{

int in;

in = ratioScrollBar.getValue(); conversion_ratio = in/100f;

ratioField.setText(Float.toString(conversion_ratio));

}

if (evt.target == field1)

{

char c=(char)evt.key; if (c == '\n')

{

convert();

}

}

www.books-shop.com

if (evt.id == Event.WINDOW_DESTROY) { dispose();

return true;

}

return false;

}

} // конец апплета

Постойте-ка, что означает это WINDOW_DESTROY? Дело в том, что мы обязательно должны обработать событие, генерируемое, когда пользователь щелкает по системной кнопке закрытия окна (или, что то же самое, фрейма). В первом из примеров, где мы знакомились с фреймами, мы этого не делали, но в настоящих апплетах без этого не обойтись. Если вы запустите еще раз апплет из первого примера этой главы и нажмете на кнопку закрытия окна, ничего не произойдет, так как в программе не предусмотрено никаких действий на этот случай. Такой фрейм можно даже свернуть, но уничтожить его полностью можно только одним способом - закрыв запустивший его Web-броузер.

Элементы одинакового размера: GridLayout

Менеджер GridLayout особенно полезен для апплетов, использующих графические изображения. Этот менеджер создает решетку, состоящую из квадратов одинакового размера, в каждом из которых расположен один компонент. Например, с помощью этого менеджера в учебной программе из главы 18 создается шахматная доска, каждое поле которой содержит холст (Canvas), на котором выводится изображение шахматной фигуры. Использование менеджера GridLayout в апплете пересчета денежных сумм показано в примере 8-4.

Пример 8-4. Апплет, использующий менеджер GridLayout. import java.awt.*;

import java.applet.Applet;

public class Grid_Layout1 extends Applet { float conversion_ratio = 1.5f; TextField field1 = new TextField(5); TextArea field2 = new TextArea(4,20); TextField ratioField = new TextField(5);

Scrollbar ratioScrollBar = new Scrollbar( Scrollbar.HORIZONTAL, 150, 25, 50, 250 );

public void init() {

setLayout(new GridLayout(3,3)); setFont(new Font("Helvetica", Font.PLAIN, 12));

setBackground(Color.gray);

ratioField.setEditable(false);

field1.setEditable(true);

field2.setEditable(false);

ratioField.setText("1.50");

field1.resize(field1.preferredSize());

field2.resize(field2.preferredSize());

ratioField.resize(15,25);

ratioScrollBar.resize(ratioScrollBar.minimumSize());

add(ratioField);

add(ratioScrollBar);

add(field1);

add(field2);

} // конец init

Внешний вид апплета показан на рис. 8-5. Обратите внимание, как полоса прокрутки и другие компоненты интерфейса, будучи помещены в прямоугольные ячейки, изменили свои пропорции.

www.books-shop.com

Рис. 8.5.

В этом примере реализована решетка размером две ячейки в высоту и две ячейки в ширину, так как весь пример содержит лишь четыре органа управления. На рис. 8-5 хорошо видно, как этот менеджер размещения поступает в ситуации, когда величина ячейки превышает размеры помещаемого в нее компонента.

После создания решетки она, как и в предыдущем примере, заполняется компонентами. Добавление компонентов при использовании менеджера GridLayout не требует указания никаких дополнительных параметров, так как каждый новый компонент продолжает текущий ряд слева направо, а когда ряд завершен, начинает следующий. Остальная часть кода в этом примере аналогична предыдущим примерам; она содержит метод convert и метод handleEvent, обеспечивающие реакцию на нажатие клавиш. При инициализации менеджера размещения ему передаются два параметра, указывающие размер создаваемой решетки. Если один из этих параметров равен нулю, это значит, что в соответствующем направлении решетка может расти настолько, насколько позволяют ограничения контейнера и размеры компонентов.

СОВЕТ Чтобы решетка могла расти в каком-то из направлений без ограниченя количества ячеек, приравняйте нулю соответствующий параметр инициализации менеджера GridLayout.

Динамическая смена компонентов: CardLayout

Менеджер CardLayout позволяет динамически заменять выводимые на экран компоненты, что особенно удобно делать с панелями, содержащими наборы вложенных компонентов. Пример 8-5 иллюстрирует реализацию такого интерфейса. В этом примере все органы управления, относящиеся к изменению коэффициента пересчета, собраны в одной панели (рис. 8-6), а поля редактирования для ввода и вывода пересчитываемых значений - в другой панели (рис. 8-7). Переключение между этими панелями осуществляется выбором одной из двух позиций из выпадающего списка вверху апплета.

www.books-shop.com

Рис. 8.6.

Пример 8-5a. Апплет, использующий менеджер CardLayout. import java.awt.*;

import java.applet.Applet;

public class LayoutCard extends Applet { float conversion_ratio = 1.5f; TextField field1 = new TextField(20); TextArea field2 = new TextArea(4,20);

TextField ratioField = new TextField(5);

Scrollbar ratioScrollBar = new Scrollbar( Scrollbar.HORIZONTAL, 150, 25, 50, 250 );

Panel cards_panel; public void init() {

setLayout(new BorderLayout());

setFont(new Font("Helvetica", Font.PLAIN, 12)); setBackground(Color.gray); field1.setEditable(true); field2.setEditable(false); ratioField.setText("1.50"); field1.resize(field1.preferredSize()); field2.resize(field2.preferredSize());

ratioField.resize(ratioField.preferredSize());

ratioScrollBar.resize(ratioScrollBar.minimumSize());

Панель, в которой будет действовать менеджер размещения CardLayout, необходимо объявить как глобальную, поскольку события, происходящие в этой панели, меняют выводимые части интерфейса и потому не могут быть ограничены вложенными панелями. Оператор setLayout(new BorderLayout()) в вышеприведенном фрагменте устанавливает менеджер размещения для окна апплета:

Panel options_panel = new Panel(); Choice options = new Choice(); options.addItem("Adjust Ratio"); options.addItem("Calculate"); options_panel.add(options); add("North", options_panel); cards_panel = new Panel(); cards_panel.setLayout(new CardLayout()); Panel ratio_panel = new Panel();

www.books-shop.com

Panel calculate_panel = new Panel(); ratio_panel.add(ratioField); ratio_panel.add(ratioScrollBar); calculate_panel.add(field1); calculate_panel.add(field2); cards_panel.add("Adjust Ratio", ratio_panel); cards_panel.add("Calculate", calculate_panel);

add("Center", cards_panel); show();

}// конец init

Вэтом фрагменте в панель под названием options_panel добавляется выпадающий список (Choice). Этот выпадающий список содержит два элемента - "Adjust Ratio" и "Calculate". Затем панель options_panel, содержащая выпадающий список, добавляется в верхнюю часть окна апплета. После этого следует определение панели, в которой будет работать менеджер CardLayout. Как обычно, создается новый экземпляр панели и ему приписывается менеджер размещения CardLayout, а затем создаются еще две панели, которые и будут сменяться на экране при помощи менеджера CardLayout. Эти две панели добавляются в панель cards_panel. Одна из этих панелей содержит поле для вывода коэффициента пересчета и полосу прокрутки для его изменения, а другая - два поля редактирования для ввода исходного значения и вывода результата пересчета. При добавлении этих двух панелей в панель cards_panel им приписываются метки (labels) - текстовые строки "Adjust Ratio" и "Calculate".

После этого панель cards_panel добавляется в окно апплета, которое, таким образом, содержит две панели - cards_panel и options_panel. То, в каком порядке вложенные панели добавлялись в cards_panel, определяет порядок их вывода на экран при вызове методов next или previous.

Кроме того, можно вывести любую из имеющихся панелей с помощью метода show, входящего в класс CardLayout, - именно этот метод мы и реализуем ниже в обработчике событий action.

Пример 8-5b. Апплет, использующий менеджер CardLayout. public void convert() {

float currency1, currency2;

String InputString = field1.getText(); field1.setText("");

currency1 = Float.valueOf(InputString).floatValue(); currency2 = conversion_ratio * currency1;

String OutputString =

"$" + InputString + " = " + "#" + Float.toString(currency2) + "\n"; field2.appendText(OutputString);

} // конец convert

public synchronized boolean handleEvent(Event evt) { if (evt.target == ratioScrollBar)

{

int in;

in = ratioScrollBar.getValue(); conversion_ratio = in/100f;

ratioField.setText(Float.toString(conversion_ratio));

}

if (evt.target == field1)

{

char c=(char)evt.key; if (c == '\n')

{

convert(); return true;

}

else { return false; }

}

return super.handleEvent(evt);

}

При объявлении метода handleEvent нам пришлось использовать ключевое слово synchronized. Это связано с тем, что в том же апплете используется обработчик событий action, и требуется обеспечить, чтобы эти два обработчика событий не вызывались в одно и то же время. Мы могли бы перенести весь код обработки событий в метод action; но в этом примере мы избрали другой

www.books-shop.com