По соображениям безопасности отметим, что наша деятельность в апплетах очень ограничена. Мы действительно чувствуем, что апплет временно внедряется в веб-браузер, поэтому его возможности, знания, контролируемые элементы должны быть строго ограниченными. Однако нам хотелось бы, чтобы Java мог создать оконное приложение для выполнения некоторых задач, а не просто располагался на веб-странице. Возможно, мы хотели бы, чтобы это приложение выполняло надёжные приложения с выдающейся мобильностью и реального времени. В ранних главах этой книги мы создали несколько консольных приложений, но в некоторых операционных системах (например, Macintosh) нет консоли. Поэтому у нас есть множество причин использовать Java для создания полноценного оконного приложения, отличного от апплета. Это, конечно, вполне разумное требование.Окно Java-приложения может содержать меню и диалоговые окна (что невозможно и затруднительно для апплета). Однако, если мы используем старую версию Java, мы потеряем внешний вид и ощущение локальной операционной системы. Библиотека JFC/Swing позволяет нам создать приложение, которое будет соответствовать внешнему виду и ощущению локальной операционной системы. Если мы хотим создать оконное приложение, которое будет работать корректно, то важно использовать последнюю версию Java вместе со всеми необходимыми инструментами, чтобы выпустить приложение, которое не вызовет путаницы у пользователя. Если по какой-то причине нам придётся использовать старую версию Java, то перед началом создания важного оконного приложения следует внимательно всё обдумать.## 13.15.1 Меню
Прямое использование меню в апплете невозможно (Java 1.0, Java 1.1 и библиотека Swing этого не позволяют), так как они предназначены для приложений. Продолжим: если вы не доверяете мне и уверены, что можно использовать меню в апплете, попробуйте сами. В апплете нет метода setMenuBar()
, который присутствует в меню (мы увидим, что он может использоваться для создания фрейма в апплете, содержащего меню).
Существуют четыре различных типа компонентов MenuComponent
(компоненты меню): меню-полоса (мы можем иметь одну меню-полосу в событийном фрейме), меню, которое управляет отдельным раскрывающимся меню или подменю, элемент меню, который представляет собой отдельный пункт меню, и компонент MenuItem
, который создает флажок (checkmark) для показа, выбран ли пункт меню. Разные системы используют различные ресурсы, а для Java и AWT нам необходимо вручную собирать все меню в исходном коде.```java
//: Menu1.java
// Меню работают только с Frames.
// Показывает подменю, пункты меню со свитками,
// и замену меню.
import java.awt.*;
public class Menu1 extends Frame { String[] вкусы = {"Шоколад", "Смородина", "Ванильный крем сироп", "Мятный чип", "Мохко-ореховый крем", "Рому со изюмом", "Пралине крем", "Кофейный пирог"}; TextField t = new TextField("Нет вкуса", 30); MenuBar mb1 = new MenuBar(); Menu f = new Menu("Файл"); Menu m = new Menu("Вкусы"); Menu s = new Menu("Безопасность"); // Альтернативный подход: CheckboxMenuItem[] безопасность = { new CheckboxMenuItem("Защита"), new CheckboxMenuItem("Скрыть") }; MenuItem[] файл = { new MenuItem("Открыть"), new MenuItem("Выход") }; // Второе меню для замены: MenuBar mb2 = new MenuBar(); Menu fooBar = new Menu("fooBar"); MenuItem[] другой = { new MenuItem("Foo"), new MenuItem("Bar"), new MenuItem("Baz") }; Button b = new Button("Переключить меню"); public Menu1() { for (int i = 0; i < вкусы.length; i++) { m.add(new MenuItem(вкусы[i])); // Добавляем разделители через каждые три пункта: if ((i + 1) % 3 == 0) m.addSeparator(); } for (int i = 0; i < безопасность.length; i++) s.add(безопасность[i]); f.add(s); for (int i = 0; i < файл.length; i++) f.add(файл[i]); mb1.add(f); mb1.add(m); setMenuBar(mb1); t.setEditable(false); add("Центр", t); // Устанавливаем систему для переключения меню: add("Верхний край", b); for (int i = 0; i < другой.length; i++) fooBar.add(другой[i]); mb2.add(fooBar); } public boolean handleEvent(Event evt) { if (evt.id == Event.WINDOW_DESTROY) System.exit(0); else return super.handleEvent(evt); } }
return true;
}
public boolean action(Event evt, Object arg) {
if(evt.target.equals(b)) {
MenuBar m = getMenuBar();
if(m == mb1) setMenuBar(mb2);
else if (m == mb2) setMenuBar(mb1);
} else if(evt.target instanceof MenuItem) {
if(arg.equals("Открыть")) {
String s = t.getText();
boolean выбран = false;
for(int i = 0; i < вкусы.length; i++)
if(s.equals(вкусы[i])) выбран = true;
if(!выбран)
t.setText("Выберите вкус! ");
else
t.setText("Открываем " + s + ". О-о, как вкусно! ");
} else if(evt.target.equals(файл[1]))
System.exit(0);
// CheckboxMenuItems cannot use string comparison;
// you should compare the target object:
else if(evt.target.equals(безопасность[0]))
t.setText("Безопасность включена");
}
return true;
}
``````java
}
return true;
}
``````markdown
setText("Защитите мороженое! " +
"Защита включена: " + safety[0].getState());
} else if (evt.target.equals(safety[1])) {
t.setText("Спрячьте мороженое! " +
"Холодно ли? " + safety[1].getState());
} else {
t.setText(arg.toString());
}
} else {
return super.action(evt, arg);
}
return true;
}
public static void main(String[] args) {
Menu1 f = new Menu1();
f.resize(300, 200);
f.show();
}
} ///:~
```
В этом программном обеспечении я избежал написания типичных длинных вызовов `add()` для каждого меню, поскольку это выглядело как множество бесполезных меток. Вместо этого я помещаю пункты меню в массив, а затем просто прохожу через каждый массив с помощью цикла `for`, вызывая `add()`. Таким образом, добавление и удаление пунктов меню становится менее неприятным.
Как альтернативный метод (который мне было трудно удовлетворить, так как он требует большего количества распределений) создание `CheckboxMenuItems` в массивах называется безопасной инициализацией; это действительно безопасно для массивов файлов и других файлов.Программа создаёт не одно, а два меню, чтобы показать, что пункты меню могут быть активированы путём их замены во время выполнения программы. Мы видим, как пункты меню составляют меню, каждое меню состоит из пунктов меню (`menuItems`), `checkboxMenuItems` или других меню (что создаёт подменю). Когда меню собраны, они могут быть установлены в текущую программу с помощью метода `setMenuBar()`. При нажатии кнопки проверяется текущее установочное меню с помощью метода `getMenuBar()`, после чего другое меню устанавливается на его место.При тестировании значения "открыто" (т. е., начального состояния), обратите внимание на правильность написания и регистра символов. Если объекта нет при запуске, Java отправляет сигнал "нет ошибки" (нет ошибки). Такой сравнительный анализ строк является очевидным источником ошибок в дизайне программы.Проверочные и невалидируемые пункты меню автоматически работают; связанные с ними `CheckBoxMenuItems` являются довольно неожиданными, поскольку некоторые из них не позволяют совпадению строковых значений (это кажется парадоксальным, хотя сравнение строковых значений не всегда является лучшим подходом). Поэтому мы можем сравнивать целевой объект вместо их меток. При демонстрации используется метод `getState()` для отображения состояния. Также можно использовать метод `setState()` для изменения состояния `CheckboxMenuItem`. Мы можем считать, что меню может быть разумно помещено в более одного контейнера меню. Это кажется логичным, так как все методы `add()` забытых нами контейнеров являются ссылками. Однако, если мы попробуем это сделать, результат будет крайне запутанным и далек от того, чего мы ожидали (невозможно понять, является ли это ошибкой программирования или намеренным поведением). Этот пример также демонстрирует, почему нам следует создать приложение вместо апплета (это потому, что приложения поддерживают меню, тогда как апплеты этого делать не могут). Мы наследуемся от фрейма вместо апплета. Кроме того, мы создаём конструктор для класса вместо установки событий через `init()`. В конце концов, мы создаём метод `main()`, где масштабируем наш новый объект и вызываем `show()`.Он отличается от апплета лишь немного, но уже представляет собой самостоятельное окно приложения с возможностью использования меню. ## 13.15.2 Диалоговое окноДиалоговое окно — это всплывающее окно, которое открывается поверх других окон. Его цель — обрабатывать специфические задачи и детали, не создавая путаницы в основном окне. Диалоговые окна широко используются в средах программирования для настроек, но как уже упоминалось, они редко встречаются в обычных программах.
Чтобы создать другие типы окон, такие как диалоговые окна, мы должны наследовать от диалогового класса. В отличие от основных окон, диалоговые окна не могут иметь меню или менять курсор, однако они очень похожи во всех остальных аспектах. Диалоговое окно имеет менеджер компоновки (по умолчанию используется `BorderLayout`) и переопределяет методы `action()`, или использует метод `handleEvent()` для обработки событий. Важной особенностью использования метода `handleEvent()` является то, что при получении события `WINDOW_DESTROY` мы не хотим закрывать запущенное приложение!Вместо этого можно использовать диалоговое окно для освобождения ресурсов с помощью вызова `dispatch()`. В следующем примере диалоговое окно состоит из сетки специальных кнопок типа `ToeButton`, определённых там как класс (используется менеджер компоновки `GridLayout`). Кнопка `ToeButton` рисует вокруг себя рамку и зависит от своего состояния: либо отображает `X`, либо `O`. Она начинается пустой, затем, в зависимости от выбора пользователя, преобразуется в `X` или `O`. Однако при нажатии на кнопку она будет чередовать между `X` и `O` (что создаёт ощущение игры в крестик-нолик, хотя и более раздражающей).Кроме того, это диалоговое окно можно настроить для изменения количества строк и столбцов в главном окне программы.```java
//: ToeTest.java
// Demonstration of dialog boxes and creating custom components
import java.awt.*;
```
```java
class ToeButton extends Canvas {
int состояние = ToeDialog.BLANK;
ToeDialog родитель;
ToeButton(ToeDialog родитель) {
этот.родитель = родитель;
}
public void paint(Graphics g) {
int x1 = 0;
int y1 = 0;
int x2 = размер().ширина - 1;
int y2 = размер().высота - 1;
g.drawRectangle(x1, y1, x2, y2);
x1 = x2 / 4;
y1 = y2 / 4;
int ширина = x2 / 2;
int высота = y2 / 2;
if (состояние == ToeDialog.XX) {
g.drawLine(x1, y1, x1 + ширина, y1 + высота);
g.drawLine(x1, y1 + высота, x1 + ширина, y1);
}
if (состояние == ToeDialog.OO) {
g.drawOval(x1, y1, x1 + ширина / 2, y1 + высота / 2);
}
}
public boolean мышьНажата(Event evt, int x, int y) {
if (состояние == ToeDialog.BLANK) {
состояние = родитель.очередь;
родитель.очередь = (родитель.очередь == ToeDialog.XX ? ToeDialog.OO : ToeDialog.XX);
} else {
состояние = (состояние == ToeDialog.XX ? ToeDialog.OO : ToeDialog.XX);
}
перерисовать();
return true;
}
}
```
Примечание: В данном контексте, поскольку `ToeDialog` является частью кода, его имя было оставлено без изменения в соответствии с правилами перевода.
```markdown
Класс `ToeDialog` расширяет класс `Dialog`.
```java
class ToeDialog extends Dialog {
// w = количество клеток в ширину
// h = количество клеток в высоту
static final int ПУСТОЕ = 0;
static final int КРУГ = 1;
static final int КРЕСТИК = 2;
int ход = КРЕСТИК; // Начинается с хода крестика
public ToeDialog(Frame parent, int w, int h) {
super(parent, "Игра сама по себе", false);
setLayout(new GridLayout(w, h));
for (int i = 0; i < w * h; i++) {
```
```java
add(new ToeButton(this));
resize(w * 50, h * 50);
}
public boolean handleEvent(Event evt) {
if (evt.id == Event.WINDOW_DESTROY) {
dispose();
} else {
return super.handleEvent(evt);
}
return true;
}
}
```Класс `ToeTest` расширяет класс `Frame`.
```java
public class ToeTest extends Frame {
TextField строки = new TextField("3");
TextField столбцы = new TextField("3");
public ToeTest() {
setTitle("Toe Test");
Panel панель = new Panel();
панель.setLayout(new GridLayout(2, 2));
панель.add(new Label("Строки", Label.CENTER));
панель.add(строки);
панель.add(new Label("Столбцы", Label.CENTER));
панель.add(столбцы);
add("North", панель);
add("South", new Button("go"));
}
public boolean handleEvent(Event evt) {
if (evt.id == Event.WINDOW_DESTROY) {
System.exit(0);
} else {
return super.handleEvent(evt);
}
return true;
}
public boolean action(Event evt, Object arg) {
if (arg.equals("go")) {
Dialog d = new ToeDialog(this,
Integer.parseInt(строки.getText()),
Integer.parseInt(столбцы.getText()));
d.show();
} else {
return super.action(evt, arg);
}
return true;
}
public static void main(String[] args) {
Frame f = new ToeTest();
f.resize(200, 100);
f.show();
}
}
```
///:
`ToeButton` класс сохраняет ссылку на его родительский объект типа `ToeDialog`. Как было указано выше, `ToeButton` и `ToeDialog` имеют тесную связь, так как один `ToeButton` может использоваться только одним `ToeDialog`, но это решение позволяет решать множество задач. В действительности, это не является плохим подходом, поскольку нет других диалоговых окон для отслеживания выбора пользователя. Конечно, можно использовать другой способ для изменения состояния `ToeDialog.turn` (статического поля `ToeButton`). Этот метод снижает их тесную связь, но препятствует созданию нескольких `ToeDialog` (хотя бы один из которых работал нормально).Метод `paint()` связан с графикой: он рисует прямоугольник вокруг кнопки и рисует `X` или `O`. Это полностью вычислительная работа, но она очень наглядна.
```Пойманный одиночным щелчком мыши перегруженный метод `mouseDown()` проверяет наличие события на кнопке. В случае отсутствия события родительское окно запрашивается для выявления выбранной кнопки и установки её состояния. Отметим, что после этого кнопка передаётся обратно в родительский класс, где меняется её выбор. Если состояние кнопки уже установлено как 'X' или 'O', то оно будет изменено. Мы можем заметить использование трёх групповых конструкций `if-else`, описанных в третьей главе данного руководства, для удобства этих вычислений. После изменения состояния кнопки она перерисовывается.
Конструктор класса `ToeDialog` очень прост: он добавляет несколько кнопок в менеджер компоновки `GridLayout`, а затем устанавливает ширину и высоту каждой кнопки равной 50 пикселям (если размер окна не будет скорректирован, кнопки не будут видны). Обратите внимание, что метод `handleEvent()` вызывает `dispose()` при событии `WINDOW_DESTROY`, поэтому весь приложение не закроется.```
Перевод:
```Пойманный одиночным щелчком мыши перегруженный метод `mouseDown()` проверяет наличие события на кнопке. В случае отсутствия события родительское окно запрашивается для выявления выбранной кнопки и установки её состояния. Отметим, что после этого кнопка передаётся обратно в родительский класс, где меняется её выбор. Если состояние кнопки уже установлено как 'X' или 'O', то оно будет изменено. Мы можем заметить использование трёх групповых конструкций if-else, описанных в третьей главе данного руководства, для удобства этих вычислений. После изменения состояния кнопки она перерисовывается.
Конструктор класса ToeDialog очень прост: он добавляет несколько кнопок в менеджер компоновки GridLayout, а затем устанавливает ширину и высоту каждой кнопки равной 50 пикселям (если размер окна не будет скорректирован, кнопки не будут видны). Обратите внимание, что метод handleEvent() вызывает dispose() при событии WINDOW_DESTROY, поэтому весь приложение не закроется.```Класс `ToeTest` создаёт полное приложение с использованием `TextField` (для ввода строк и столбцов сетки кнопок) и кнопки `go`. Вы можете заметить, что метод `action()` использует не самую лучшую "строковую проверку" для определения нажатий кнопок (пожалуйста, убедитесь, что вы правильно записали все слова!). При нажатии кнопки данные из `TextField` извлекаются, и поскольку они представлены в виде строки, используется статический метод `Integer.parseInt()` для преобразования в целое число. Как только диалоговый класс создан, его следует активировать и показать вызовом метода `show()`.Обратите внимание, что объект класса `ToeDialog` присваивается ссылке на диалог `d`. Это пример приведенного выше преобразования, хотя это не создает значительных различий, так как все события вызываются через метод `show()`. Однако, если бы нам требовалось вызвать некоторые методы, существующие внутри класса `ToeDialog`, нам потребовалось бы использовать ссылку именно на этот класс, чтобы не потерять информацию в процессе преобразования.
(1) Диалоговое окно для работы с файлами
Многие операционные системы имеют множество специальных встроенных диалоговых окон для обработки событий выбора, таких как шрифты, цвета, принтеры и подобные. Почти все операционные системы поддерживают открытие и сохранение файлов, но Java предлагает более удобный способ использования класса `FileDialog`. Конечно, это больше не требует проверки всех используемых апплетов, так как апплеты не могут читать или записывать файлы на локальном диске (это было бы нарушением доверия новых браузеров к апплетам). Ниже представлен пример программы, которая использует два диалоговых окна для выбора файла — одно для открытия, другое для сохранения. Большая часть кода уже знакома нам, а все интересные действия происходят в методах `action()` при нажатии двух разных кнопок.```markdown
```java
//: FileDialogTest.java
// Demonstration of using dialog boxes for file operations
import java.awt.*;
public class FileDialogTest extends Frame {
TextField filename = new TextField();
TextField directory = new TextField();
Button open = new Button("Open");
Button save = new Button("Save");
public FileDialogTest() {
setTitle("File Dialog Window");
Panel p = new Panel();
p.setLayout(new FlowLayout());
p.add(open);
p.add(save);
add("South", p);
directory.setEditable(false);
filename.setEditable(false);
p = new Panel();
p.setLayout(new GridLayout(2, 1));
p.add(filename);
p.add(directory);
add("North", p);
}
public boolean handleEvent(Event evt) {
if (evt.id == Event.WINDOW_DESTROY)
System.exit(0);
else
return super.handleEvent(evt);
return true;
}
public boolean action(Event evt, Object arg) {
if (evt.target.equals(open)) {
// Two arguments, by default used to open a file:
FileDialog d = new FileDialog(this,
"Which file would you like to open?");
d.setFile("*.java"); // File name filter
d.setDirectory("."); // Current directory
d.show();
String openFile;
if ((openFile = d.getFile()) != null) {
filename.setText(openFile);
directory.setText(d.getDirectory());
} else {
filename.setText("You pressed Cancel");
directory.setText("");
}
} else if (evt.target.equals(save)) {
FileDialog d = new FileDialog(this,
"Which file would you like to save?",
FileDialog.SAVE);
d.setFile("*.java");
d.setDirectory(".");
d.show();
String saveFile;
if ((saveFile = d.getFile()) != null) {
filename.setText(saveFile);
directory.setText(d.getDirectory());
} else {
filename.setText("You pressed Cancel");
directory.setText("");
}
} else
return super.action(evt, arg);
return true;
}
```
``` public static void main(String[] args) {
Frame f = new FileDialogTest();
f.setSize(250, 110);
f.setVisible(true);
}
}
``` ///:~
```
Для диалогового окна "Открыть файл" мы используем конструктор для установки двух параметров; сначала это ссылка на родительское окно, а затем заголовок строки заголовков `FileDialog`. Метод `setFile()` предоставляет начальное имя файла — возможно, локальная операционная система поддерживает маски, поэтому во всех этих примерах все файлы с расширением `.java` будут отображаться в начале. Метод `setDirectory()` выбирает начальную директорию для выбора файла (обычно операционная система позволяет пользователю менять директорию).
Команда `show()` возвращает только после закрытия диалогового окна. Объект `FileDialog` продолжает существовать, поэтому мы можем читать данные из него. Если мы вызываем метод `getFile()`, и он возвращает пустое значение, это означает, что пользователь вышел из диалогового окна. Имя файла и результат вызова метода `getDirectory()` отображаются в полях типа `TextField`.
Сохранение работы кнопки выполняется тем же способом, за исключением использования другого конструктора из-за `FileDialog`. Этот конструктор устанавливает три параметра, причём третий параметр должен быть либо `FileDialog.SAVE`, либо `FileDialog.OPEN`.
```
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )