java 使用几个曲线球在 Swing JTextArea 上强制执行最大字符数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13863795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 14:13:42  来源:igfitidea点击:

Enforce max characters on Swing JTextArea with a few curve balls

javaswingjtextarea

提问by IAmYourFaja

I'm trying to add functionality to a Swing JLabel and JTextArea such that:

我正在尝试向 Swing JLabel 和 JTextArea 添加功能,以便:

  • The user is only allowed to enter 500 characters into the textarea (max)
  • The label contains a string message telling the user how many characters they have left (after every key stroke or backspace)
  • When the components initialize the label reads "500 characters maximum!"
  • For the first 500 characters typed, for every keystroke (a - z, A - Z, 0 - 9, and punctuation) typed, the label reads "x characters remaining", where xis the number of chars they have left before they reach the max of 500
  • When the 500th character is typed, the label reads "0 characters remaining", and no further characters can be typed into the text area
  • If the user types the backspace button (KeyEvent.VK_BACK_SPACE), they "free" up a character, and the count increments. Thus if they had 400 characters remaining, and they type backspace, the label now reads "401 characters remaining"
  • If the user highlights a set of characters and performs a bulk command on them (such as a backspace, or replacing the highlighted text with a single character), the correct # of chars remaining will be calculated correctly and the label will be updated. So if they have 50 chars remaining, and they highlight 5 letters and hit backspace, they now have "55 characters remaining"
  • 用户只允许在 textarea 中输入 500 个字符(最大)
  • 标签包含一条字符串消息,告诉用户他们还剩多少个字符(在每次击键或退格后)
  • 当组件初始化时,标签显示“最多 500 个字符!”
  • 对于键入的前 500 个字符,对于每次键入的键击(a - z、A - Z、0 - 9 和标点符号),标签显示“剩余 x 个字符”,其中x是它们到达前剩余的字符数最多 500
  • 输入第 500 个字符时,标签显示“剩余 0 个字符”,文本区域不能再输入任何字符
  • 如果用户键入退格按钮 ( KeyEvent.VK_BACK_SPACE),他们会“释放”一个字符,并且计数会增加。因此,如果他们剩余 400 个字符,并且他们键入退格,则标签现在显示为“剩余 401 个字符”
  • 如果用户突出显示一组字符并对它们执行批量命令(例如退格,或用单个字符替换突出显示的文本),将正确计算剩余的正确字符数并更新标签。因此,如果他们剩余 50 个字符,并且他们突出显示 5 个字母并按退格键,他们现在有“剩余 55 个字符”

I have 90% of this functionality working, but have a few bugs, and have no clue as to how to implement the last item above (bulk commands on highlighted text). Here's what I have:

我有 90% 的功能在工作,但有一些错误,并且不知道如何实现上面的最后一项(突出显示文本的批量命令)。这是我所拥有的:

boolean ignoreInput = false;
int charMax = 500;
JLabel charCntLabel = getLabel();
JTextArea myTextArea = getTextArea();

myTextArea.addKeyListener(new KeyListener() {
    @Override
    public void keyTyped(KeyEvent e) {
        return;
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // If we should be ignoring input then set make sure we
        // enforce max character count and remove the newly typed key.
        if(ignoreInput)
            myTextArea.setText(myTextArea.getText().substring(0,
                myTextArea.getText().length()));
    }

    @Override
    public void keyPressed(KeyEvent e) {
        String charsRemaining = " characters remaining";
        int newLen = 0;

        // The key has just been pressed so Swing hasn't updated
        // the text area with the new KeyEvent.
        int currLen = myTextArea.getText().length();

        // Adjust newLen depending on whether the user just pressed
        // the backspace key or not.
        if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
            newLen = currLen - 1;
            ignoreInput = false;
        }
        else
            newLen = currLen + 1;

        if(newLen < 0)
            newLen = 0;

        if(newLen == 0)
            charCntLabel.setText(charMax + " characters maximum!");
        else if(newLen >= 0 && newLen < charMax)
            charCntLabel.setText((charMax - newLen) + charsRemaining);
        else if(newLen >= charMax) {
            ignoreInput = true;
            charCntLabel.setText("0 " + charsRemaining);
        }
    }
});

The above code works pretty well, but has a few bugs:

上面的代码工作得很好,但有一些错误:

  • It doesn't prevent the user from typing in > 500 characters. When the user types in the 500th character, the label reads "0 characters remaining." But you can continue to type in characters after that, and the label stays the same.
  • If you have > 500 characters in the textarea, and you start backspacing, you'll see each character being removed from the textarea (the underlying model), but the label stays the same. But, once you backspace enough to get to the 500th character, and you backspace, the label will start changing properly, telling you that you have "1 characters remaining", "2 characters remaining", etc. So...
  • This code seems to work but just stops working > 500 characters. Once you get back inside that 500 char max, it begins working again.
  • 它不会阻止用户输入 > 500 个字符。当用户输入第 500 个字符时,标签显示“剩余 0 个字符”。但是您可以在此之后继续输入字符,并且标签保持不变。
  • 如果 textarea 中有 > 500 个字符,并且开始退格,您会看到每个字符都从 textarea(底层模型)中删除,但标签保持不变。但是,一旦您退格到第 500 个字符,然后退格,标签就会开始正确更改,告诉您“剩余 1 个字符”、“剩余 2 个字符”等。所以...
  • 此代码似乎有效,但只是停止工作 > 500 个字符。一旦您回到最大 500 个字符的范围内,它就会重新开始工作。

