JTextField DocumentListener 异常变异错误 [英] JTextField DocumentListener exception mutate error

查看:24
本文介绍了JTextField DocumentListener 异常变异错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您认为可能重复的问题的解决方案(

第 2 步:在字符组合框中选择 Cloud

第 3 步:输入整数数字并识别问题

我注意到在第 1338 行尝试访问一个对象并执行 setValue() 方法 时它在抱怨......有人知道为什么会发生这种情况吗?

更新:

DocumentFilter 似乎做对了,只要我注释掉一行(检查最大值规则时需要).这是目前为止的代码:

private void setHpBaseStatsTextFieldAction(){((AbstractDocument) hpBaseStatsTextField.getDocument()).setDocumentFilter(新文档过滤器(){public void replace(DocumentFilter.FilterBypass fb, int offset, int length,字符串文本,AttributeSet attrs) 抛出 BadLocationException {if (offset >= fb.getDocument().getLength()) {System.out.println("新增:" + text);} 别的 {String old = fb.getDocument().getText(offset, length);System.out.println("用" + text 替换" + old + ");}super.replace(fb, offset, length, text, attrs);updateStatGridLabels();}public void insertString(DocumentFilter.FilterBypass fb, int offset,字符串文本,AttributeSet attr) 抛出 BadLocationException {System.out.println("新增:" + text);super.insertString(fb, offset, text, attr);updateStatGridLabels();}public void remove(DocumentFilter.FilterBypass fb, int offset, int length)抛出 BadLocationException {System.out.println("移除:" + fb.getDocument().getText(offset, length));super.remove(fb, offset, length);updateStatGridLabels();}公共无效updateStatGridLabels(){String currCharacter = charSelCombo.getSelectedItem().toString();String checkBaseHpInGui = hpBaseStatsTextField.getText();int baseHpInGui = 0;如果(isInteger(checkBaseHpInGui)){baseHpInGui = Integer.parseInt(checkBaseHpInGui);}if ((!(currCharacter.equals(guiCharSelDefaultValue[unselectedDefaultElement]))) && (isInteger(checkBaseHpInGui))){字符[getSelectedCharactersIndex()].setBaseHp(baseHpInGui);//hpBaseStatsTextField.setText(Integer.toString(characters[getSelectedCharactersIndex()].getBaseHp()));setHpStatGridRowValues();}}});}

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

更新 2: 我开始按照 camickr 的建议使用 SwingUtilities.invokeLater.它适用于我在上面的代码片段中注释掉的那行,但是,它将它设置为无限循环......

private void setHpBaseStatsTextFieldAction(){((AbstractDocument) hpBaseStatsTextField.getDocument()).setDocumentFilter(新文档过滤器(){boolean newTextReplaceSet = false;public void replace(DocumentFilter.FilterBypass fb, int offset, int length,字符串文本,AttributeSet attrs) 抛出 BadLocationException {if (offset >= fb.getDocument().getLength()) {System.out.println("新增:" + text);} 别的 {String old = fb.getDocument().getText(offset, length);System.out.println("用" + text 替换" + old + ");}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) 抛出 BadLocationException {System.out.println("新增:" + text);super.insertString(fb, offset, text, attr);updateStatGridLabels();newTextInsertSet = true;}boolean newTextRemoveSet = false;public void remove(DocumentFilter.FilterBypass fb, int offset, int length)抛出 BadLocationException {System.out.println("移除:" + fb.getDocument().getText(offset, length));super.remove(fb, offset, length);updateStatGridLabels();newTextRemoveSet = true;}公共无效updateStatGridLabels(){String currCharacter = charSelCombo.getSelectedItem().toString();String checkBaseHpInGui = hpBaseStatsTextField.getText();int baseHpInGui = 0;如果(isInteger(checkBaseHpInGui)){baseHpInGui = Integer.parseInt(checkBaseHpInGui);}if ((!(currCharacter.equals(guiCharSelDefaultValue[unselectedDefaultElement]))) && (isInteger(checkBaseHpInGui))){字符[getSelectedCharactersIndex()].setBaseHp(baseHpInGui);if (!newTextReplaceSet || !newTextInsertSet || newTextRemoveSet) {SwingUtilities.invokeLater(new Runnable() {公共无效运行(){System.out.println("运行");hpBaseStatsTextField.setText(Integer.toString(characters[getSelectedCharactersIndex()].getBaseHp()));}});}//hpBaseStatsTextField.setText(Integer.toString(characters[getSelectedCharactersIndex()].getBaseHp()));setHpStatGridRowValues();}}});}

我似乎无法正确设置布尔标志的组合以阻止它执行无限循环,但仍然执行 System.out.println("run"); 行JTextField 中所做的每一个更改.如果我将 if 语句if (!newTextReplaceSet || !newTextInsertSet || newTextRemoveSet) 更改为 if (!newTextReplaceSet || newTextInsertSet || newTextRemoveSet)code>,它将从无限循环执行 System.out.println("run"); 到只执行一次(并且在对 进行另一次更改时不会再次执行)JTextField).有人可以帮我吗?

解决方案

出于某种原因,这在使用 actionListener 时工作正常

这是因为 ActionListener 代码是在 Document 更新后调用的.

<块引用>

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

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

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

如果你真的需要这样做,那么有两个常见的解决方案:

  1. 不要使用 DocumentListener.相反,您可以使用 DocumentFilter,它允许您在添加/删除测试时操作文档.

  2. 您需要延迟更新文档.这样做的方法是使用 SwingUtilities.invokeLater(...).这会将代码放在 Event Dispatch Thread (EDT) 的末尾.

<块引用>

baseHp 不能迭代到 9999 以上).

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

阅读Swing 教程.有使用 JFormattedTextField 和 JSpinner 的示例.您还可以在文本组件功能"部分找到 DocumentFilter 的示例.

<块引用>

然而,它将它设置为无限循环......

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

  1. DocumentListener 用于在从 Document 添加/删除数据后在您的应用程序中进行处理

  2. DocumentFilter 用于在将数据添加到 Document 之前编辑数据

然而,循环问题在两种情况下都是一样的,解决方案是一样的.

问题在于你:

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

所以一个解决方案是在调用 setText(...) 方法之前移除 Listener,然后恢复 Listener.

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

虽然我会说实际更改输入的数据是不正常的.通常,您验证数据并在出现错误时显示消息,而不是尝试修复数据并重置文本.通过不更改文本字段,您不必担心会导致无限循环.

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

Edit: The solution to question you think is a possible duplicate(java.lang.IllegalStateException while using Document Listener in TextArea, Java) is exactly what I have entered in the code to come to this problem. I used the 3 override methods when writing the custom DocumentListener. However, this did not solve the problem.

For some reason, this worked okay when using actionListener (See here - Update JTextField.addActionListener without pressing "enter"). The issue with actionListener was I had to hit "enter" every time I wanted the label to update. So someone suggested use DocumentListener instead. Having done this. I am getting an error I cannot figure out, as well as really strange behavior.

Since the code is too large to post here, I have zipped up the project (using with Intellij and JRE 1.8) > https://www.dropbox.com/s/pf4hiuk9y0jby7y/FF7LevelUpStatCalculator.zip?dl=0

Isolated the code block for where the issue is happening (for quick review):

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();
                    }
                }
            }
    );
}

Reproduce Issue(s):

Step 1: Run StartGui.java

Step 2: Select Cloud in the character combobox

Step 3: Enter integer digits and recognize issue(s)

I noticed it's complaining when trying to access an object and execute a setValue() method on line #1338... Anyone have any ideas why this is happening?

Update:

DocumentFilter seems to be doing it correctly, as long as I comment out a line (that is needed when checking the rules of the max value). Here is the code thus far:

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();
                    }
                }
            }
    );
}

