Как проверить ручное редактирование поля JSpinner с использованием подхода DefaultEditor

Я адаптирую код отсюда:

Прослушиватель изменения значения для JTextField

РЕДАКТИРОВАТЬ 2

Следующий код дает мне бесконечный цикл диалогов, когда я нажимаю стрелку вверх:

STRING: STRING: 10 VALS: 10 STRING: STRING: 10 VALS: 10 STRING: STRING: 10 VALS: 10 .....

Предупреждение, вам нужно будет использовать диспетчер задач, чтобы убить его.

    public static void main(String[] args) {
    // TODO Auto-generated method stub

    JFrame F = new JFrame();
    F.setVisible(true);
    JPanel p = new JPanel();


    final JSpinner spin2 = new JSpinner();
    spin2.setModel(new SpinnerNumberModel(10, 10, 100, 1));

    JComponent comp = spin2.getEditor();
    JFormattedTextField field = (JFormattedTextField) comp.getComponent(0);
    DefaultFormatter formatter = (DefaultFormatter) field.getFormatter();
    formatter.setCommitsOnValidEdit(true);


    ((JSpinner.DefaultEditor)spin2.getEditor()).getTextField().getDocument().addDocumentListener(new DocumentListener() {
          public void changedUpdate(DocumentEvent e) {
                warn();
              }
              public void removeUpdate(DocumentEvent e) {
                warn();
              }
              public void insertUpdate(DocumentEvent e) {
                warn();
              }

              public void warn() {
                  String text = ((JSpinner.DefaultEditor)spin2.getEditor()).getTextField().getText();
                  JOptionPane.showMessageDialog(null,   "STRING: "+text, "Error Massage",     JOptionPane.ERROR_MESSAGE);
                  if (text != null && !text.trim().isEmpty()) {
                      int stringValue = Integer.parseInt(((JSpinner.DefaultEditor)spin2.getEditor()).getTextField().getText());
                      JOptionPane.showMessageDialog(null,
                              "VALS: "+spin2.getValue(), "Error Massage",
                              JOptionPane.ERROR_MESSAGE);
                     if (stringValue<10 || stringValue >100){
                       JOptionPane.showMessageDialog(null,
                          "Error: Number outside bounds", "Error Massage",
                          JOptionPane.ERROR_MESSAGE);
                     }

                  }
              }
            });


    p.add(spin2);   


    F.add(p);
    F.pack();
    F.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


}

РЕДАКТИРОВАТЬ 3

Это меняет фон на красный, когда недействительно, НО значения возвращаются к предыдущим (если недействительны), когда поле теряет фокус. Я хочу иметь возможность установить JOptionPane в этот момент, говоря, что значение STILL недействительно, вместо того, чтобы вернуться к предыдущему:

        ((JSpinner.DefaultEditor)Position.getEditor()).getTextField().addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                //LOG.info("" + evt);
                if ("editValid".equals(evt.getPropertyName())) {
                    if (Boolean.FALSE.equals(evt.getNewValue())) {
                        SpinnerNumberModel model = (SpinnerNumberModel) Position.getModel();  

                        ((JSpinner.DefaultEditor)Position.getEditor()).getTextField().setBackground(Color.RED);
                        ((JSpinner.DefaultEditor)Position.getEditor()).getTextField().setToolTipText("Amount must be in range [ " + model.getMinimum() + " ... " + model.getMaximum() + " ] for this symbol");

                    }
                    else{
                        ((JSpinner.DefaultEditor)Position.getEditor()).getTextField().setBackground(Color.WHITE);
                    }
                }

            }
        });

//////////////////////////////////////////////////////////////////////////////////////////////////

ИСХОДНЫЙ ВОПРОС

Но если я использую счетчик, чтобы ввести значение ниже нижней границы. Я получаю "(" в текстовом поле и эту ошибку:

Пользовательские DocumentListeners и formattedTextField плохо сочетаются друг с другом, лучше не смешивать. Вместо этого используйте PropertyChangeListener в текстовом поле, которое прослушивает изменения своего свойства editValid: всякий раз, когда оно изменяется на false, вы можете уведомлять пользователей

final JSpinner spin2 = new JSpinner();
spin2.setModel(new SpinnerNumberModel(10, 10, 100, 1));

JComponent comp = spin2.getEditor();
JFormattedTextField field = (JFormattedTextField) comp.getComponent(0);
DefaultFormatter formatter = (DefaultFormatter) field.getFormatter();
formatter.setCommitsOnValidEdit(true);


