Обеспечьте максимальное количество символов в Swing JTextArea с помощью нескольких кривых шариков

Я пытаюсь добавить функциональность в Swing JLabel и JTextArea таким образом, чтобы:

  • Пользователю разрешено вводить только 500 символов в текстовое поле (макс.)
  • Метка содержит строковое сообщение, сообщающее пользователю, сколько символов у него осталось (после каждого нажатия клавиши или возврата)
  • Когда компоненты инициализируются, на этикетке написано «максимум 500 символов!»
  • Для первых 500 набранных символов, для каждого нажатия клавиши (a - z, A - Z, 0 - 9 и знаков препинания) метка читается как «осталось x символов», где x - количество оставшихся символов до того, как они достигнут максимум 500
  • Когда вводится 500-й символ, на этикетке отображается «осталось 0 символов», и никакие другие символы не могут быть введены в текстовую область.
  • Если пользователь нажимает кнопку возврата (KeyEvent.VK_BACK_SPACE), он «освобождает» символ, и счетчик увеличивается. Таким образом, если у них осталось 400 символов, и они набирают backspace, метка теперь читается как «осталось 401 символ».
  • Если пользователь выделяет набор символов и выполняет над ними групповую команду (например, возврат или замену выделенного текста одним символом), правильное количество оставшихся символов будет вычислено правильно, и метка будет обновлена. Таким образом, если у них осталось 50 символов, и они выделяют 5 букв и нажимают клавишу Backspace, теперь у них остается «55 символов».

У меня работает 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);
        }
    }
});

Приведенный выше код работает довольно хорошо, но имеет несколько ошибок:

  • Это не мешает пользователю вводить> 500 символов. Когда пользователь вводит 500-й символ, на этикетке отображается «Осталось 0 символов». Но после этого вы можете продолжать вводить символы, и метка останется прежней.
  • Если у вас есть> 500 символов в текстовом поле и вы начинаете делать обратный интервал, вы увидите, что каждый символ удаляется из текстового поля (базовой модели), но метка остается той же. Но после того, как вы вернетесь назад, чтобы перейти к 500-му символу, и вы вернетесь, метка начнет правильно меняться, сообщая вам, что у вас «осталось 1 символ», «осталось 2 символа» и т. д. Так...
  • Этот код вроде работает, но просто перестает работать> 500 символов. Как только вы вернетесь внутрь этих 500 символов, он снова начнет работать.

Вопросы

  1. Есть ли более простой способ реализовать эту желаемую функциональность (и для Swing JTextArea)? Мне кажется, что я изобретаю здесь колесо заново, и что может быть более «чистый» способ применения максимальных значений символов и обновления их соответствующих меток.
  2. Если нет, может ли кто-нибудь обнаружить мою ошибку> 500 символов? Я смотрела на это все утро и выдергивала волосы.
  3. Самое главное, как мне реализовать свое требование обрабатывать массовые команды для выделенного текста? Как вручную выделить текст внутри текстового поля, прослушать изменения выделенного текста (например, удалить несколько выделенных символов с помощью кнопки возврата и т. Д.) И правильно вычислить новое значение для оставшихся символов?

Заранее спасибо.


person IAmYourFaja    schedule 13.12.2012    source источник
comment
Вы усложняете себе задачу. Не считайте набранные символы, просто проверьте, сколько символов в JTextArea, используя (например) getText (). Ограничение суммы, которую можно ввести, - это другая проблема, которую можно решить, предоставив свой собственный документ (модель) в JTextArea.   -  person Durandal    schedule 13.12.2012


Ответы (3)


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

Возьмем это в качестве примера, я использовал компонент из файла примера выше:

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");
        }
    }
}
person José Roberto Araújo Júnior    schedule 13.12.2012
comment
@HeineyBehinds, возможно, использовать JOptionPane в случае, если Document поднял максимальное количество символов, - person mKorbel; 13.12.2012
comment
Спасибо @Jose (+1) - отличное предложение, однако я не вижу, где я могу найти DocumentSizeFilter по любой из этих ссылок. Вы знаете, какую библиотеку мне нужно добавить? Спасибо еще раз! - person IAmYourFaja; 13.12.2012
comment
@HeineyBehinds Это ссылка на рабочий пример: docs.oracle.com/javase/tutorial/uiswing/examples/components/ - person José Roberto Araújo Júnior; 13.12.2012
comment
Почему нет более прямого пути, странно, нет метода для этого? - person shareef; 08.05.2015

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){}

} 
person Ray S. Kan    schedule 02.10.2016

Чтобы добавить к тому, что сказал Рэй С. Кан:

Существует более простой способ ограничить символы для JTextArea без использования DocumentFilter, как показано Рэем С. Каном, но проблема с его ответом заключается в том, что он не мешает кому-либо вставлять длинный текст. Следующее предотвратит вставку пользователем чего-либо, чтобы обойти ограничение:

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

Это остановит нажатие клавиши, если длина не превышает max, но если она проходит max, она просто заменит строку в текстовой области более короткой строкой. Переменная text - это объект качания JTextArea.

person Forseth11    schedule 04.12.2017
comment
Похоже на эту строку: else if (text.getText (). Length () ›max) следует читать: else if (text.getText (). Length ()› = max) - person Vitaly Sazanovich; 08.08.2019