I could still use some help with this.

Update 2: I started using SwingUtilities.invokeLater as camickr suggested. It works with the line I commented out in the above code snippet, however, it sets it off into an infinite loop...

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();
                    }
                }
            }
    );
}

I can't seem to get the combination of boolean flags set right to stop it from doing an infinite loop, but still execute the line System.out.println("run"); with every change made in the JTextField. If I change the if statement from if (!newTextReplaceSet || !newTextInsertSet || newTextRemoveSet) to if (!newTextReplaceSet || newTextInsertSet || newTextRemoveSet), it will go from executing System.out.println("run"); with an infinite loop, to only executing it once (and not again when another change is made to the JTextField). Can anyone help me?

解决方案

For some reason, this worked okay when using actionListener

This is because the ActionListener code is invoked AFTER the Document has been updated.

I used the 3 override methods when writing the custom DocumentListener. However, this did not solve the problem

The problem is that you can't change the Document in the DocumentListener because the Document hasn't been updated with the changed text yet.

The real question is why are you attempting to update a text field as you type text into the text field?

If you really need to do this then there a two common solution:

  1. Don't use a DocumentListener. Instead you can use a DocumentFilter which allows you to manipulate the Document as test is being added/removed.

  2. You need to delay updating the Document. The way you do this is to use SwingUtilities.invokeLater(...). This will place the code on the end of the Event Dispatch Thread (EDT).

baseHp cannot iterate above 9999).

Then this is editing based logic, which should be done in a DocumentFilter. Or you can even use a JFormattedTextField or maybe a JSpinner.

Read the Swing Tutorial. There are examples of using a JFormattedTextField and JSpinner. You will also find example of a DocumentFilter in the `Text Component Features' section.

Edit:

however, it sets it off into an infinite loop...

Well, first you need to understand the difference between using a DocumentListener and a DocumentFilter.

  1. The DocumentListener is used for processing in your application after the data has been added/removed from the Document

  2. A DocumentFilter is used to edit the data before the data is added to the Document

However, the looping problem is the same in both cases and the solution is the same.

The problem is that you:

  1. enter text into the text field
  2. a Listener is invoked
  3. you manipulate the text and use setText() to reset the data
  4. the Listener is invoked again.
  5. ... 3 and 4 keep repeating.

So a solution is to remove the Listener before you invoke the setText(...) method and then restore the Listener.

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

Although I would say it is not normal to actually change the data entered. Normally you validate the data and display a message if there is an error instead of trying to fix the data and reset the text. By not changing the text field you don't have to worry about causing an infinite loop.

That is why you would use a JFormattedTextField or maybe JSpinner as the editing component. You can easily force the data to be number with a maximum number of digits.

这篇关于JTextField DocumentListener 异常变异错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