((JSpinner.DefaultEditor)spin2.getEditor()).getTextField().getDocument().addDocumentListener(new DocumentListener() {
          public void changedUpdate(DocumentEvent e) {
                warn();
              }
              public void removeUpdate(DocumentEvent e) {
                warn();
              }
              public void insertUpdate(DocumentEvent e) {
                warn();
              }

              public void warn() {
                  int stringValue = Integer.parseInt(((JSpinner.DefaultEditor)spin2.getEditor()).getTextField().getText());
                  JOptionPane.showMessageDialog(null,
                          "VALS: "+spin2.getValue(), "Error Massage",
                          JOptionPane.ERROR_MESSAGE);
                 if (stringValue<10 || stringValue >100){
                   JOptionPane.showMessageDialog(null,
                      "Error: Please enter number bigger than 0", "Error Massage",
                      JOptionPane.ERROR_MESSAGE);
                 }

              }
            });

person ManInMoon    schedule 03.01.2014    source источник
comment
@MadProgrammer некоторые ошибки, основаны на моем сообщении, мне нужно удалить это сообщение, но логика то же самое для DocumentListener или DocumentFilter :-)   -  person MadProgrammer    schedule 03.01.2014
comment
для лучшей справки, скорее опубликуйте краткий исполняемый, компилируемый,   -  person mKorbel    schedule 03.01.2014
comment
@MadProgrammer это обратная ситуация   -  person mKorbel    schedule 03.01.2014
comment
@mKorbel Извините, мой комментарий был немного коротким. Я бы не использовал _1_ для проверки значений, а фактически отфильтровал бы их напрямую. Но я думаю, что у счетчика уже есть свое собственное или это отформатированное поле...: P   -  person mKorbel    schedule 03.01.2014
comment
@MadProgrammer не проблема при использовании JOptionPane, этот производный от контейнера верхнего уровня должен быть завернут в invokeLater во всех случаях   -  person MadProgrammer    schedule 03.01.2014
comment
@MadProgrammer Я действительно застрял здесь - не могли бы вы указать мне рабочий пример, пожалуйста.   -  person mKorbel    schedule 03.01.2014
comment
Эй, это отлично работает. Однако, если я ввожу низкое значение и перемещаю мышь в другое поле. Значение текстового поля счетчика возвращается к предыдущему значению. Я хотел бы поймать это событие и выдать предупреждение в этот момент. Я предполагаю, что это необходимо сделать, поскольку текстовое поле теряет фокус. Можете ли вы сказать мне, как это сделать, пожалуйста?   -  person ManInMoon    schedule 03.01.2014


Ответы (4)


Кстати, лично я согласен с Мэдом - такое навязчивое уведомление имеет тенденцию раздражать меня и, возможно, ваших пользователей..

field.addPropertyChangeListener(new PropertyChangeListener() {

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        LOG.info("" + evt);
        if ("editValid".equals(evt.getPropertyName()) 
            &&  Boolean.FALSE.equals(evt.getNewValue())) {
          SpinnerNumberModel model = (SpinnerNumberModel) spin2.getModel();  
          JOptionPane.showMessageDialog(null,
          "Error: Number must be in range [" + model.getMinimum() + " ..." + model.getMaximum() + "]",
           "Error Massage",
          JOptionPane.ERROR_MESSAGE);

        }

    }
});

У вас есть несколько основных вариантов.

person kleopatra    schedule 03.01.2014
comment
@ManInMoon вы получаете уведомление один раз при вводе недопустимого значения (= введите низкое значение) — если пользователь игнорирует предупреждение, недопустимое промежуточное значение возвращается к допустимому значению для защиты целостности данных. . В этом случае вы ничего не должны делать - пользователь, игнорирующий первое предупреждение, скорее всего, проигнорирует и второе (или n-е) предупреждение... - person ManInMoon; 04.01.2014
comment
Да, наверное, ты прав. НО я хотел бы знать, когда произойдет возврат, и отключить некоторые другие функции, которые полагаются на это значение. - person kleopatra; 04.01.2014
comment
Да, я вижу это и использую его для установки цвета фона в зависимости от того, является ли значение ИСТИНА или ЛОЖЬ. Однако, если я не выберу значение в пределах границ, оно вернется к предыдущему значению, как только я уйду от поля. Я хотел бы поймать это. - person ManInMoon; 04.01.2014
comment
что вы имеете в виду под ловушкой? Помимо получения уведомления об изменении свойства editValid, которое вы видите в случае возврата... - person ManInMoon; 06.01.2014
comment
извините, но это настолько недружелюбно к пользователю, что я отказываюсь помочь вам зайти так далеко ;-) - person kleopatra; 06.01.2014
comment
Извините, я не понимаю. Как вы думаете, что не так с этим подходом? - person kleopatra; 06.01.2014
comment
Не говоря уже о более ранних комментариях, вы однажды предупредили, что поведение по умолчанию заключается в том, чтобы гарантировать целостность данных путем возврата, и вы хотите взорвать это и в то же время заманить своих пользователей в ловушку чрезмерно навязчивым повторным предупреждением ... - person ManInMoon; 06.01.2014
comment
нет совсем! Поле становится красным, чтобы количество пользователей было недействительным. Тогда, только если он решит оставить его недействительным, он получит сообщение! Если я позволю ему вернуться, он получит значение, которое он НЕ выбрал! Как это неправильно? - person kleopatra; 06.01.2014
comment
Возможно, я не объясняю себя. Значение, которое пользователь вводит в IS, является недопустимым, но он может не заметить, что счетчик возвращается к предыдущему значению. Что я хочу сделать, так это предупредить его, что его типизированное значение было изменено. Ты видишь? - person ManInMoon; 06.01.2014
comment
как он/она может не заметить, что цвет поля изменился? В любом случае, я бы предложил новый вопрос с SSCCE, объясняющий точные шаги для воспроизведения того, что вы хотите / ожидаете, по сравнению с тем, что на самом деле происходит. Сделайте его достаточно сложным (хотя и коротким и самостоятельным), чтобы продемонстрировать ваши настоящие требования. - person ManInMoon; 06.01.2014
comment
stackoverflow.com/questions/20966239/ - person kleopatra; 06.01.2014
comment
Спасибо большое. К сожалению, он никогда не доходит до предупреждения(), ошибка возникает в методах insertUpdate() или removeUpdate(). Я посмотрю на проверку ввода, как вы предлагаете. НО починить мой Spinner было бы предпочтительнее. Странно, я бы подумал, что это основное требование. - person ManInMoon; 07.01.2014

