JTextField, JTextArea, JTextPane

Пакет javax.swing.text библиотеки Swing содержит компоненты для работы с текстом. Мощные текстовые компоненты позволяют реализовать средства ввода и редактирования текста любой сложности, начиная от однострочного текстового поля и заканчивая многофункциональным текстовым редактором с разнообразными возможностями.

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

Текстовые компоненты имеют архитектуру MVC. Модель текстовых компонентов представлена довольно простым интерфейсом Document, который позволяет получать информацию об изменениях в документе и хранящийся в нем текст, а также при необходимости изменять полученный текст. Вид реализован в UI-представителях текстовых компонентов; но составляется он на основе специальных объектов Element и View, больше отвечающих именно текстовым компонентам. Благодаря этим объектам можно гибко настраивать и расширять внешний вид и структуру текстовых компонентов без вмешательства в сложный процесс их конечной прорисовки. Контроллер соединен с видом для обработки событий, как правило не связанных с клавиатурой, и частично реализован в виде карты клавиатуры (keymap). Карта клавиатуры позволяет гибко, без смены UI-представителя текстового компонента, менять реакцию текстового компонента на нажатия клавиш.

Ссылки на странице

Текстовые поля JTextField, JPasswordField

Текстовое поле JTextField является самым простым компонентом и наиболее часто встречающимся в пользовательских интерфейсах. Как правило, поле является однострочным и служит для ввода текста. В библиотеке Swing имеется два текстовых поля. Первое, представленное классом JTextField, позволяет вводить однострочный текст. Второе поле, реализованное классом JPasswordField и унаследованное от поля JTextField, дает возможность организовать ввод «закрытой» информации (чаще всего паролей), которая не должна напрямую отображаться на экране.

Оба текстовых поля JTextField, JPasswordField просты. Работа с ними чаще всего сводится к заданию количества отображаемых в поле символов и начального текста, если таковой требуется. После чего остается только поместить поле в контейнер и в нужный момент получить из него набранный пользователем текст.

Пример использования текстовых полей JTextField

// Использование текстовых полей JTextField
import javax.swing.*;

import java.awt.Font;
import java.awt.event.*;
import java.awt.FlowLayout;

public class TextFieldTest extends JFrame
{
    // Текстовые поля
    JTextField smallField, bigField;

    public TextFieldTest()
    {
        super("Текстовые поля");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        // Создание текстовых полей
        smallField = new JTextField(15);
        smallField.setToolTipText("Короткое поле");
        bigField = new JTextField("Текст поля", 25);
        bigField.setToolTipText("Длиное поле");
        // Настройка шрифта
        bigField.setFont(new Font("Dialog", Font.PLAIN, 14));
        bigField.setHorizontalAlignment(JTextField.RIGHT);
        // Слушатель окончания ввода
        smallField.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // Отображение введенного текста
                JOptionPane.showMessageDialog(TextFieldTest.this, 
                               "Ваше слово: " + smallField.getText());
            }
        });
        // Поле с паролем
        JPasswordField password = new JPasswordField(12);
        password.setEchoChar('*');
        // Создание панели с текстовыми полями
        JPanel contents = new JPanel(new FlowLayout(FlowLayout.LEFT));
        contents.add(smallField);
        contents.add(bigField  );
        contents.add(password  );
        setContentPane(contents);
        // Определяем размер окна и выводим его на экран
        setSize(400, 130);
        setVisible(true);
    }
    public static void main(String[] args) {
        new TextFieldTest();
    }
}

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

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

В примере также используется поле для ввода «закрытых» данных JPasswordField. Это поле унаследовано от обычного поля JTextField. Из собственных методов поля JPasswordField можно упомянуть лишь метод setEchoChar(), служащий для смены символа-заменителя. По умолчанию в качестве такого символа используется звездочка '*'. Разработчики класса JPasswordField не рекомендуют применять для получения введенного в поле значения (пароля) обычный метод getText(). Дело в том, что создаваемая данным методом строка String может кэшироваться (объекты String в Java максимально оптимизируются компилятором и виртуальной машиной), и злоумышленник сможет похитить ваш пароль сканированием памяти приложения. Для получения данных предоставляется более безопасный метод getPassword(), возвращающий массив символов char, значения которого после проверки имеет смысл обнулить и при желании вызвать сборщик мусора. Поле JPasswordField особым образом копирует данные в буфер обмена — оно переопределяет методы cut() и сору(), определенные в базовом классе JTextComponent, запрещая копировать набранный текст в буфер обмена.

