编辑:您认为可能重复的问题的解决方案(java.lang.IllegalStateException while using Document Listener in TextArea, Java)正是我在代码中输入的解决此问题的方法。在编写自定义DocumentListener时,我使用了3种覆盖方法。但是,这并不能解决问题。

由于某些原因,使用actionListener时此方法可以正常工作(请参阅此处-Update JTextField.addActionListener without pressing "enter")。 actionListener的问题是,每次我想更新标签时,我都必须打“ enter”。因此有人建议使用DocumentListener代替。完成此操作。我遇到了我无法弄清的错误,以及非常奇怪的行为。

由于代码太大而无法在此处发布,因此我将项目压缩(与Intellij和JRE 1.8结合使用)> https://www.dropbox.com/s/pf4hiuk9y0jby7y/FF7LevelUpStatCalculator.zip?dl=0

隔离发生问题的代码块(以便快速查看):

private void setHpBaseStatsTextFieldAction(){
    hpBaseStatsTextField.getDocument().addDocumentListener(
            new DocumentListener() {
                public void insertUpdate(DocumentEvent e) {
                    updateStatGridLabels();
                }
                public void removeUpdate(DocumentEvent e) {
                    updateStatGridLabels();
                }
                public void changedUpdate(DocumentEvent e) {
                    //Plain text components do not fire these events
                }

                public void updateStatGridLabels() {
                    String currCharacter = charSelCombo.getSelectedItem().toString();
                    String checkBaseHpInGui = hpBaseStatsTextField.getText();
                    int baseHpInGui = 0;
                    if (isInteger(checkBaseHpInGui)){
                        baseHpInGui = Integer.parseInt(checkBaseHpInGui);
                    }
                    if ((!(currCharacter.equals(guiCharSelDefaultValue[unselectedDefaultElement]))) && (isInteger(checkBaseHpInGui))){
                        characters[getSelectedCharactersIndex()].setBaseHp(baseHpInGui);
                        hpBaseStatsTextField.setText(Integer.toString(characters[getSelectedCharactersIndex()].getBaseHp()));
                        setHpStatGridRowValues();
                    }
                }
            }
    );
}


重现问题:

步骤1:运行StartGui.java
java - JTextField DocumentListener异常变异错误-LMLPHP

步骤2:在角色组合框中选择Cloud
java - JTextField DocumentListener异常变异错误-LMLPHP

步骤3:输入整数并识别问题
java - JTextField DocumentListener异常变异错误-LMLPHP
java - JTextField DocumentListener异常变异错误-LMLPHP

我注意到它在尝试访问对象并在#1338行上执行setValue() method时在抱怨...任何人都知道为什么会发生这种情况?

更新:

只要我注释掉一行(检查最大值的规则时就需要),DocumentFilter似乎就可以正确地执行此操作。这是到目前为止的代码:

private void setHpBaseStatsTextFieldAction(){
    ((AbstractDocument) hpBaseStatsTextField.getDocument()).setDocumentFilter(
            new DocumentFilter() {
                public void replace(DocumentFilter.FilterBypass fb, int offset, int length,
                                    String text, AttributeSet attrs) throws BadLocationException {
                    if (offset >= fb.getDocument().getLength()) {
                        System.out.println("Added: " + text);
                    } else {
                        String old = fb.getDocument().getText(offset, length);
                        System.out.println("Replaced " + old + " with " + text);
                    }
                    super.replace(fb, offset, length, text, attrs);
                    updateStatGridLabels();
                }

                public void insertString(DocumentFilter.FilterBypass fb, int offset,
                                         String text, AttributeSet attr) throws BadLocationException {
                    System.out.println("Added: " + text);
                    super.insertString(fb, offset, text, attr);
                    updateStatGridLabels();
                }

                public void remove(DocumentFilter.FilterBypass fb, int offset, int length)
                        throws BadLocationException {
                    System.out.println("Removed: " + fb.getDocument().getText(offset, length));
                    super.remove(fb, offset, length);
                    updateStatGridLabels();
                }

                public void updateStatGridLabels() {
                    String currCharacter = charSelCombo.getSelectedItem().toString();
                    String checkBaseHpInGui = hpBaseStatsTextField.getText();
                    int baseHpInGui = 0;
                    if (isInteger(checkBaseHpInGui)){
                        baseHpInGui = Integer.parseInt(checkBaseHpInGui);
                    }
                    if ((!(currCharacter.equals(guiCharSelDefaultValue[unselectedDefaultElement]))) && (isInteger(checkBaseHpInGui))){
                        characters[getSelectedCharactersIndex()].setBaseHp(baseHpInGui);
                        //hpBaseStatsTextField.setText(Integer.toString(characters[getSelectedCharactersIndex()].getBaseHp()));
                        setHpStatGridRowValues();
                    }
                }
            }
    );
}


我仍然可以使用一些帮助。

更新2:我开始按照camickr的建议使用SwingUtilities.invokeLater。它可以与我在上面的代码段中注释掉的行一起使用,但是,它会将它设置为无限循环...

