Как предотвратить ввод символа в JTextField после отправки события? - PullRequest
0 голосов
/ 15 декабря 2018

В моем классе MainWindow (JFrame) я использую клавиши «+» и «-» в качестве горячих клавиш для изменения значения определенного JTextField, называемого градусным полем вверх или вниз.Я добавляю KeyEventDispatcher со следующим dispatchKeyEventMethod в мой KeyboardFocusManger:

        @Override
        public boolean dispatchKeyEvent(KeyEvent evt) {
            if(simPanel.main.isFocused()){
                int type = evt.getID();
                if(type == KeyEvent.KEY_PRESSED){
                    int keyCode = evt.getKeyCode();
                    switch(keyCode){
                        // other cases
                        case KeyEvent.VK_PLUS:
                            // increase degreeField by 1, if double
                            // otherwise set it to "270.0"
                            return true;
                        case KeyEvent.VK_MINUS:
                            // decrease degreeField by 1, if double
                            // otherwise set it to "270.0"
                            return true;
                        default:
                            return false;
                    }
                }
                else if(type == KeyEvent.KEY_RELEASED){
                    // irrelevant
                    return false;
                }
            }
            return false;
        }

KeyEventDispatcher работает, и текст СтепениField изменяется, как и ожидалось.Однако, когда у меня есть другой фокус JTextField, в это поле также вводятся «+» или «-».

Поскольку я возвращаю true, у меня сложилось впечатление, что событие больше не должно отправлятьсяJTextField я сосредоточился.Используя отладчик NetBeans, я установил точку останова в соответствующем регистре и проверил текст выделенного текстового поля.В то время не было добавлено +.Поэтому + добавляется после того, как я закончу отправку этого события и возвращаю true.

У кого-нибудь есть идеи, как я могу на самом деле предотвратить дальнейшее распространение события?

Я знаю, что могу поставитьдополнительный слушатель в текстовом поле для предотвращения ввода символов «+» и «-», но я бы предпочел решение, которое работает и для не-символьных ключей.(У меня похожая проблема с клавишами со стрелками вверх и вниз; она ничего не ломает, просто раздражает циклическое переключение текстовых полей).


Спасибо MadProgrammer за это:

InputMap im = senderXField.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = senderXField.getActionMap();

im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "Pressed.+");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Pressed.up");

am.put("Pressed.+", angleUpAction); // works fine

К сожалению

am.put("Pressed.up", indexUpAction); // does not trigger

Это относится ко всем клавишам со стрелками.Они делают свое обычное дело (соответственно перемещают курсор или фокус) и не запускают действия.Если я использую WHEN_FOCUSED InputMap, они будут работать по назначению, что заставит меня поверить, что их поведение по умолчанию реализовано как связывание клавиш на уровне WHEN_FOCUSED, которое может быть перезаписано.

Хотя, технически, в качестве обходного пути, я мог быреализовать команды клавиш со стрелками для всех текстовых полей в моем MainWindow, это было бы ... странно.Я надеюсь, что есть лучшее решение, которое позволит мне сохранить команду для всего окна, а также перезаписать их поведение по умолчанию.


Решено

Ответы [ 2 ]

0 голосов
/ 16 декабря 2018

Я добавляю KeyEventDispatcher со следующим dispatchKeyEventMethod к своему KeyboardFocusManger:

Нет, нет, нет и нет на стольких уровнях

Фактический ответ на ваш вопросв два раза.

Во-первых, используйте DocumentFilter для фильтрации нежелательных входных данных, см. Реализация DocumentFilter для получения более подробной информации.

Причина использования этого сводится кк ряду вопросов ...

  • KeyListener могут быть уведомлены ПОСЛЕ того, как контент передается в базовую Document модель
  • Процедуры обработки ключей также могут игнорировать состояние потребления
  • KeyListener не обрабатывает сценарий использования, когда пользователь вставляет текст в поле
  • KeyListener не обрабатывает сценарий использования, когда вызывается setText
  • KeyListener - это всего лишь неудачный выбор для фильтрации контента.