Метод setToolTipText() позволяет для каждого поля установить всплывающую "подсказку". Интерфейс окна представлен на следующем скриншоте.

После ввода значения в верхнее поле была нажата клавиша <Enter>, в результате чего было открыто диалоговое окно с отображением введенного текста.

Свойства текстовых полей

Свойства и методы get/setОписание
text Чтение введенного в поле текста или его замена. Для поля с конфиденциальной информацией лучше использовать метод getPassword()
columns Определение количества символов в поле; можно получить размер поля или изменить его
font Определение используемого в текстовом поле шрифта.
horizontalAlignment Управление выравниванием текста в поле. По умолчанию текст выравнивается по левой границе поля.
echoChar (только для JPasswordField) Определение символа-заменителя для ввода закрытой информации. По умолчанию используется символ звездочки (*)

Многострочное поле JTextArea

Многострочное текстовое поле JTextArea предназначено для ввода простого неразмеченного различными атрибутами текста. В отличие от обычных полей, позволяющих вводить только одну строку текста, многострочные поля дают пользователю возможность вводить произвольное количество строк текста.

Для многострочных полей необходимо задавать не только ширину (максимальное количество символов), но и высоту(максимальное количество строк). JTextArea следует размещать в панелях прокрутки JScrollPane. Рассмотрим пример использования JTextArea.

// Пример использования многострочных полей JTextArea

import javax.swing.*;
import java.awt.Font;

public class TextAreaTest extends JFrame 
{
    public TextAreaTest() 
    {
        super("Пример JTextArea");
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        // Cоздание многострочных полей
        JTextArea area1 = new JTextArea("Многострочное поле", 8, 10);
        // Шрифт и табуляция
        area1.setFont(new Font("Dialog", Font.PLAIN, 14));
        area1.setTabSize(10);

        JTextArea area2 = new JTextArea(15, 10);
        area2.setText("Второе многострочное поле");
        // Параметры переноса слов
        area2.setLineWrap(true);
        area2.setWrapStyleWord(true);

        // Добавим поля в окно
        JPanel contents = new JPanel();
        contents.add(new JScrollPane(area1));
        contents.add(new JScrollPane(area2));
        setContentPane(contents);
        
        // Выводим окно на экран
        setSize(400, 300);
        setVisible(true);
    }
    public static void main(String[] args) {
        new TextAreaTest();
    }
}

В примере создается два многострочных текстовых полей JTextArea, для которых были изменены некоторые наиболее часто используемых свойства. Первое текстовое поле создается с помощью конструктора, заполняещего поле текстом и определяющего количество строк и символов. Следует обратить внимание, что количество строк идет в списке параметров перед количеством символов. Задаваемые в конструкторе количество строк и символов поля определяют его размер в контейнере, но не накладывают ограничений на объем вводимого текста. Для первого поля был изменен шрифт и определено нестандартное значение для табуляции вызывом метода setTabSize(). Данный метод позволяет указать, какое количество символов пробела будет замещать символ табуляции, вставляемый нажатием клавиши <Tab>.

Второе текстовое поле создается с помощью конструктора, которому в качестве параметров передается количество строк и символов. После этого с использованием метода setText() определяется содержимое поля и меняются свойства, управляющие процессом переноса текста на новые строки. По умолчанию текст в поле JTextArea не переносится на новую строку. Изменить данное поведение позволяет метод setLineWrap(). Метод setWrapStyleWord() изменяет стиль переноса длинных слов на новые строки. Если вы передадите в этот метод значение true, то слова, не умещающиеся в строке, будут целиком переноситься на новую строку. По умолчанию значение этого свойства равно false. Это означает, что текст переносится, как только ему перестает хватать места в строке, независимо от того, в каком месте слова приходится делать перенос.

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