private void setHpBaseStatsTextFieldAction(){
    ((AbstractDocument) hpBaseStatsTextField.getDocument()).setDocumentFilter(
            new DocumentFilter() {
                boolean newTextReplaceSet = false;
                public void replace(DocumentFilter.FilterBypass fb, int offset, int length,
                                    String text, AttributeSet attrs) throws BadLocationException {
                    if (offset >= fb.getDocument().getLength()) {
                        System.out.println("Added: " + text);
                    } else {
                        String old = fb.getDocument().getText(offset, length);
                        System.out.println("Replaced " + old + " with " + text);
                    }
                    super.replace(fb, offset, length, text, attrs);
                    updateStatGridLabels();
                    newTextReplaceSet = true;
                }
                boolean newTextInsertSet = false;
                public void insertString(DocumentFilter.FilterBypass fb, int offset,
                                         String text, AttributeSet attr) throws BadLocationException {
                    System.out.println("Added: " + text);
                    super.insertString(fb, offset, text, attr);
                    updateStatGridLabels();
                    newTextInsertSet = true;
                }
                boolean newTextRemoveSet = false;
                public void remove(DocumentFilter.FilterBypass fb, int offset, int length)
                        throws BadLocationException {
                    System.out.println("Removed: " + fb.getDocument().getText(offset, length));
                    super.remove(fb, offset, length);
                    updateStatGridLabels();
                    newTextRemoveSet = true;
                }

                public void updateStatGridLabels() {
                    String currCharacter = charSelCombo.getSelectedItem().toString();
                    String checkBaseHpInGui = hpBaseStatsTextField.getText();
                    int baseHpInGui = 0;
                    if (isInteger(checkBaseHpInGui)){
                        baseHpInGui = Integer.parseInt(checkBaseHpInGui);
                    }
                    if ((!(currCharacter.equals(guiCharSelDefaultValue[unselectedDefaultElement]))) && (isInteger(checkBaseHpInGui))){
                        characters[getSelectedCharactersIndex()].setBaseHp(baseHpInGui);
                        if (!newTextReplaceSet || !newTextInsertSet || newTextRemoveSet) {
                            SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                    System.out.println("run");
                                    hpBaseStatsTextField.setText(Integer.toString(characters[getSelectedCharactersIndex()].getBaseHp()));
                                }
                            });
                        }
                        //hpBaseStatsTextField.setText(Integer.toString(characters[getSelectedCharactersIndex()].getBaseHp()));
                        setHpStatGridRowValues();
                    }
                }
            }
    );
}


我似乎无法正确设置布尔标志的组合以阻止它进行无限循环,但仍然对JTextField中进行的每个更改执行System.out.println("run");行。如果将if statementif (!newTextReplaceSet || !newTextInsertSet || newTextRemoveSet)更改为if (!newTextReplaceSet || newTextInsertSet || newTextRemoveSet),它将从执行无限循环的System.out.println("run");变为仅执行一次(而对JTextField进行另一次更改则不再执行)。谁能帮我?

最佳答案

出于某种原因,使用actionListener时可以正常工作


这是因为Document更新后,将调用ActionListener代码。


  在编写自定义DocumentListener时,我使用了3种覆盖方法。但是,这并没有解决问题


问题在于您无法在DocumentListener中更改Document,因为Document尚未使用更改后的文本进行更新。

真正的问题是,当您在文本字段中键入文本时,为什么要尝试更新文本字段?

如果您确实需要执行此操作,则有两种常见的解决方案:


不要使用DocumentListener。相反,您可以使用DocumentFilter,它允许您在添加/删除测试时操作文档。
您需要延迟更新文档。执行此操作的方法是使用SwingUtilities.invokeLater(...)。这会将代码放在Event Dispatch Thread (EDT)的末尾。



  baseHp无法在9999以上进行迭代。


然后,这是基于编辑的逻辑,应在DocumentFilter中完成。或者甚至可以使用JFormattedTextField或JSpinner。

阅读Swing Tutorial。有使用JFormattedTextField和JSpinner的示例。您还可以在“文本组件功能”部分中找到DocumentFilter的示例。

编辑:


  但是,它使它陷入无限循环...


好吧,首先,您需要了解使用DocumentListener和DocumentFilter之间的区别。


从文档中添加/删除数据后,DocumentListener用于在应用程序中进行处理
在将数据添加到文档之前,使用DocumentFilter来编辑数据


但是,在两种情况下循环问题都是相同的,解决方案是相同的。

问题是您:


在文本字段中输入文本
侦听器被调用
您操纵文本并使用setText()重置数据
监听器再次被调用。
... 3和4不断重复。


因此,一种解决方案是在调用setText(...)方法之前删除侦听器,然后还原侦听器。

doc.removeDocumentListener( this );
textfield.setText(...);
doc.addDocumentListener( this );


尽管我会说实际上更改输入的数据是不正常的。通常,您会验证数据并在出现错误时显示一条消息,而不是尝试修复数据并重置文本。通过不更改文本字段,您不必担心引起无限循环。

这就是为什么要使用JFormattedTextField或JSpinner作为编辑组件的原因。您可以轻松地将数据强制为最大位数的数字。

10-07 16:38