Enforce max characters on Swing JTextArea with a few curve balls - java

I'm trying to add functionality to a Swing JLabel and JTextArea such that:
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 x is 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"
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:
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.
The questions
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.
If not, can anybody spot my > 500 char bug? I've been looking at it all morning and am pulling my hair out.
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?
Thanks in advance.

You can limit the max size by using a DocumentFilter, check this documentation section, it has a working example of what you need.
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");
}
}
}

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

To add on to what Ray S. Kan said:
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:
#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 text variable is the JTextArea swing object.

Related

jbuttons and a jtextfield that has an action listener

I have a keypad made up of jbuttons and a jtextfield that has an action listener. When I press the button the number shows in the textfield but the next number overwrites it. Could anyone tell me how to append the text to the length 13 numbers and when it get's there to carriage return.
If I use the keyboard to enter numbers I can enter a String of numbers but not from the buttons.
I am using:
JButton buttonNo2 = new JButton("2");
buttonNo2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textfield.setText("2");
}
buttonNo1.setBounds(11, 18, 50, 50);
keyboardPanel.add(buttonNo2);
buttonNo1.setForeground(Color.BLUE);
buttonNo1.setFont(new Font("Perpetua", Font.BOLD, 20));
Try using something like
textfield.setText(textfield.getText() + "2");
instead of textfield.setText("2");
setText does just that, sets the text of the text field to the value you specified.
Also buttonNo1.setBounds(11, 18, 50, 50); looks like you're trying to do without a layout manager. Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
You could also make it much simpler for yourself, and use the Action API instead, which would save you a not lot of repeated typing...
public class NumberAction extends AbstractAction {
private JTextField field;
private int number;
public NumberAction(JTextField field, int number) {
this.field = field;
this.number = number;
putValue(NAME, Integer.toString(number));
}
#Override
public void actionPerformed(ActionEvent e) {
Document doc = field.getDocument();
try {
doc.insertString(doc.getLength(), Integer.toString(number), null);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
Then you would just need to add each button as required...
add(new JButton(new NumberAction(textfield, 1)));
add(new JButton(new NumberAction(textfield, 2)));
add(new JButton(new NumberAction(textfield, 3)));
See How to Use Actions for more details
You have to get the text first and set the text by appending the previous and current text.
public void actionPerformed(ActionEvent e) {
String str = textfield.getText();
textfield.setText(str+the number printed);
}
As #MadProgrammer already posted How to achieve it with JTextField ,
You can opt for JTextArea#append method
public void append(String str)
Appends the given text to the end of the document. Does nothing if the
model is null or the string is null or empty.
Parameters:
str - the text to insert

JTextArea updating after KeyListener? [duplicate]

First off I making a simple typing program. If you type the a letter and hit the spacebar the TTextArea will be matched with the label text to see if it matches. But it keeps coming out wrong cause there is space added before the letter after the first output everytime and I do not understand why? Is this something that just happens or can ypu
The is my code
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_SPACE){
input = textArea.getText();
if(input.length() <= 0){
JOptionPane.showMessageDialog(null, "Type something first");
} else {
input.trim();
System.out.println(input);
gameLogic.score(input, letterLabel.getText());
gameLogic.error(input, letterLabel.getText());
scoreLabel.setText("Score: " + String.valueOf(gameLogic.score));
errorLabel.setText("Errors: " + String.valueOf(gameLogic.error));
gameLogic.changeDifficulty();
letterLabel.setText( gameLogic.changeText());
textArea.setText("");
textArea.setCaretPosition(0);
}
}
this is my output
l
l
x
k
ss
Several things...
KeyListener is not a recommend way to deal with monitoring or effecting the changes to any text component. If you are lucky enough that the key stroke isn't consumed by the component, the component has already being updated with the last key stroke.
A better approach would be to use a DocumentListener if you just wanted to monitor changes to the text component or a DocumentFilter if you want to change what is being passed to the field.
Check out Using Text Components for more details

How to collect only one value from a JToggleButton when a mouse moves over the container?

I am working on doing a word finder puzzle game. When I am trying to get to happen is a user clicks on a letter then moves his mouse across other letters to create a word. I am having some problems with the listeners. I have been going back and fourth using mouseDragged and mouseMoved. So far mouseMoved seems to work better because it dynamically grabs values. The problem is I can't figure out how to get it only grab one value. In an ideal world it would move of a Button or label grab that value once and ignore the value till it reaches a new button or label. Currently it just grabs values at every instant a mouse is on that container. The logic for my Mouse method is below:
public void mouseMoved(MouseEvent e) {
int count = countClicked;
int num = 0;
for(JToggleButton row : puzzleGrid){
if(e.getComponent() == row && count == 1) {
if(num == 0){
num++;
for(JLabel l: solWords)
{
sb.append(row.getText());
System.out.println(l.getText()+" = "+ sb.toString());
if(l.getText().contentEquals(row.getText()))
System.out.println(row.getText());
}
}
}
}
}
I am using the value gathered from the containers to check against an array of JLabels containing the solution values.
You could store the last letter in a static variable:
static String lastLetter = null;
mouseMoved(...) {
if(row.getText().equals(lastLetter)) {
continue;
}
lastLetter = row.getText();
}

Java: How cursor automatically move from one TextField to other

In my application four TextArea is there and I want to enter only four character in one Text area and cursor automatically move to next TestArea. Again when I enter four character in this TextArea then again cursor automatically move to next TextArea.
Example: At the time of installing Window XP it want "Key" and there are four section when you enter four character in first section then cursor automatically move to the next section.
Same thing I want in my application.
For this first of all I add CustomizedTextFields.jar and then created four IntegerField:
private IntegerField text1;
private IntegerField text2;
private IntegerField text3;
private IntegerField text4;
after this I show all these IntegerField on my frame.
Now I tried this code to send cursor to the next field but it's not working:
text1.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
int a2 = text1.getText().length();
if (a2 == 3) {
text2.getCursor();
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
});
interesting enough question to try improving my shadowy knowledge of the text package :-)
There are two separate requirements here
restrict the lenght of the text: that's done with a DocumentFilter as #mKorbel already noted
automatically transferFocus to the next component after the max length is reached: turns out that can be done with a NavigationFilter
in code:
JComponent panel = new JPanel();
final int maxSize = 3;
for (int i = 0; i < 4; i++) {
final JTextField field = new JTextField(5);
NavigationFilter filter = new NavigationFilter() {
#Override
public void setDot(FilterBypass fb, int dot, Bias bias) {
if (dot >= maxSize) {
fb.setDot(0, bias);
field.transferFocus();
return;
}
fb.setDot(dot, bias);
}
#Override
public void moveDot(FilterBypass fb, int dot, Bias bias) {
if (dot >= maxSize) {
fb.setDot(0, bias);
field.transferFocus();
return;
}
fb.moveDot(dot, bias);
}
};
field.setNavigationFilter(filter);
((AbstractDocument) field.getDocument()).setDocumentFilter(new DocumentSizeFilter(maxSize));
panel.add(field);
}
The documentFilter is the one from the Swing Tutorial
At the time of installing Window XP it want "Key" and there are four section
when you enter four character in first section then cursor automatically move
to the next section.
add DocumentListener to the JTextComponents, for listening add DocumentFilter
don't use KeyListener for JTextComponents, use only DocumentListener
add required next JTextArea to the DocumentListener, if is there typed 4th. Char into JTextArea,
notice, moving with Focus from one JTextArea to another would be better wrapped into invokeLater
Replace text2.getCursor() with text2.requestFocus().
getCursor() is for retrieving the shape of the mouse pointer when hovering over a component.
Also, with this method it is still possible to enter more than 4 chars in a field, for example by pasting from clipboard. If you want to block that, you would need to check if text entered is longer than 4 chars, and if so, take only first 4 chars from it.
Something like this should work:
text1.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
String value=text1.getText();
if(value.length()==4){
text2.requestFocus();
}
}
Where text2 is your next textfield
simply just create textarea and go to key typed events
den u may write this
String number=jTextArea1.getText();
int l=number.length();
if(l==3){
jTextArea1.transferFocus();
}

How to intercept keyboard strokes going to Java Swing JTextField?

The JTextField is a calculator display initialized to zero and it is bad form to display a decimal number with a leading 0 like 0123 or 00123. The numeric buttons (0..9) in a NetBeans Swing JFrame use append() [below] to drop the leading zeros, but the user may prefer the keyboard to a mouse, and non-numeric characters also need to be handled.
private void append(String s) {
if (newEntry) {
newEntry = false;
calcDisplay.setText(s);
} else if (0 != Float.parseFloat(calcDisplay.getText().toString())) {
calcDisplay.setText(calcDisplay.getText().toString() + s);
}
}
You could restrict the characters input to the JTextField by adding a custom KeyListener. Here is a quick example to demonstrate the idea:
myTextField.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
char c = e.getKeyChar();
if (!Character.isDigit(c)) {
e.consume(); // Stop the event from propagating.
}
}
});
Of course, you need to consider special keys like Delete and combinations like CTRL-C, so your KeyListener should be more sophisticated. There are probably even freely available utilities to do most of the grunt work for you.
You can do this with DocumentFilter.
(Edit: This answer originally included a ling to a simple complete example program on my now defunct weblog.)

Categories

Resources