Сложности при работе с интерфейсом Document возникают только тогда, когда вы пытаетесь реализовать свой собственный текстовый редактор. Тем не менее, существует наиболее распространенный случай использования этого интерфейса: отслеживание изменений.
Иногда бывает необходимо обновлять часть пользовательского интерфейса всякий раз, когда пользователь редактирует текст, не ожидая, пока пользователь щелкнет на кнопке. Ниже представлен простой пример. Мы отображаем три текстовых поля для красного, голубого и зеленого компонентов цвета. Всякий раз, когда изменяется содержимое текстовых полей, необходимо обновлять цвета. На рис. 1 показано работающее приложение, код которого будет представлен в конце статьи.
Рис.1. Отслеживание изменений в текстовом поле.
Прежде всего, обратите внимание, что отслеживание нажатий клавиш на клавиатуре — это не совсем подходящая идея. Дело в том, что некоторые нажатия клавиш(например, клавиш управления курсором) не приводят к изменению текста.
Текст может быть обновлен в результате действий, производимых с помощью мыши(например, использование средней кнопки мыши для вставки данных в системе X11). Вместо этого, вам нужно «попросить» документ(а не компонент текста) уведомлять вас всякий раз, когда производится изменение данных. Для этого потребуется установить слушатель документа(document listener):
1 |
textField.getDocument().addDocumentListener(listener); |
После того как текст будет изменен, вызывается один из следующих методов:
1 2 3 |
void insertUpdate(DocumentEvent event) void removeUpdate(DocumentEvent event) void changeUpdate(DocumentEvent event) |
Первые два метода вызываются при вставке или удаления символов. Третий метод для текстовых полей не вызывается вообще. Для более сложных типов документов он может быть вызван при некотором другом изменении, таком как изменение в форматировании.
К сожалению, нет возможности сообщить об изменении текста посредством одиночного обратного вызова — как правило, вам не следует беспокоиться о том, как он изменяется. Более того, на этот случай даже нет специального класса. Таким образом, ваш слушатель документа должен реализовывать все три метода. Ниже показано, что предпринимается в программе:
1 2 3 4 5 6 |
DocumentListener listener = new DocumentListener() { public void insertUpdate(DocumentEvent event) { setColor(); } public void removeUpdate(DocumentEvent event) { setColor(); } public void changedUpdate(DocumentEvent event) { setColor(); } } |
Метод setColor() использует метод getText для получения строк, вводимых пользователем в данный момент времени, из текстовых полей и установки цвета.
Наша программа имеет одно ограничение. Пользователь может ввести в текстовом поле текст с ошибками(например, «мешина» вместо «машина») или вообще оставить его пустым. Пока что мы перехватываем исключение NumberFormatException, генерируемое методом parseInt, и просто не обновляем цвет, если запись в текстовом поле не является числом.
Кстати, вместо того, чтобы прослушивать события документа, можно добавить в поле слушатель события действия. Слушателю действия посылается уведомление всякий раз, когда пользователь нажимает клавишу <Enter>. Мы не рекомендуем применять такой подход, так как пользователи не всегда помнят о том, что нужно нажимать клавишу <Enter> после завершения ввода данных. Если вы используете слушатель действия, вам необходимо также установить слушатель фокуса, чтобы вы могли не пропустить момент, когда пользователь покинет текстовое поле.
Код программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
import java.awt.Color; import java.awt.EventQueue; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; public class ChangeTrackingTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { ColorFrame frame = new ColorFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); frame.setSize(WIDTH, HEIGHT); } }); } protected static final int WIDTH = 300; protected static final int HEIGHT = 300; } /* Фрейм с тремя текстовыми полями * для установления фонового цвета. */ class ColorFrame extends JFrame { public ColorFrame() { setTitle("ChangeTrackingTest"); DocumentListener listener = new DocumentListener() { public void removeUpdate(DocumentEvent event) { setColor(); } public void insertUpdate(DocumentEvent event) { setColor(); } public void changedUpdate(DocumentEvent event) { } }; panel = new JPanel(); panel.add(new JLabel("Red:")); redField = new JTextField("255", 3); panel.add(redField); redField.getDocument().addDocumentListener(listener); panel.add(new JLabel("Green:")); greenField = new JTextField("255", 3); panel.add(greenField); greenField.getDocument().addDocumentListener(listener); panel.add(new JLabel("Blue:")); blueField = new JTextField("255", 3); panel.add(blueField); blueField.getDocument().addDocumentListener(listener); add(panel); pack(); } /* * Устанавливаем фоновый цвет для зачений, * хранящихся в текстовом поле. */ public void setColor() { try { int red = Integer.parseInt(redField.getText().trim()); int green = Integer.parseInt(greenField.getText().trim()); int blue = Integer.parseInt(blueField.getText().trim()); panel.setBackground(new Color(red, green, blue)); } catch(NumberFormatException e) { // Не устанавливает цвет, если введенные данные // невозможно проанализировать. } } private JPanel panel; private JTextField redField; private JTextField greenField; private JTextField blueField; } |