Лично я хотел бы сделать и то, и другое...

  1. Вы можете проверить наличие "пустого" String
  2. Исключение говорит, что строка, которую вы передаете String, является пустой строкой. Поэтому не забудьте проверить
    public void warn() {
        String text = ((JSpinner.DefaultEditor)spin2.getEditor()).getTextField().getText();
        if (text != null && !text.trim().isEmpty()) {
            try {
                int stringValue = Integer.parseInt(text);
                JOptionPane.showMessageDialog(null,
                      "VALS: "+spin2.getValue(), "Error Massage",
                      JOptionPane.ERROR_MESSAGE);
                if (stringValue<10 || stringValue >100){
                JOptionPane.showMessageDialog(null,
                  "Error: Please enter number bigger than 0", "Error Massage",
                  JOptionPane.ERROR_MESSAGE);
                }
            } catch (NumberFormatException exp) {
                exp.printStackTrace();
            }
        }
    }
    
    на null и пустое значение, прежде чем передавать его в JSpinner.

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

public void warn() {
    String text = ((JSpinner.DefaultEditor)spin2.getEditor()).getTextField().getText();
    if (text != null && !text.trim().isEmpty()) {
        try {
            int stringValue = Integer.parseInt(text);
            JOptionPane.showMessageDialog(null,
                  "VALS: "+spin2.getValue(), "Error Massage",
                  JOptionPane.ERROR_MESSAGE);
            if (stringValue<10 || stringValue >100){
            JOptionPane.showMessageDialog(null,
              "Error: Please enter number bigger than 0", "Error Massage",
              JOptionPane.ERROR_MESSAGE);
            }
        } catch (NumberFormatException exp) {
            exp.printStackTrace();
        }
    }
}

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

Если вам не особенно нужны функциональные возможности JSpinner (прогон значений вверх и вниз в последовательности), вы можете взглянуть на использование DocumentFilter (для примеры), что позволит вам контролировать то, что попадает в поле. Вы должны знать, что невозможно (или достаточно близко к этому) добавить DocumentFilter к JSpinner... :P

Вы можете поймать исключение

person MadProgrammer    schedule 03.01.2014
comment
См. топ EDIT 2, пожалуйста - person ManInMoon; 03.01.2014
comment
@ManInMoon, неправда. Это в вашей трассировке стека вашей ошибки: com.NoviumResearch.ValueAtRisk.Sigma$7.warn(Sigma.java:626) - person ManInMoon; 03.01.2014

Вы можете взглянуть на apache commons-io и класс StringUtils, у них есть довольно много хороших методов, облегчающих проверку пустых/пустых строк.

Это оказалось огромной болью для тыла. Я хотел сразу же покрасить фон текстового поля. Закончилось созданием редактора, который переопределял редактор, который я хотел использовать, а затем соответствующим образом установил его в качестве редактора счетчиков. Кажется, пока работает на меня.

person thuri    schedule 03.01.2014
comment
Если вы хотите пофантазировать, вы можете зарегистрировать слушателей для события изменения здесь. Поскольку я просто хотел изменить цвета в текстовом поле, мне не нужно было идти дальше этого. - person jzd; 03.01.2014

Добавьте класс java со следующим (удалите кавычки вокруг блока кода, у меня проблемы с редактором переполнения стека):

