I'm working with Java and I have a JTextField which can only have 4 digits. I'm using a class that extends DocumentFilter to filter out any other character, and to limit the number of characters to 4.
The problem is that once I have 4 digits, if I select all of them and try to overwrite them by typing another digit it doesn't auto overwrite and does nothing, I have to explicitly type "Backspace" or "Delete" in my keyboard to remove the 4 digits and then (once the field is clear) I can type again.
How can I make the JTextField act as the rest of the operating system that once I have some text selected if I type a character it “removes all, then writes the character” (it substitutes the contents).
I have one aux class, JustLimitDigitFilter.java
:
import javax.swing.text.DocumentFilter;
import javax.swing.text.BadLocationException;
import java.awt.Toolkit;
import javax.swing.text.AttributeSet;
public class JustLimitDigitFilter extends DocumentFilter {
int limit;
public JustLimitDigitFilter(int limit) {
this.limit = limit;
}
@Override
public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
// if (text == null) {
// return;
// }
String str = text.replaceAll("\\D", "");
if (!str.isEmpty() && (fb.getDocument().getLength() + str.length()) <= limit) {
super.insertString(fb, offset, str, attr);
} else {
Toolkit.getDefaultToolkit().beep();
}
}
@Override
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attr)
throws BadLocationException {
// if (text == null) {
// return;
// }
String str = text.replaceAll("\\D", "");
if (!str.isEmpty() && (fb.getDocument().getLength() + str.length()) <= limit) {
super.replace(fb, offset, length, str, attr);
} else {
Toolkit.getDefaultToolkit().beep();
}
}
}
And the main class, App.java
:
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.JTextField;
import javax.swing.text.AbstractDocument;
import javax.swing.text.DocumentFilter;
public class App {
private JFrame frame;
private JTextField textField;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
App window = new App();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public App() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 191, 96);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
JPanel panel = new JPanel();
panel.setBounds(6, 6, 179, 62);
frame.getContentPane().add(panel);
panel.setLayout(null);
textField = new JTextField();
textField.setBounds(6, 6, 167, 26);
panel.add(textField);
textField.setColumns(10);
// without this code below this, the textfield is “normal” when
// something is selected if I write it overwrites the selection
AbstractDocument doc = (AbstractDocument) textField.getDocument();
doc.setDocumentFilter(new JustLimitDigitFilter(4));
}
}
Any suggestion is welcome since I'm new to this, apart from the doubt I explicitly have.
Method replace()
in class DocumentFilter
actually performs two operations. First it removes length
characters starting at offset
, after which it inserts text
at offset
. Hence the following line in your replace()
method causes nothing to happen when the JTextField
contains the maximum allowable number of characters...
if (!str.isEmpty() && (fb.getDocument().getLength() + str.length()) <= limit) {
If the JTextField
is full, then its length will be the maximum number of characters, so adding the length of str
will always be greater than limit
.