Материал: Портянкин И. Swing

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Текстовые компоненты

495

набором. Для хранения набора атрибутов мы применяем класс SimpleAttributeSet7, в котором указываем, что текст должен иметь красный цвет. Передав данный набор в метод и отказавшись от полной замены имеющегося стиля (третий параметр равен false), мы окрасим часть заголовка в красный цвет, не меняя остальных его атрибутов, подобных размеру и шрифту.

Напоследок мы задействуем кое-что из экзотических возможностей редактора JTextPane: добавляем в конец документа самый настоящий компонент — флажок JCheckBox. Предварительно флажок делается прозрачным (свойство opaque устанавливается в false), в противном случае он будет закрашивать свою область цветом фона и выпадать из общей картины документа. Также приходится переместить курсор в конец документа методом setCaretPosition(), иначе флажок появится вместо конца документа в его начале. Ну а сама вставка компонента на текущую позицию (указываемую курсором) выполняется методом insertComponent().

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

Форматированный вывод — компонент

JFormattedTextField

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

7 Набор атрибутов описывается интерфейсом AttributeSet, класс SimpleAttributeSet — самая простая реализация этого интерфейса, позволяющая быстро настроить набор. Кстати, стили по сути также являются наборами атрибутов: класс стилей Style реализует интерфейс AttributeSet, отличие лишь в том, что стиль обладает собственным уникальным именем.

496

ГЛАВА 16

Отличие между полем для форматированного вывода данных и обычным текстовым полемзаключаетсявналичиивполеJFormattedTextFieldособогоформатирующегообъекта, который должен быть унаследован от абстрактного внутреннего класса AbstractFormatter. Когда пользователь проводит в текстовом поле какие-либо действия — набирает новый символ, удаляет фрагмент текста, отменяет операцию — вызывается форматирующий объект. В его задачу входит анализ значения, которое оказывается в текстовом поле, и принятие решения о соответствии этого значения некоторому формату. Основными составляющими форматирующего объекта являются фильтр документа (DocumentFilter), который принимает решение, разрешать или нет очередное изменение в документе,

атакже навигационный фильтр (NavigationFilter). Навигационный фильтр получает исчерпывающую информацию о перемещениях курсора в текстовом поле и способен запрещать курсору появляться в некоторых областях документа (таких как разделители номеров, дат, и других данных, которые не должны редактироваться). Форматирующий объект также ответственен за действие, которое предпринимается в случае ввода пользователем неверного значения (по умолчанию раздается звуковой сигнал).

Написав свой форматирующий объект (унаследовав его от абстрактного класса AbstractFormatter), вы сможете от начала и до конца следить за процессом редактирования в текстовом поле JFormattedTextField, организуя ограниченный ввод самых сложных данных. Но главное преимущество компонента JFormattedTextField заключается не в этом,

ав наличии нескольких уже готовых форматирующих объектов, способных помочь в организации эффективного ввода самых разнообразных данных. В табл. 16.3 представлен краткий перечень стандартных объектов (все описываемые форматирующие объекты находятся в пакете javax.swing.text).

Таблица 16.3. Стандартные форматирующие объекты для JFormattedTextField

Форматирующий Описание объект

MaskFormatter Организует ввод данных на основе простой маски: набора специальных символов, задающих символы, которые допустимы на определенных позициях документа. Подробное описание масок вы найдете в интерактивной

документации данного класса, а простой пример мы вскоре увидим. Вместо

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

DateFormatter Позволяет редактировать даты в любом удобном вам (или пользователю

ваших приложений) формате. Для форматирования дат используется класс

DateFormat из пакета java.text. Довольно удобен, так как позволяет быстро локализовать ввод дат для любого поддерживаемого JDK языка (а в со-

временных выпусках JDK поддерживаются практически все языки мира).

Формат для отображения даты вы сможете задать в конструкторе формати-

рующего объекта или поменять прямо во время работы программы. Впро-

чем, современные интерфейсы предполагают применение для выбора дат всплывающих диалогов, реализацию такого диалога можно найти в библио-

теке дополнений SwingX

