Лучший способ переназначить ввод определенных символов/клавиш на JTextComponent?

Я работаю над JTextPane, который работает (почти) точно так же, как поле ввода тегов здесь, в stackoverflow. Для этого я преобразовываю текст в компоненты, как только пользователь нажимает ввод, табуляцию или пробел. Естественно, я не хочу, чтобы какой-либо из этих символов вводился в текстовую панель. Я нашел это решение, SSCCE:

import java.awt.Color;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.StyledDocument;

@SuppressWarnings("serial")
public class TagTextPane extends JTextPane {

    public TagTextPane() {
        this.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "finalizeTag");
        this.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "finalizeTag");
        this.getInputMap().put(KeyStroke.getKeyStroke("TAB"), "focusNext");
        this.getActionMap().put("focusNext", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                finalizeTag();
                transferFocus();
            }
        });
        this.getActionMap().put("finalizeTag", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                finalizeTag();
            }
        });
    }

    private void finalizeTag() {
        StyledDocument doc = (StyledDocument) getDocument();
        Element element = doc.getCharacterElement(getCaretPosition() - 1);
        int start = element.getStartOffset();
        int len = element.getEndOffset() - start;
        String tag = "";
        try {
            tag = this.getDocument().getText(start, len);
        } catch (BadLocationException e) {
        }
        this.setSelectionStart(start);
        this.setSelectionEnd(start + len);
        JLabel label = new JLabel(tag);
        label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        this.insertComponent(label);        
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("TagTextPaneTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        TagTextPane tPane = new TagTextPane();
        frame.setSize(400, 100);
        frame.getContentPane().add(tPane);
        frame.setVisible(true);
    }
}

Нажатие «tab» обычно приводит к вставке символа табуляции, а нажатие «enter» обычно приводит к вводу разрыва строки, а нажатие «space» приводит к вводу пробела. Странно то, что мой код предотвращает ввод разрывов строк и табуляции, но все же позволяет вводить пробелы в обычном режиме при выполнении предполагаемого действия.

  1. Почему этот подход ведет себя по-разному для этих ключей?

  2. Как я могу остановить ввод пробела? (Возможно, позже я захочу распространить это поведение на запятые и тому подобное.)

  3. Как я могу оптимально контролировать поведение определенных символов и нажатий клавиш в JTextComponent? Я видел это, где предлагается использовать KeyListener, но я также видел это, где Роб Камик указывает, почему не следует использовать KeyListener. Так мне лучше использовать DocumentFilter, или это разбить бабочку на колесе?

  4. Это даже лучший способ создать такое поле ввода тега?


person AplusKminus    schedule 03.04.2013    source источник
comment
См. Как использовать привязки клавиш про название спец действия ни слова.   -  person trashgod    schedule 03.04.2013
comment
@trashgod Я читал это, но я интерпретирую это как означающее, что если я не хочу, чтобы ключ вызывал какое-либо другое поведение (именно то, что я хочу), то я не могу использовать действие none. Но дело в том, что я не использую действие none, я уже использую правильно названное действие.   -  person AplusKminus    schedule 03.04.2013
comment
Упс, у меня хоть SPACE была привязка. Может быть, использовать DocumentListener?   -  person trashgod    schedule 03.04.2013
comment
@trashgod Возможно. Для этого мне нужно понять это лучше. Не могли бы вы указать мне учебник или что-то в этом роде, где я могу понять точную цепочку событий, которая разворачивается между нажатием клавиши пользователем и началом действия, предусмотренного программой?   -  person AplusKminus    schedule 03.04.2013
comment
См. руководство и другие примеры   -  person trashgod    schedule 03.04.2013
comment
@trashgod Вы меня неправильно поняли: я хочу понять, как распространяется нажатие клавиши. Через что и где она вызывает поведение. Я знаком с прослушивателями всех видов, но обработка нажатия клавиш — это нечто большее.   -  person AplusKminus    schedule 03.04.2013
comment
Самый простой способ увидеть это взломать отладчик.   -  person trashgod    schedule 03.04.2013
comment
@trashgod Что мне сломать? Где я сломаюсь? Я не могу установить точку останова в коде, принадлежащем JVM, не так ли? Я хочу увидеть, какой компонент или слушатель или что-то еще получит первое уведомление о нажатии клавиши, и что произойдет дальше. Хотелось бы не только увидеть это именно в этом случае, но и получить общее представление об этом процессе.   -  person AplusKminus    schedule 03.04.2013
comment
Полное руководство выходит за рамки SO; да, вы можете сломать классы библиотеки Java; Я использую NetBeans.   -  person trashgod    schedule 03.04.2013


Ответы (1)


this.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "finalizeTag");

Это обработка события keyPressed для символа пробела.

Вы хотите обработать событие keyTyped символа пробела:

this.getInputMap().put(KeyStroke.getKeyStroke(' '), "finalizeTag");
person camickr    schedule 03.04.2013
comment
Ага, если typed, pressed или released не указаны, предполагается pressed. - person trashgod; 03.04.2013
comment
Это отвечает на мои вопросы № 2 и 3, но отвечает ли это на № 1? Дело в том, что я регистрирую события для всех трех клавиш одинаково (при условии, что pressed), но они ведут себя по-разному, так как ни табуляция, ни новая строка фактически не печатаются. Я мог бы понять, если бы новая строка работала по-разному, потому что это не один и тот же символ в разных системах, но табуляция должна быть, верно? - person AplusKminus; 04.04.2013
comment
@SheridanVespo, все клавиши ведут себя одинаково, то есть вызывается ваше действие finalizeTag. Когда вы нажимаете пробел, это также генерирует событие keyTyped, которое фактически добавляет в документ после выполнения действия finalizeTag. Клавиши Tab и Enter не генерируют событие keyTyped, поэтому в документ ничего не добавляется. - person camickr; 04.04.2013