Затем при добавлении счетчика сделайте следующее:

`public class CustomNumberEditor extends JSpinner.NumberEditor {
    public CustomNumberEditor( JSpinner spinner ) {
        super( spinner );
        ((DefaultFormatter ) ((JFormattedTextField) getComponent( 0 )).getFormatter()).setCommitsOnValidEdit( true );
    }
    @Override public void propertyChange(PropertyChangeEvent e) {
        super.propertyChange( e );
        if( e.getPropertyName().equals( "value" ) )
            doStuff( (int) e.getNewValue() );
    }
    private void doStuff( int value )
    {
        //do stuff
    }
}`

Я бы не стал использовать

`public class CustomNumberEditor extends JSpinner.NumberEditor {
    public CustomNumberEditor( JSpinner spinner ) {
        super( spinner );
        ((DefaultFormatter ) ((JFormattedTextField) getComponent( 0 )).getFormatter()).setCommitsOnValidEdit( true );
    }
    @Override public void propertyChange(PropertyChangeEvent e) {
        super.propertyChange( e );
        if( e.getPropertyName().equals( "value" ) )
            doStuff( (int) e.getNewValue() );
    }
    private void doStuff( int value )
    {
        //do stuff
    }
}`
для этой задачи. Вместо этого либо позвольте счетчику делать свою работу, либо используйте
_quantitySpinner.setEditor(new CustomNumberEditor(_quantitySpinner));
или _3_.

_quantitySpinner.setEditor(new CustomNumberEditor(_quantitySpinner));
person Evan    schedule 25.09.2018
comment
Еще одно важное замечание: для этого вам необходимо установить setAllowsInvalid в значение false в вашем редакторе. Если вы по какой-то причине хотите принять недопустимые значения, я уверен, что есть вторая функция, которую можно было бы переопределить в редакторе, но это не входило в объем того, что мне нужно было сделать. - person Evan; 25.09.2018
comment
Исключение в потоке "AWT-EventQueue-0" java.lang.NumberFormatException: для входной строки: "" в java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) в java.lang.Integer.parseInt(Integer.java: 504) в java.lang.Integer.parseInt(Integer.java:527) в com.NResearch.ValueAtRisk.Sigma$7.warn(Sigma.java:626) в com.NResearch.ValueAtRisk.Sigma$7.removeUpdate(Sigma.java :619) в javax.swing.text.AbstractDocument.fireRemoveUpdate(AbstractDocument.java:260) в javax.swing.text.AbstractDocument.handleRemove(AbstractDocument.java:623) в javax.swing.text.AbstractDocument.remove(AbstractDocument. java:591) в javax.swing.text.AbstractDocument.replace(AbstractDocument.java:667) в javax.swing.text.JTextComponent.setText(JTextComponent.java:1718) в javax.swing.JFormattedTextField$AbstractFormatter.install(JFormattedTextField .java:949) в javax.swing.text.DefaultFormatter.install(DefaultFormatter.java:124) в javax.swing.text.I nternationalFormatter.install(InternationalFormatter.java:285) в javax.swing.JFormattedTextField.setFormatter(JFormattedTextField.java:465) в javax.swing.JFormattedTextField.setValue(JFormattedTextField.java:789) в javax.swing.JFormattedTextField.processFocusEvent(JFormattedTextField) .java:636) в java.awt.Component.processEvent(Component.java:6261) в java.awt.Container.processEvent(Container.java:2229) в java.awt.Component.dispatchEventImpl(Component.java:4861) в java.awt.Container.dispatchEventImpl(Container.java:2287) в java.awt.Component.dispatchEvent(Component.java:4687) в java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1895) в java.awt. DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:938) в java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:570) в java.awt.Component.dispatchEventImpl(Component.java:4731) в java.awt.Container.dispa tchEventImpl(Container.java:2287) в java.awt.Component.dispatchEvent(Component.java:4687) в java.awt.EventQueue.dispatchEventImpl(EventQueue.java:735) в java.awt.EventQueue.access$200(EventQueue. java:103) в java.awt.EventQueue$3.run(EventQueue.java:694) в java.awt.EventQueue$3.run(EventQueue.java:692) в java.security.AccessController.doPrivileged(собственный метод) в java .security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) в java.security. ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87) в java.awt.EventQueue$4.run(EventQueue.java:708) в java.awt.EventQueue$4.run(EventQueue.java:706) в java.security.AccessController. doPrivileged(собственный метод) в java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) в java.awt.EventQueue.dispatchEvent(EventQueue.java:705) в java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242) ) в java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161) в java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150) в java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146) в java.awt .EventDispatchThread.pumpEvents(EventDispatchThread.java:138) в java.awt.EventDispatchThread.run(EventDispatchThread.java:91) - person Evan; 26.09.2018