NumberFormatter Рассматривает вводимые пользователем значения как числа, записанные

в определенном формате. Для форматирования чисел использует класс NumberFormat из пакета java.text. Также как и предыдущий класс, позволяет

легко локализовать приложение и настроить формат прямо во время рабо-

ты программы

Все стандартные форматирующие объекты унаследованы от класса DefaultFormatter, реализующего основные функции базового класса AbstractFormatter. Как правило, ваша работа заключается именно в настройке одного из доступных форматирующих объек-

Текстовые компоненты

497

тов и подключении его к полю JFormattedTextField. Создание собственных форматирующих объектов — довольно кропотливая задача. Зачастую проще создать собственную модель документа Document (мы ее вскоре обсудим).

Кроме того, что вы можете указать форматирующий объект, который следует использовать в текстовом поле JFormattedTextField, имеется еще одна возможность настраивать форматирование. Фабрика AbstractFormatterFactory, описанная, как и форматирующий объект AbstractFormatter, в виде абстрактного внутреннего класса, служит для создания форматирующих объектов для текстовых полей JFormattedTextField. Если вы создадите поле JFormattedTextField, передав в конструктор фабрику форматирующих объектов, то поле при редактировании будет каждый раз запрашивать ее для получения форматирующего объекта. Это дает дополнительную гибкость: вы можете возвращать различные форматирующие объекты, в зависимости от того, что в данный момент находится в текстовом поле. В пакете javax.swing.text имеется стандартная реализация фабрики DefaultFormatterFactory. С ее помощью вы сможете задать различные форматирующие объекты для трех ситуаций: когда поле не пусто и обладает фокусом ввода, когда поле не пусто и не обладает фокусом ввода, и, наконец, когда поле пусто. Хотя фабрика форматирующих объектов и предоставляет дополнительную гибкость, чаще всего достаточно одного форматирующего объекта «на все случаи жизни».

А теперь рассмотрим пример, в котором используем все три стандартных форматирующих объекта, поставляемых вместе со Swing, а заодно применим некоторые наиболее полезные свойства текстового поля JFormattedTextField:

//FormattedFields.java

//Применение полей JFormattedTextField import javax.swing.*;

import javax.swing.text.*; import java.text.*; import java.util.Date; import java.awt.*;

public class FormattedFields extends JFrame { // поля для форматированного ввода данных private JFormattedTextField

phoneField, dateField, numberField; public FormattedFields() {

super("FormattedFields"); setDefaultCloseOperation(EXIT_ON_CLOSE);

//ограниченный ввод на основе маски

//телефонный номер

try {

MaskFormatter phone =

new MaskFormatter("+#-###-###-##-##"); phone.setPlaceholderCharacter('0'); phoneField = new JFormattedTextField(phone); phoneField.setColumns(15);

}catch (Exception ex) { ex.printStackTrace();

498

ГЛАВА 16

}

//редактирование даты

//формат даты

DateFormat date =

new SimpleDateFormat("dd MMMM yyyy, EEEE");

//настройка форматирующего объекта

DateFormatter formatter = new DateFormatter(date); formatter.setAllowsInvalid(false); formatter.setOverwriteMode(true);

// настройка текстового поля

dateField = new JFormattedTextField(formatter); dateField.setColumns(15); dateField.setValue(new Date());

//редактирование чисел

//формат числа с экспонентой

NumberFormat number = new DecimalFormat("##0.##E0"); numberField = new JFormattedTextField(

new NumberFormatter(number));

//настройка поля numberField.setColumns(10); numberField.setValue(1500);

//добавляем поля в панель содержимого setLayout(new FlowLayout());

add(new JLabel("Телефон:")); add(phoneField);

add(new JLabel("Дата:")); add(dateField);

add(new JLabel("Число:")); add(numberField);

//выводим окно на экран

setSize(400, 300); setVisible(true);

}

public static void main(String[] args) { SwingUtilities.invokeLater(

new Runnable() {

public void run() { new FormattedFields(); } });

}

}