Обратите внимание на разницу реализации переноса текста в двух компонентах JTextArea. Для переноса текста в левом поле приходится вручную нажимать клавишу Enter, а в правом поле перенос выполняется автоматически.

Свойства многострочных текстовых полей JTextArea

Свойства (и методы get/set)Описание
rows, columns Определение размеров многострочного текстового поля, в строках и символах соответственно. Изменить размеры поля можно в run-time, при этом поле JTextArea автоматически проведет проверку корректности и перерисовку контейнера.
lineWrap, wrapStyleWord Управление включением переноса текста по строкам и типом этого переноса. Когда перенос строк отключен, второе свойство не действует. Если перенос строк включен и второе свойство равно false, то перенос происходит в том месте, где заканчивается строка, независимо от того, в какой точке слова это случается. В противном случае перенос происходит по словам, которые не разбиваются на части, а переходят на новую строку целиком.
font Определение шрифта для многострочного текстового поля. Шрифт по умолчанию задается текущим менеджером внешнего вида.
lineCount, lineOfOffset,
lineStartOffset, lineEndOffset
(только методы get)
Данные методы позволяют получить информацию о распределении текста многострочного поля по строкам. Первый метод get дает общее количество строк текста в поле. Второй метод предоставляет возможность узнать, на какой строке поля находится символ с данным смещением от начала текста. Последние два метода действуют обратным образом: для заданного номера строки они позволяют узнать смещение символа, находящегося в начале строки и в конце строки.

Многострочное поле JTextArea обладает еще парой полезных методов. Метод append() позволяет присоединить к уже имеющемуся в поле тексту новую часть без удаления прежнего содержимого. Метод insert() дает возможность вставить в произвольную область находящегося в поле текста новую строку.

Текстовый редактор JEditorPane

Редактор JEditorPane является мощным инструментом для отображения на экране текста любого формата. Он поддерживает два широко распространенных формата: HTML и RTF (Rich Text Format — расширенный текстовый формат). Потенциально редактор JEditorPane может отображать текст любого формата, с любыми элементами и любым оформлением. Такую гибкость редактору обеспечивает фабрика классов EditorKit, в обязанности которой входит создание и настройка всех объектов, необходимых для отображения текста некоторого типа, в том числе модели документа (объекта Document), фабрики для отображения элементов документа ViewFactory, курсора и списка команд, поддерживаемых данным типом текста.

EditorKit также отвечает за правильное открытие и сохранение документа поддерживаемого ею формата. Так что возможности JEditorPane ограничены лишь наличием фабрик для различных текстовых форматов. Поддерживаемые стандартно форматы RTF и HTML описываются фабриками RTFEditorKit и HTMLEditorKit соответственно.

Для документов, включающих различного рода ссылки, редактор JEditorPane предоставляет слушателей HyperlinkListener. Эти слушатели оповещаются при активизации пользователем ссылки в документе. Слушателям передается URL-адрес активизированной ссылки, так что они могут заставить редактор немедленно перейти по этому адресу, вызвав все тот же метод setPage(), или использовать полученную информацию по своему усмотрению. Ссылки поддерживаются стандартной фабрикой HTMLEditorKit, необходимой для отображения HTML-документов, так что вы сможете сразу узнать, когда пользователь активизирует ссылку. Следует отметить, что ссылки активизируются, только когда редактирование текста запрещено (свойство editable равно false).

Рассмотрим пример создания браузера для просмотра HTML-документов с помощью редактора JEditorPane. Переходить с одной страницы на другую можно будет с помощью адресной строки или ссылок в текущем документе.

// Браузер на основе редактора JEditorPane

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;

public class JEditorPaneTest extends JFrame 
{
    private  JEditorPane  editor ;  // наш редактор
    private  JTextField   url    ;  // текстовое поле с адресом
    private  final String unavailable = "Адрес недоступен";