The questions

问题

  1. Is there a simpler way to implement this desired functionality (and for a Swing JTextArea)? I feel like I'm reinventing the wheel here and that there might be a "cleaner" way of enforcing character maximums and updating their respective labels.
  2. If not, can anybody spot my > 500 char bug? I've been looking at it all morning and am pulling my hair out.
  3. Most importantly, how do I implement my requirement to handle bulk commands to highlighted text? How do I hand text selections inside the textarea, listen for changes to the highlighted text (e.g., deleting multiple highlighted characters with the backspace button, etc.), and correctly calculate the new value for chars remaining?
  1. 有没有更简单的方法来实现这个所需的功能(以及 Swing JTextArea)?我觉得我在这里重新发明轮子,并且可能有一种“更干净”的方式来强制执行字符最大值并更新它们各自的标签。
  2. 如果没有,有人能发现我的 > 500 个字符的错误吗?我整个早上都在看着它,正在拔头发。
  3. 最重要的是,我如何实现我的要求来处理高亮文本的批量命令?如何在 textarea 内处理文本选择,监听突出显示文本的变化(例如,使用退格按钮删除多个突出显示的字符等),并正确计算剩余字符的新值?

Thanks in advance.

提前致谢。

回答by José Roberto Araújo Júnior

You can limit the max size by using a DocumentFilter, check this documentation section, it has a working exampleof what you need.

您可以通过使用DocumentFilter来限制最大大小,检查此文档部分,它有一个您需要的工作示例

Take this as an example, I used the component from the example file above:

以此为例,我使用了上面示例文件中的组件:

import java.awt.BorderLayout;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

import components.DocumentSizeFilter;

public class Test {

    public static void main(String[] args) {
        new TestFrame().setVisible(true);
    }

    private static class TestFrame extends JFrame{
        private JTextField textField;
        private DefaultStyledDocument doc;
        private JLabel remaningLabel = new JLabel();

        public TestFrame() {
            setLayout(new BorderLayout());

            textField = new JTextField();
            doc = new DefaultStyledDocument();
            doc.setDocumentFilter(new DocumentSizeFilter(500));
            doc.addDocumentListener(new DocumentListener(){
                @Override
                public void changedUpdate(DocumentEvent e) { updateCount();}
                @Override
                public void insertUpdate(DocumentEvent e) { updateCount();}
                @Override
                public void removeUpdate(DocumentEvent e) { updateCount();}
            });
            textField.setDocument(doc);

            updateCount();

            add(textField, BorderLayout.CENTER);
            add(remaningLabel, BorderLayout.SOUTH);

            setLocationRelativeTo(null);
            pack();
        }

        private void updateCount()
        {
            remaningLabel.setText((500 -doc.getLength()) + " characters remaining");
        }
    }
}

回答by Ray S. Kan

evt.consume(); will help alot in this case..you dont need to use DocumentFilter. here is a much easier way of limiting user at certain length

evt.consume(); 在这种情况下会很有帮助..你不需要使用DocumentFilter。这是将用户限制在特定长度的更简单的方法

private void jTextArea1KeyTyped(java.awt.event.KeyEvent evt) {                                    
    String s=jTextArea1.getText();
   int l=s.length();
   jTextField1.setText(String.valueOf(l));
   int i=10-l;
   jTextField2.setText(String.valueOf(i));
   try{
   if(l>=10){evt.consume();
   }
   }
   catch(Exception w){}

} 

回答by Forseth11

To add on to what Ray S. Kan said:

补充一下 Ray S. Kan 所说的:

There is an easier way to limit the characters for a JTextArea without having to use a DocumentFilter as shown by Ray S. Kan, but the issue with his answer is that it does not prevent someone from pasting in a long text. The following will prevent a user from pasting in stuff to bypass the limit:

有一种更简单的方法来限制 JTextArea 的字符,而不必使用文档过滤器,如 Ray S. Kan 所示,但他的答案的问题在于它不会阻止某人粘贴长文本。以下将阻止用户粘贴内容以绕过限制:

@Override
public void keyTyped(KeyEvent e) {
    int max = 25;
    if(text.getText().length() > max+1) {
        e.consume();
        String shortened = text.getText().substring(0, max);
        text.setText(shortened);
    }else if(text.getText().length() > max) {
        e.consume();
    }
}

This will stop a key from being pressed if the length is not pass max, but if it passes max, it will simply replace the string in the text area with a shorter string. The textvariable is the JTextArea swing object.

如果长度未超过 max,这将阻止按键被按下,但如果超过 max,它将简单地用较短的字符串替换文本区域中的字符串。该text变量是 JTextArea 摆动对象。