Мы создаем небольшое окно с рамкой, в нем разместятся три текстовых поля для вывода форматированных данных JFormattedTextField. Каждое из полей продемонстрирует работу одного из стандартных форматирующих объектов.

Текстовые компоненты

499

Первое поле применяет в качестве форматирующего объекта MaskFormatter, данный объект позволяет организовать ввод данных на основе особой маски, составленной из специальных символов. Подробное описание масок, как мы уже отмечали, находится в интерактивной документации. В примере маска составляется для ввода расширенного телефонного номера: с кодом страны и города. Обратите внимание, как в маске задаются десятичные числа — они обозначаются символом #. Остальные символы, использованные при создании объекта MaskFormatter, обычные, а это означает, что редактировать их пользователю будет нельзя, так как они служат разделителями данных. Объект MaskFormatter приходится создавать в блоке try/catch — если при анализе маски будет обнаружена ошибка синтаксиса, возникнет исключение. Помимо собственно создания форматирующего объекта мы настраиваем некоторые дополнительные свойства. Свойство placeholderCharacter отвечает за то, какой символ будет заменять незаполненные пользователем части маски. По умолчанию используется пробел, но для нашей маски он не очень хорошо подходит, потому что размер текстового поля с пробелами намного меньше того же текстового поля, но уже заполненного цифрами. К тому же по маске, заполненной пробелами, трудно определить, что в ней требуется записывать цифры, а не что-либо иное. Поэтому мы заменяем пробел символом нуля. Более того, мы увеличиваем размер текстового поля до 15 символов. Поле с избыточным количеством символом смотрится лучше, а за пределы маски пользователю все равно выходить запрещено (за этим следит форматирующий объект).

Второе поле служит для набора дат, заданных в определенном формате. Формат даты задается объектом DateFormat. В классе DateFormat имеются несколько статических методов, позволяющих создавать стандартные форматы дат, принятых в различных странах, однако мы настраиваем формат даты по своему вкусу, создавая объект SimpleDateFormat. Он позволяет указать формат даты с помощью несложной текстовой маски; полное описание правил создания таких масок вы найдете в документации класса SimpleDateFormat. Наш формат для даты содержит число месяца (за него отвечают символы маски «dd»), полное название месяца («MMMM»), четырехзначные число года и название дня недели («yyyy» и «EEEE», соответственно). Созданный формат даты мы передаем в форматирующий объект DateFormatter, после чего настраиваем для него несколько дополнительных свойств. В отличие от объекта MaskFormatter форматирующий объект для дат по умолчанию разрешает ввод неверных (не соответствующих формату даты) значений. Мы запрещаем ввод таких значений, изменяя свойство allowsInvalid. Для удобства изменения даты мы также включаем режим перезаписи значений (по умолчанию работает режим вставки), применяя свойство overwriteMode. После настройки форматирующий объект передается в текстовое поле, размер которого мы также увеличиваем до 15 символов. Обратите внимание, как задается новое значение, которое будет отображаться текстовым полем: в метод setValue() мы передаем объект Date, инкапсулирующий дату. За преобразование этого объекта в текст отвечает как раз форматирующий объект DateFormatter.

Наконец, третье поле JFormattedTextField настраивается для ввода чисел в определенном формате. Формат чисел определяется объектом NumberFormat. Как и в случае с объектом для формата дат DateFormat, в нем имеется несколько статических методов для получения стандартных форматов чисел различных стран и языков, однако мы применим для создания формата числа объект DecimalFormat. Он позволяет настраивать формат числа с помощью несложной текстовой маски. В примере мы настроили маску для целых чисел в экспоненциальном формате: две десятичных цифры в самом числе, две возможных десятичных цифры в мантиссе. Число и мантисса разделяются точкой и заканчиваются нулями. Подробное описание маски для определения формата числа вы найдете в документации класса DecimalFormat. Созданный формат числа присоединяется к форматирующему объекту NumberFormatter, а тот, в свою очередь, передается текстовому полю. Для текстового поля мы немного увеличиваем размеры (увеличиваем количество столбцов).

Источник: https://studfile.net/preview/15935977/