    public JEditorPaneTest()
    {
        super("Пример с JEditorPane");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        // Создаие пользовательского интерфейса
        createGUI();
        // Вывод окна на экран
        setSize(500, 400);
        setVisible(true);
    }
    /**
     * Процедура создания интерфейса
     */
    private void createGUI()
    {
        // Панель с адресной строкой
        JPanel pnlURL = new JPanel();
        pnlURL.setLayout(new FlowLayout(FlowLayout.LEFT));
        pnlURL.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        // Поле URL адреса
        url = new JTextField("http://mail.ru", 35);
        // Слушатель окончания ввода
        url.addActionListener(new URLAction());
        pnlURL.add(new JLabel("Адрес:"));
        pnlURL.add(url);
        try {
            // Создание редактора
            editor = new JEditorPane(url.getText());
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, unavailable);
        }
        editor.setContentType("text/html");
        editor.setEditable(false);
        // Поддержка ссылок
        editor.addHyperlinkListener(new LinkListener());
        // Размещение в форме
        getContentPane().add(pnlURL, BorderLayout.NORTH);
        getContentPane().add(new JScrollPane(editor));
    }
    // Слушатель, получающий уведомления о вводе нового адреса
    class URLAction implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            // Переход по адресу
            String newAddress = url.getText();
            try {
                editor.setPage(newAddress);
            } catch (Exception e) {
                JOptionPane.showMessageDialog(JEditorPaneTest.this, 
                                              unavailable);
            }
        }
    }
    // Слушатель, обеспечивающий поддержку ссылок
    class LinkListener implements HyperlinkListener {
        public void hyperlinkUpdate(HyperlinkEvent he) {
            // Проверка типа события
            if ( he.getEventType() != HyperlinkEvent.EventType.ACTIVATED) 
                return;
            // Переходим по адресу
            try {
                editor.setPage(he.getURL());
            } catch (Exception e) {
                JOptionPane.showMessageDialog(JEditorPaneTest.this, 
                                              unavailable);
            }
        }
    }
    public static void main(String[] args) {
        new JEditorPaneTest();
    }
}

Интерфейс программы включает текстовое поле, в котором пользователь будет набирать адрес для перехода, и редактор JEditorPane для отображения страницы сайта по указанному в поле адресу. В панели JPanel будет располагаться текстовое поле для ввода адреса. В ней используется менеджер последовательного расположения FlowLayout с выравниванием по левому краю. К текстовому полю подключаем слушателя окончания ввода, реализованного во внутреннем классе URLAction. Когда пользователь заканчивает ввод, нажав специальную клавишу <Enter>, вызывается метод слушателя actionPerformed(). В этом методе получаем текст, набранный в поле ввода адреса и методом setPage() загружаем в редактор новую страницу. В случае неудачи будет выведено краткое сообщение.

Интерфейс окна представлен на следующем скриншоте.

Для создания редактора JEditorPane в примере выбирается конструктор, который позволяет сразу же задать страницу для отображения. Далее методом setContentType() указаются типы документов, которые должен отображать редактор («text/html»). Для поддержки переходов по ссылкам редактирование должно быть отключено, т.е. свойство editable равно false. В заключение к редактору подключается слушатель типа HyperlinkListener, который будет получать информацию о событиях, происходящих со ссылками документа. Слушатель реализован во внутреннем классе LinkListener.

В метод hyperlinkUpdate() слушателя LinkListener поступает информация о событиях, происходящих со ссылками. В методе проверяется тип события. Если событием являлась активизация ссылки, то определяется адрес активизированной ссылки методом getURL() и редактор переводится на новую страницу методом setPage() с передачей ему полученного адреса. В случае ошибки на экран выводится краткое сообщение.

Следует отметить, что выбирать страницы для просмотра данным редактором нужно аккуратнее: ни языки сценариев, ни модули расширения, ни возможности HTML 4.0, к сожалению, не поддерживаются.

Текстовый редактор JTextPane

Унаследованный от JEditorPane текстовый редактор JTextPane незаменим при создании в приложении многофункционального текстового редактора. Обладая всеми возможностями своего родителя JEditorPane, класс JTextPane добавляет к нему то, без чего представить себе современные редакторы практически невозможно — разметку текста стилями. Для этого в нем используется специальная модель документа StyledDocument и настроенная на поддержку такой модели фабрика классов StyledEditorKit.

