В версии Java 1.1 были добавлены важные новые возможности, включая проход фокуса, доступ к цветам рабочего стола, печать "в песочнице" и раннюю поддержку буфера обмена.
Проход фокуса очень прост, так как он явно существует в компонентах библиотеки AWT, и нам ничего дополнительно делать не требуется. Если мы создаем свои собственные компоненты и хотим сделать их способными обрабатывать проход фокуса, мы переопределяем метод isFocusTraversable()
, чтобы он возвращал истинное значение. Если мы хотим захватить клавиатурный фокус при одиночном щелчке мыши, мы можем захватить событие нажатия кнопки мыши и вызвать метод requestFocus()
для запроса фокуса.
Используя цвета рабочего стола, мы можем узнать, какие цвета выбраны текущим пользователем. Это позволяет использовать эти цвета в своих программах по мере необходимости. Цвета автоматически инициализируются и хранятся в статических членах класса SystemColor
. Единственное, что нам нужно сделать — это прочитать интересующие нас члены. Названия различных цветов очевидны: desktop
, activeCaption
, activeCaptionText
, activeCaptionBorder
, inactiveCaption
, inactiveCaptionText
, inactiveCaptionBorder
, window
, windowBorder
, windowText
, menu
, menuText
, text
, textText
, textHighlight
, textHighlightText
, textInactiveText
, control
, controlText
, controlHighlight
, controlLtHighlight
, controlShadow
, controlDkShadow
, scrollbar
, info
(для помощи) и infoText
(для текста помощи).## Пример печати
PrintDemo
Класс PrintDemo
расширяет класс Frame
. В нем создаются кнопки, поле ввода текста и выборщик.
public class PrintDemo extends Frame {
Button
printText = new Button("Печатать текст"),
printGraphics = new Button("Печатать графику");
TextField ringNum = new TextField(3);
Choice faces = new Choice();
Graphics g = null;
Plot plot = new Plot3(); // Попробуйте различные варианты графика
Toolkit tk = Toolkit.getDefaultToolkit();
public PrintDemo() {
ringNum.setText("3");
ringNum.addTextListener(new RingL());
Panel p = new Panel();
p.setLayout(new FlowLayout());
printText.addActionListener(new TBL());
p.add(printText);
p.add(new Label("Шрифт:"));
p.add(faces);
printGraphics.addActionListener(new GBL());
p.add(printGraphics);
}
}
``` p.add(new Label("Количество колец:"));
p.add(ringNum);
setLayout(new BorderLayout());
add(p, BorderLayout.NORTH);
add(plot, BorderLayout.CENTER);
String[] fontList = tk.getFontList();
for (int i = 0; i < fontList.length; i++) {
faces.add(fontList[i]);
}
faces.select("Сerif");
}
class PrintData {
public PrintJob pj;
public int pageWidth, pageHeight;
PrintData(String jobName) {
pj = getToolkit().getPrintJob(PrintDemo.this, jobName, null);
if (pj != null) {
pageWidth = pj.getPageDimension().width;
pageHeight = pj.getPageDimension().height;
g = pj.getGraphics();
}
}
void end() { pj.end(); }
}
class ChangeFont {
private int stringHeight;
ChangeFont(String face, int style, int point) {
if (g != null) {
g.setFont(new Font(face, style, point));
stringHeight = g.getFontMetrics().getHeight();
}
}
int stringWidth(String s) {
return g.getFontMetrics().stringWidth(s);
}
int stringHeight() { return stringHeight; }
}
class TBL implements ActionListener {
public void actionPerformed(ActionEvent e) {
PrintData pd = new PrintData("Пример печати текста");
// Нулл указывает на отмену операции печати:
if (pd == null) return;
}
}```java
String s = "PrintDemo";
ChangeFont cf = new ChangeFont(faces.getSelectedItem(), Font.ITALIC, 72);
g.drawString(s, (pd.pageWidth - cf.stringWidth(s)) / 2, (pd.pageHeight - cf.stringHeight()) / 3);
s = "Меньший размер шрифта";
cf = new ChangeFont(faces.getSelectedItem(), Font.BOLD, 48);
g.drawString(s, (pd.pageWidth - cf.stringWidth(s)) / 2, (int)((pd.pageHeight - cf.stringHeight()) / 1.5));
g.dispose();
pd.end();
}
class GBL implements ActionListener {
public void actionPerformed(ActionEvent e) {
PrintData pd = new PrintData("Пример печати графики");
if (pd == null) return;
plot.print(g);
g.dispose();
pd.end();
}
}
class RingL implements TextListener {
public void textValueChanged(TextEvent e) {
int i = 1;
try {
i = Integer.parseInt(ringNum.getText());
} catch (NumberFormatException ex) {
i = 1;
}
plot.rings = i;
plot.repaint();
}
}
public static void main(String[] args) {
Frame pdemo = new PrintDemo();
pdemo.setTitle("Пример печати");
pdemo.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
pdemo.setSize(500, 500);
pdemo.setVisible(true);
}
}
``````java
class Plot extends Canvas {
public int rings = 3;
}
class Plot1 extends Plot {
// Call to the default paint() method:
public void paint(Graphics g) {
int w = getSize().width;
int h = getSize().height;
int xc = w / 2;
int yc = w / 2;
int x = 0, y = 0;
for (int i = 0; i < rings; i++) {
if (x < xc && y < yc) {
g.drawOval(x, y, w, h);
x += 10;
y += 10;
w -= 20;
h -= 20;
}
}
}
}
class Plot2 extends Plot {
// To fit an image on a page, you need to know whether you are printing or drawing:
public void paint(Graphics g) {
int w, h;
if (g instanceof PrintGraphics) {
PrintJob pj = ((PrintGraphics) g).getPrintJob();
w = pj.getPageDimension().width;
h = pj.getPageDimension().height;
} else {
w = getSize().width;
h = getSize().height;
}
int xc = w / 2;
int yc = w / 2;
int x = 0, y = 0;
for (int i = 0; i < rings; i++) {
if (x < xc && y < yc) {
g.drawOval(x, y, w, h);
x += 10;
y += 10;
w -= 20;
h -= 20;
}
}
}
}
class Plot3 extends Plot {
// Better. Separating printing from drawing:
public void print(Graphics g) {
// Assume this is a PrintGraphics object:
PrintJob pj = ((PrintGraphics) g).getPrintJob();
int w = pj.getPageDimension().width;
int h = pj.getPageDimension().height;
doGraphics(g, w, h);
}
public void paint(Graphics g) {
int w = getSize().width;
int h = getSize().height;
doGraphics(g, w, h);
}
private void doGraphics(Graphics g, int w, int h) {
int xc = w / 2;
int yc = w / 2;
int x = 0, y = 0;
for (int i = 0; i < rings; i++) {
if (x < xc && y < yc) {
g.drawOval(x, y, w, h);
x += 10;
y += 10;
w -= 20;
h -= 20;
}
}
}
} ///:~
```Этот программный код позволяет нам выбирать шрифты из выпадающего списка (и мы заметим, что множество полезных шрифтов в Java 1.1 версии были строго ограничены, так как у нас нет хороших шрифтов установленных на наших машинах). Он использует эти шрифты для вывода жирного, курсивного и текста различных размеров. Кроме того, создается новый компонент для демонстрации графики. При печати графики, круги будут отображаться на экране и печататься на бумаге, а три производных класса `Plot1`, `Plot2`, `Plot3` используют различные методы для выполнения задачи, чтобы мы могли видеть выбранные нами элементы. Также можно изменять количество `rings` в одном графике — это интересно, поскольку это показывает хрупкость печати в Java 1.1 версии. В моей системе, когда счетчик количества `rings` становится "слишком большим" (то есть превышает определённое значение), принтер выдает ошибку и не работает правильно, тогда как при "довольно малых" значениях счетчика принтер работает хорошо. Мы также замечаем, что размер страницы может меняться при печати на бумаге с фактическими размерами, отличными от реальных. Эти характеристики могут быть внедрены в будущие выпуски Java, и мы можем использовать этот код для тестирования.Для повышения повторного использования любую функцию можно упаковать внутрь вложенного класса. Например, каждый раз, когда мне нужно начать работу над печатью (будь то графика или текст), мне нужно создать объект `PrintJob`, который имеет свои собственные объекты графики вместе со своими шириной и высотой. Созданный объект `PrintJob` и извлечённые размеры страницы упакованы внутри класса `PrintData`.
Печать текстаКонцепция печати текста проста и понятна: мы выбираем шрифт и размер, определяем положение строки на странице и используем метод `Graphics.drawString()`, чтобы вывести строку на странице. Это означает, что нам необходимо точно рассчитывать положение каждой строки на странице и убедиться, что она не выходит за границы нижней части страницы или не конфликтует с другими строками. Если мы хотим заниматься текстовым процессингом, то это будет задачей, которая хорошо подходит нам. Класс `ChangeFont` включает небольшое количество методов изменения шрифта от одного до другого, автоматически создаёт новый объект шрифта с нужным нам шрифтом, стилем (жирным и курсивом — подчеркивание и прочее пока не поддерживаются) и размером. Он также просто вычисляет ширину и высоту строки. Когда мы нажимаем кнопку "Print text", активируется приемник TBL. Мы можем заметить, как он последовательно создаёт объекты `ChangeFont` и вызывает метод `drawString()`, чтобы распечатать строку на заранее рассчитанной позиции. Обратите внимание, правильно ли эти расчёты приводят к ожидаемому результату (версия, которую я использовал, ошибок не содержала).(2) Печать графики
Нажатие кнопки "Печать графики" активирует приемник GBL. При необходимости печати создается объект `PrintData`, который инициализируется, а затем мы просто вызываем метод печати `print()` для этого компонента. Для принудительной печати нам необходимо вызвать метод обработки `dispose()` для графического объекта и метод завершения `end()` для объекта `PrintData` (или изменить его на вызов метода завершения `end()` для объекта `PrintJob`).Этот процесс продолжается в графических объектах. Мы видим, что базовый класс графики очень простой — он расширяет холст и включает вызов прерывания `ring`, указывающий, сколько центрированных колец должны быть нарисованы на этом специальном холсте. Эти три производных класса демонстрируют различные способы достижения одной цели: отображения на экране и печати на странице. `Plot1` использует самый простой метод программирования: игнорирует различия между отрисовкой и печатью, а также переопределяет метод отрисовки `paint()`. Причина использования такого подхода заключается в том, что по умолчанию метод печати `print()` просто меняет поведение, вызывая метод `paint()`. Однако мы заметим, что размер вывода зависит от размера экранной области, так как ширина и высота определяются при вызове метода `canvas.getSize()`. Поэтому это вполне логично. Если размер нашего изображения всегда остаётся постоянным, то такой подход может быть приемлемым. Когда внешний вид отрисованного объекта имеет такое большое значение, нам следует глубже понять важность его размеров. К сожалению, как мы увидим в `Plot2`, этот подход становится проблематичным. По какой-то непонятной причине, мы не можем попросту заставить графический объект отрисовывать свой внешний вид по своему размеру. Это сделало бы всю процедуру значительно лучше.Вместо этого, если мы печатаем вместо отрисовки, нам придётся использовать ключевое слово `instanceof` (как описано в главе 11 этой книги) для проверки типа `PrintGraphics`, затем выполнить приведение типов и вызвать уникальный метод `PrintGraphics`: `getPrintJob()`. Теперь у нас будет ссылка на `PrintJob`, и мы сможем получить высоту и ширину бумаги. Этот способ является хаком, но возможно, он имеет свои причины (в других аспектах, до настоящего времени мы видели некоторые дизайнерские решения библиотек, поэтому мы можем догадываться о мысли создателей этих решений). Можно заметить, что метод отрисовки `paint()` в `Plot2` рассматривает возможности печати и отображения. Однако, поскольку при печати вызывается метод `Print()`, почему бы не использовать этот подход? Этот метод также используется в `Plot3`, и он устраняет необходимость использования оператора `instanceof`, так как внутри метода `Print()` можно предположить возможность преобразования объекта в тип `PrintGraphics`. Это тоже неплохо. В этом случае улучшение достигается путём вынесения общего кода отрисовки в отдельный метод `doGraphics()`.(2) Выполнение кадра программы
Что если мы захотим распечатать что-либо внутри апплета? Для этого нам необходимо получить объект типа `PrintJob` через метод `getPrintJob()` компонента инструмента, а также установить уникальный объект кадра вместо апплета. Таким образом, становится возможной печать из приложения, а не из апплета. Однако, это позволяет создавать кадры внутри апплета (вместо генерации апплетов и помещения кадров, как это было раньше в примерах апплетов или приложений). Это полезная техника, так как она позволяет использовать некоторые приложения внутри апплета (при условии, что они не нарушают безопасность апплета). Тем не менее, когда окно приложения появляется внутри апплета, браузер вставляет предупреждение поверх него, одно из которых говорит "Warning: Applet Window" (Предупреждение: Окно апплета).
Эта техника прямолинейна для помещения кадра внутрь апплета. Единственным моментом является необходимость добавления кода для кадра (вместо вызова `System.exit()`), когда пользователь закрывает его:
```java
//: PrintDemoApplet.java
// Создание кадра внутри апплета
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class PrintDemoApplet extends Applet {
public void init() {
Button b = new Button("Запустить PrintDemo");
b.addActionListener(new PDL());
add(b);
}
}
``` class PDL implements ActionListener {
public void actionPerformed(ActionEvent e) {
final PrintDemo pd = new PrintDemo();
pd.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
pd.dispose();
}
});
pd.setSize(500, 500);
pd.show();
}
}
} ///:~
```Сопутствующие возможностям печати в Java 1.1 возникли некоторые недоразумения. Некоторые заявления казались указывать на возможность печати прямо из апплета. Однако система безопасности Java включает функцию, которая может остановить апплет, который пытается запустить процесс печати; этот апплет требует инициализации через веб-браузер или браузер апплетов. В момент написания этой книги это выглядело как нерешённый спор. Когда я запускал этот апплет в веб-браузере, окно `PrintDemo` (пример печати) отображалось, но он не мог распечататься из браузера.
Когда я запускаю этот апплет в веб-браузере, окно `PrintDemo` (пример печати) отображается, но он не может распечатываться из браузера.## 13.17.3 Буфер обмена```Java 1.1 предоставляет ограниченную поддержку операций с системным буфером обмена (в пакете `Java.awt.datatransfer`). Мы можем копировать строки как объекты текста в буфер обмена и также можем вставлять текст из буфера обмена в текстовое поле. Конечно, буфер обмена предназначен для хранения различных типов данных; данные в буфере обмена могут быть перемещены между программами через действия вырезания и вставки. Хотя в настоящее время буфер обмена поддерживает только строковые данные, API буфера обмена Java обеспечивает хорошую масштабируемость благодаря концепции "формата". Когда данные выходят из буфера обмена, они имеют связанное множество форматов, которое может быть модифицировано (например, графика может быть представлена как набор строк или изображение). Мы заметим, что если специальные данные буфера обмена поддерживают такие форматы, это будет очень интересно.## Класс `CutAndPaste` наследует от `Frame`
### Описание
Класс `CutAndPaste` представляет собой простое графическое приложение с возможностью вырезать, копировать и вставить текст.
#### Элементы класса
- **mb**: Объект типа `MenuBar`.
- **edit**: Объект типа `Menu`, представляющий меню "Редактирование".
- **cut**, **copy**, **paste**: Объекты типа `MenuItem`, представляющие пункты меню "Вырезать", "Скопировать" и "Вставить".
- **text**: Объект типа `TextArea`, используемый для ввода и просмотра текста.
- **clipbd**: Объект типа `Clipboard`, используемый для хранения данных буфера обмена.
### Конструктор `CutAndPaste`
```java
public class CutAndPaste extends Frame {
private MenuBar mb;
private Menu edit;
private MenuItem cut, copy, paste;
private TextArea text;
private Clipboard clipbd;
public CutAndPaste() {
mb = new MenuBar();
edit = new Menu("Редактирование");
cut = new MenuItem("Вырезать");
copy = new MenuItem("Скопировать");
paste = new MenuItem("Вставить");
text = new TextArea(10, 40);
clipbd = getToolkit().getSystemClipboard();
// Добавляем пункты меню
edit.add(cut);
edit.add(copy);
edit.add(paste);
// Устанавливаем меню
setMenuBar(mb);
mb.add(edit);
// Устанавливаем область текста
add(text, BorderLayout.CENTER);
// Устанавливаем обработчики событий
cut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String str = text.getSelectedText();
if (str != null) {
Transferable transferable = new StringSelection(str);
try {
clipbd.setContents(transferable, null);
} catch (Exception ex) {}
}
}
});
copy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String str = text.getSelectedText();
if (str != null) {
Transferable transferable = new StringSelection(str);
try {
clipbd.setContents(transferable, null);
} catch (Exception ex) {}
}
}
});
paste.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
Transferable t = clipbd.getContents(this);
if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
String str = (String)t.getTransferData(DataFlavor.stringFlavor);
text.insert(str, text.getCaretPosition());
}
} catch (Exception ex) {}
}
});
}
public static void main(String[] args) {
CutAndPaste cap = new CutAndPaste();
cap.setSize(400, 300);
cap.setVisible(true);
}
}
```Инициализирует компоненты и добавляет их в окно:
- Устанавливает действие для каждого пункта меню.
- Добавляет пункты меню в меню "Редактирование".
- Добавляет меню "Редактирование" в менюбар.
- Устанавливает менюбар как главное меню окна.
- Добавляет область текста в центральное расположение окна.
### Вложенные классы
#### Класс `CopyL`
Представляет реализацию действия для пункта меню "Копировать":
- Получает выбранный текст из области текста.
- Создает объект `StringSelection` для выбранного текста.
- Устанавливает содержимое буфера обмена данным объектом.
#### Класс `CutL`
Представляет реализацию действия для пункта меню "Вырезать":
- Получает выбранный текст из области текста.
- Создает объект `StringSelection` для выбранного текста.
- Устанавливает содержимое буфера обмена данным объектом.
- Удаляет выбранный текст из области текста.
#### Класс `PasteL`
Представляет реализацию действия для пункта меню "Вставить":
- Получает данные из буфера обмена.
- Преобразует данные в строковый формат.
- Вставляет полученную строку в выбранное место в области текста.
### Метод `main`
Основной метод программы:
- Создает экземпляр класса `CutAndPaste`.
- Добавляет слушатель событий закрытия окна.
- Устанавливает размер окна.
- Отображает окно.
```java
public class CutAndPaste extends Frame {
``````Создание и добавление меню и `TextArea` теперь кажется довольно однообразной задачей. Это сильно отличается от создания поля буферного обмена `clipboard`, которое осуществляется с помощью компонентов инструментов.```Все действия выполняются в рецепторах. Рецепторы `CopyL` и `Cupl` за исключением последней строки `CutL` удаляют скопированную строку. Две специальные строки создаются объектами `StringSelection`, который создает строку из текстовой строки и вызывает метод `setContents()`. Более точно, это означает запись строки в буфер обмена.
В рецепторе `PasteL` данные извлекаются из буфера обмена с использованием метода `getContents()`. Любое возвращаемое значение является анонимным и перемещаемым, и мы не знаем точно, что находится внутри него. Один способ узнать это — вызвать метод `getTransferDataFlavors()`, который возвращает массив объектов типа `DataFlavor`, указывающих на поддерживаемые данным объектом характеристики. Мы также можем проверить его через интересующую нас характеристику с помощью метода `isDataFlavorSupported()`. Однако здесь используется более решительный подход: вызывается метод `getTransferData()`, предполагая, что содержимое поддерживает строковый тип данных, и это не представляет собой сложность, управляемую исключениями.
В будущем мы надеемся видеть поддержку большего количества характеристик данных.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )