Make JSpinner only read numbers but also detect backspace
java, jformattedtextfield, jspinner, swing
Solution
you are right `JFormattedTextField` isn't correctly implemented to `JSpinner`, you have implements `DocumentFilter` for filtering of un_wanted `Chars` typed from keyboad or pasted from ClipBoard, (thanks to @StanislavL)
you have solve by yourself issues with `selectAll()` on `focusGained()` wrapped into `invokeLater()`,
example
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.text.*;
public class TestDigitsOnlySpinner {
public static void main(String... args) {
SwingUtilities.invokeLater((Runnable) new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("enter digit");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JSpinner jspinner = makeDigitsOnlySpinnerUsingDocumentFilter();
frame.getContentPane().add(jspinner, BorderLayout.CENTER);
frame.getContentPane().add(new JButton("just another widget"), BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
private JSpinner makeDigitsOnlySpinner_BasicAttempt() {
JSpinner spinner = new JSpinner(new SpinnerNumberModel());
return spinner;
}
private JSpinner makeDigitsOnlySpinnerUsingDocumentFilter() {
JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, 20, 1));
JSpinner.NumberEditor jsEditor = (JSpinner.NumberEditor) spinner.getEditor();
final Document jsDoc = jsEditor.getTextField().getDocument();
if (jsDoc instanceof PlainDocument) {
AbstractDocument doc = new PlainDocument() {
private static final long serialVersionUID = 1L;
@Override
public void setDocumentFilter(DocumentFilter filter) {
if (filter instanceof MyDocumentFilter) {
super.setDocumentFilter(filter);
}
}
};
doc.setDocumentFilter(new MyDocumentFilter());
jsEditor.getTextField().setDocument(doc);
}
return spinner;
}
});
}
private static class MyDocumentFilter extends DocumentFilter {
@Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
if (stringContainsOnlyDigits(string)) {
super.insertString(fb, offset, string, attr);
}
}
@Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
super.remove(fb, offset, length);
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
if (stringContainsOnlyDigits(text)) {
super.replace(fb, offset, length, text, attrs);
}
}
private boolean stringContainsOnlyDigits(String text) {
for (int i = 0; i < text.length(); i++) {
if (!Character.isDigit(text.charAt(i))) {
return false;
}
}
return true;
}
}
private TestDigitsOnlySpinner() {
}
}
Problem
I'm trying to make a JSpinner that will only accepts numbers but I also want it to read/respond to backspace. ``` public class test { JFrame frame; JPanel panel; JSpinner spinner; public test() { frame = new JFrame("test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setPreferredSize(new Dimension(200,200)); panel = new JPanel(); SpinnerNumberModel numSpin = new SpinnerNumberModel(10, 0,1000,1); spinner = new JSpinner(numSpin); JFormattedTextField txt = ((JSpinner.NumberEditor) spinner.getEditor()).getTextField(); ((NumberFormatter) txt.getFormatter()).setAllowsInvalid(false); panel.add(spinner); frame.setContentPane(panel); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { test test = new test(); } } ``` The code above works to make only numbers but this doesn't allow me to backspace. I found some examples on this site but they were written for C.