Текстовый редактор JTextPane использует стили Style для управления документом : установка шрифта и его размера, цвета символов, выравнивание текста и т.п. Стиль позволяет четко разделить внешний вид документа и текст. Для редактировании текста используется несколько стилей для выделения заголовков, основного текста, сносок и т.д. Текст, набранный с использованием стилей, изменяется автоматически.

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

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

Рассмотрим пример с использованием возможностей компонента JTextPane.

// Пример использования редактора JTextPane

import java.awt.Color;
import java.awt.Font;

import javax.swing.*;
import javax.swing.text.*;

public class JTextPaneTest extends JFrame
{
    // Текстовый редактор
    private  JTextPane textEditor = null;
    // Стили редактора
    private  Style     heading    = null; // стиль заголовка
    private  Style     normal     = null; // стиль текста

    private  final  String      STYLE_heading = "heading",
                                STYLE_normal  = "normal" ,
                                FONT_style    = "Times New Roman";
    private  final  String[][]  TEXT = {
        {"Компонент JTextPane \r\n"                           , "heading"},
        {"\r\n"                                               , "normal" },
        {"JTextPane незаменим при создании в приложении  \r\n", "normal" },
        {"многофункционального текстового редактора.\r\n"     , "normal" },
        {"\r\n"                                               , "normal" },
        {"Он позволяет вставлять в документ визуальные \r\n"  , "normal" },
        {"компоненты типа JCheckBox и JRadioButton.\r\n "     , "normal" }};
    public JTextPaneTest()
    {
        super("Пример редактора JTextPane");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        // Создание редактора
        textEditor = new JTextPane();
        // Определение стилей редактора
        createStyles(textEditor);
        // Загрузка документа
        loadText(textEditor);
        changeDocumentStyle(textEditor);
        // Размещение редактора в панели содержимого
        getContentPane().add(new JScrollPane(textEditor));
        // Открытие окна
        setSize(380, 240);
        setVisible(true);
    }
    /**
     * Процедура формирования стилей редактора
     * @param editor редактор
     */
    private void createStyles(JTextPane editor)
    {
        // Создание стилей
        normal = editor.addStyle(STYLE_normal, null);
        StyleConstants.setFontFamily(normal, FONT_style); 
        StyleConstants.setFontSize(normal, 16);
        // Наследуем свойстdо FontFamily
        heading = editor.addStyle(STYLE_heading, normal);
        StyleConstants.setFontSize(heading, 24);
        StyleConstants.setBold(heading, true);
    }
    /**
     * Процедура загрузки текста в редактор
     * @param editor редактор
     */
    private void loadText(JTextPane editor)
    {
        // Загружаем в документ содержимое
        for (int i = 0; i < TEXT.length; i++) {
            Style style = (TEXT[i][1].equals(STYLE_heading)) ? 
                                                   heading : normal;
            insertText(editor, TEXT[i][0], style);
        }
        // Размещение компонента в конце текста
        StyledDocument doc = editor.getStyledDocument();
        editor.setCaretPosition(doc.getLength());
        JCheckBox check = new JCheckBox("JCheckBox");
        check.setFont(new Font(FONT_style, Font.ITALIC, 16));
        check.setOpaque(false);
        editor.insertComponent(check);

        JRadioButton radio = new JRadioButton("JRadioButton");
        radio.setFont(new Font(FONT_style, Font.ITALIC, 16));
        radio.setOpaque(false);
        radio.setSelected(true);
        editor.insertComponent(radio);
    }
    /**
     * Процедура изменения стиля документа
     * @param editor редактор
     */
    private void changeDocumentStyle(JTextPane editor)
    {
        // Изменение стиля части текста
        SimpleAttributeSet blue = new SimpleAttributeSet();
        StyleConstants.setForeground(blue, Color.blue);
        StyledDocument doc = editor.getStyledDocument();
        doc.setCharacterAttributes(10, 9, blue, false);
    }
    /**
     * Процедура добавления в редактор строки определенного стиля
     * @param editor редактор
     * @param string строка
     * @param style стиль
     */
    private void insertText(JTextPane editor, String string, 
                                              Style style)
    {
        try {
            Document doc = editor.getDocument();
            doc.insertString(doc.getLength(), string, style);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args)
    {
        new JTextPaneTest();
    }
}

Структурно пример использования JTextPane включает несколько методов, в которых реализуются различные функции текстового редактора JTextPaneTest. В методе createStyles(editor) определяются стили для заголовка документа heading и остального текста normal. Для установки атрибутов стилей используются статические методы класса StyleConstants. Стиль заголовка heading наследует свойства стиля обычного текста normal.

Метод loadText(editor) загружает в редактор текст. Текст вставляется в компонент JTextPane посредством модели документа Document с ипользованием метода insertString(). Методу insertString() необходимо в качестве параметров указать строку и стиль. В примере вставку текста в документ выполняет вспомогательный метод insertText(), в котором строка добавляется в конец документа. Размещаемый в редакторе текст описан в локальном массиве TEXT.

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

Редактор JTextPane позволяет вставлять в документ визуальные компоненты. В примере были использованы флажок JCheckBox и кнопка JRadioButton. Предварительно компоненты были сделаны прозрачными (свойство opaque). Для размещения компонентов курсор был устанавливлен в конец документа методом setCaretPosition(). Вставка компонента в текущую позицию курсора выполняется методом insertComponent().

Представление документа в поле текстового редактора изображено на следующем скриншоте.

Форматирование текстового поля, JFormattedTextField

Текстовый компонент определения формата данных JFormattedTextField унаследован от обычного текстового поля JTextField. Он позволяет представлять данные согласно специальным текстовым форматам и маскам, а также ограничивает ввод пользователя, разрешая тому вводить данные только в соответствии с заданным в поле форматом.

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

В таблице представлен краткий перечень стандартных объектов.

Форматирующий объектОписание
MaskFormatter Организация ввода данных на основе простой маски, представляющая набор специальных символов, определяющих допустимые значения на определенных позициях поля. Подробное описание масок можно найти в интерактивной документации данного класса.
DateFormatter Определение формата даты. Для форматирования дат используется класс DateFormat из пакета java.text.
NumberFormatter Определение вводимых пользователем значений числа, записанные в определенном формате. Для форматирования чисел использует класс NumberFormat из пакета java.text.

Пример форматированного ввода по маске, MaskFormatter

// Пример форматирования поля мобильного телефона
try {
    // Определение маски и содание поля ввода мобильного телефона
    MaskFormatter phoneFormatter = new MaskFormatter("+#-###-###-##-##");
    phoneFormatter.setPlaceholderCharacter('0');
    JFormattedTextField ftfPhone = new JFormattedTextField(phoneFormatter);
    ftfPhone.setColumns(16);
} catch (Exception e) {
    e.printStackTrace();
}

Свойство placeholderCharacter определяет символ, который будет заменять незаполненные пользователем части маски. По умолчанию используется пробел. Но для маски мобильного телефона он не совсем подходит. Поэтому символ пробела заменен символом нуля.

Пример форматированного ввода даты, DateFormatter

// Определение маски и поля ввода даты
DateFormat date = new SimpleDateFormat("dd MMMM yyyy, EEEE");
// Форматирующий объект даты
DateFormatter dateFormatter = new DateFormatter(date);
dateFormatter.setAllowsInvalid(false);
dateFormatter.setOverwriteMode(true);

// Создание форматированного текстового поля даты
JFormattedTextField ftfDate = new JFormattedTextField(dateFormatter);
ftfDate.setColumns(32);
ftfDate.setValue(new Date());

Формат даты определяется объектом DateFormat. В классе DateFormat имеются несколько статических методов, позволяющих создавать стандартные форматы дат, принятых в различных странах (см. Интернационализация, i18n, l10n. В примере формат даты настраивается с использованием объекта SimpleDateFormat, который позволяет указать формат даты с помощью несложной текстовой маски. Полное описание правил создания таких масок можно найти в документации класса SimpleDateFormat. Наш формат даты содержит число месяца «dd», полное название месяца «ММММ», четырехзначное число года «уууу» и название дня недели «ЕЕЕЕ». Созданный формат даты передается в форматирующий объект DateFormatter, после чего у него настраивается несколько дополнительных свойств.

В отличие от объекта MaskFormatter форматирующий объект для дат DateFormatter по умолчанию разрешает ввод неверных, не соответствующих формату даты, значений. Методом setAllowsInvalid можно запретить ввод таких значений. Для удобства изменения даты включен режим перезаписи значений overwriteMode. По умолчанию работает режим вставки. Настроенный форматирующий объект передается в конструктор текстового поля. Новое значение даты в текстовом форматированном поле задается в методе setValue(). Форматирующий объект DateFormatter отвечает за преобразование объекта даты в текст.

Пример форматированного ввода вещественного числа, NumberFormatter

// Определение маски и поля ввода вещественного числа

// Формат числа с экспонентой
// NumberFormat number = new DecimalFormat("##0.##E0");

NumberFormat number = new DecimalFormat("##0.###");
JFormattedTextField numberField = new JFormattedTextField(
                                      new NumberFormatter(number));
numberField.setColumns(10);
numberField.setValue(new Float(123.45));

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

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

Модель документа Document

Текстовые компоненты библиотеки Swing имеют модель, в которой хранятся данные. В качестве данных выступает текст, набранный пользователем или вставленный программно. Модель всех текстовых компонентов описывается интерфейсом Document, который поддерживается текстовым компонентом JTextComponent. Методы интерфейса Document перечислены в таблице.

МетодОписание
getText(позиция, длина) Получение фрагмента текста, заданный начальной позицией и длиной. Позиция должна быть не меньше нуля и не больше длины текста, иначе возникнет исключение.
insertString(позиция, текст, атрибуты) Вставка текста в произвольную позицию документа.
remove(позиция, длина) Удаление из документа фрагмент текста, заданный позицией и длиной.
getLength() Получение длины текста, хранимого в модели документа.

Document хранит текст и позволяет сопоставлять ему наборы атрибутов AttributeSet. К атрибутам текста относится, шрифт и его размер, цвет текста и т.п. Следует отметить, что модель, используемая в текстовых полях, не сохраняет атрибуты; весь текст прорисовывается в едином виде. Модель, применяемая в редакторе JTextPane, сохраняет атрибуты текста и позволяет выводить текст в различном оформлении.

Для контроля за изменениями в текстовых полях Swing имеется событие DocumentEvent и его слушатель DocumentListener, который можно присоединить к модели Document. Событие DocumentEvent возникает каждый раз при изменении текста документа. В интерфейсе слушателя DocumentListener определены три метода; каждый из них вызывается при определенном типе события в документе: удалении removeUpdate(DocumentEvent e), обновлении changedUpdate(DocumentEvent e) или вставке insertUpdate(DocumentEvent e) текста.

Пример использования класса JTextField c модифицированным DocumentListener включен в архив примеров, рассмотренных на странице. В примере AutoCompleteFieldTest.java выполняется подстановка слова в текстовое поле по нескольким начальным символам.

Интерфейс Caret

Курсор текстовых компонентов реализовыван интерфейсом Caret. Курсор позволяет программно изменять текущую позицию в тексте и управлять выделенным текстом. Для работы с выделенным текстом в базовом классе JTextComponent имеется несколько методов, но изначально выделение текста находится в ведении текстового курсора.

Курсор имеет два основных свойства — его позиция dot и начальная позиция выделенного текста mark. Позиция курсора показывает, куда будут вставляться символы.

Курсор, описанный интерфейсом Caret, поддерживает событие ChangeEvent. Присоединив к курсору слушателя ChangeListener, можно получать информацию обо всех перемещениях курсора. Базовый класс JTextComponent поддерживает событие CaretEvent. Оно также запускается при перемещениях курсора. Но если событие ChangeEvent содержит только источник события (сам курсор), то событие CaretEvent позволяет сразу же узнать текущую позицию курсора и начало отсчета.

Пример использования класса курсора Caret включен в архив примеров, рассмотренных на странице. В примере CaretTest.java демонстрируется работа с методами курсора setDot(int position), moveDot(int position), setBlinkRate(int freq), getMark() и getDot().

Скачать примеры

Исходные коды примеров, рассмотренных на странице, можно скачать здесь (10 Кб).

  Рейтинг@Mail.ru