Во-вторых, вам следует использовать Key Bindings API вместоKeyEventDispatcher.

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

Привязки клавиш также легче использовать повторно.Вы можете применить Action, используемый привязками клавиш к кнопкам или даже к более глобальному состоянию.Их также можно использовать для привязки нескольких клавиш к одному действию, например, клавиша + (на цифровой клавиатуре) и клавиши Shift + =

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;

public class Text {

    public static void main(String[] args) {
        new Text();
    }

    public Text() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new GridBagLayout());
            JTextField field = new JTextField(10);
            ((AbstractDocument)field.getDocument()).setDocumentFilter(new IntegerDocumentFilter());

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            add(field, gbc);

            InputMap im = field.getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = field.getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.SHIFT_DOWN_MASK), "Pressed.+");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "Pressed.+");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "Pressed.-");

            am.put("Pressed.+", new DeltaAction(field, 1));
            am.put("Pressed.-", new DeltaAction(field, -1));

            add(new JButton("Test"), gbc);
        }

        protected class DeltaAction extends AbstractAction {

            private JTextField field;
            private int delta;

            public DeltaAction(JTextField field, int delta) {
                this.field = field;
                this.delta = delta;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                String text = field.getText();
                if (text == null || text.isEmpty()) {
                    text = "0";
                }
                try {
                    int value = Integer.parseInt(text);
                    value += delta;
                    field.setText(Integer.toString(value));
                } catch (NumberFormatException exp) {
                    System.err.println("Can not convert " + text + " to an int");
                }
            }
        }

        public class IntegerDocumentFilter extends DocumentFilter {

            @Override
            public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
                try {
                    StringBuilder sb = new StringBuilder();
                    Document document = fb.getDocument();
                    sb.append(document.getText(0, offset));
                    sb.append(text);
                    sb.append(document.getText(offset, document.getLength()));

                    Integer.parseInt(sb.toString());
                    super.insertString(fb, offset, text, attr);
                } catch (NumberFormatException exp) {
                    System.err.println("Can not insert " + text + " into document");
                }
            }

            @Override
            public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String string, AttributeSet attr) throws BadLocationException {
                if (length > 0) {
                    fb.remove(offset, length);
                }
                insertString(fb, offset, string, attr);
            }
        }

    }

}
0 голосов
/ 15 декабря 2018

Вы должны добавить две отдельные панели и добавить слушатель в JPanel, в которой добавлен градус степени.

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class MyClass extends JFrame {

private JTextField degreeField = new JTextField("0");
private JTextField anotherField = new JTextField("0");
private JPanel panel1 = new JPanel();
private JPanel panel2 = new JPanel();

MyClass(){
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    panel1.add(degreeField);
    panel2.add(anotherField);
    this.getContentPane().add(panel1, BorderLayout.CENTER);
    this.getContentPane().add(panel2, BorderLayout.PAGE_END);

    panel1.addKeyListener(new KeyListener() {
        @Override
        public void keyTyped( KeyEvent e ) {
            int keyCode = e.getKeyCode();
            switch(keyCode){
                // other cases
                case KeyEvent.VK_PLUS:
                    degreeField.setText((Integer.parseInt(degreeField.getText()) + 1 )+ "");
                case KeyEvent.VK_MINUS:
                    degreeField.setText((Integer.parseInt(degreeField.getText()) - 1 ) + "");

                default:
                    //do nothing
            }
        }

        @Override
        public void keyPressed( KeyEvent e ) {
            //do nothing
        }

        @Override
        public void keyReleased( KeyEvent e ) {
            //do nothing
        }
    });
}

public static void main(String[] args){
    MyClass m = new MyClass();
    m.pack();
    m.setVisible(true);

}
}
...