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.
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() {
}
}
Related
In the following example I have created two texFields. While writing on first text filed if the space or number typed by user then it should show message on another textField_1. But once user entered number/space it giving java.lang.NullPointerException.
public class NewDemo extends JFrame {
private JPanel p1;
private JTextField textField,textField_1;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
NewDemo frame = new NewDemo();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public NewDemo() {
setTitle("New Demo");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 500, 500);
p1 = new JPanel();
p1.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(p1);
p1.setLayout(null);
textField = new JTextField();
textField.setBounds(70, 71, 86, 20);
p1.add(textField);
textField.setColumns(10);
JLabel lblNewLabel = new JLabel("");
lblNewLabel.setBounds(70, 96, 86, 14);
p1.add(lblNewLabel);
textField_1 = new JTextField();
textField_1.setBounds(204, 71, 86, 20);
p1.add(textField_1);
textField_1.setColumns(10);
System.out.println("before calling");
((AbstractDocument) textField.getDocument()).setDocumentFilter(new MyDocumentFilter());
System.out.println("AfterCalling");
}
public JTextField getTextField_1() {
return textField_1;
}}
Here is the second class MyDocumentFilter where the java.lang.NullPointerException error occurring in the else block of replace method.
class MyDocumentFilter extends DocumentFilter {
private NewDemo n1;
#Override
public void replace(FilterBypass fb, int i, int i1, String string, AttributeSet as) throws BadLocationException {
System.out.println("Starting: replace Method");
for (int n = string.length(); n > 0; n--)
char c = string.charAt(n - 1);
System.out.println(c);
if (Character.isAlphabetic(c)) {
System.out.println("In if: replace method");
super.replace(fb, i, i1, String.valueOf(c), as);
} else {
System.out.println("Not allowed:BEFORE");
n1.getTextField_1().setText("not allowed");//***HERE IS THE ERROR
System.out.println("Not allowed:AFTER");
}
}
}
#Override
public void remove(FilterBypass fb, int i, int i1) throws BadLocationException {
System.out.println("In :remove method");
super.remove(fb, i, i1);
}
#Override
public void insertString(FilterBypass fb, int i, String string, AttributeSet as) throws BadLocationException {
System.out.println("In: insterString Method");
super.insertString(fb, i, string, as);
}}
Your DocumentFilter needs a reference to the displayed NewDemo GUI object. Don't just create a new NewDemo instance as another has suggested, since that will create a non-displayed separate instance, but rather pass in the appropriate displayed reference.
e.g.,
((AbstractDocument) textField.getDocument())
.setDocumentFilter(new MyDocumentFilter(this)); //!!
and
class MyDocumentFilter extends DocumentFilter {
private NewDemo n1;
public MyDocumentFilter(NewDemo n1) {
this.n1 = n1;
}
But yeah, also follow any recommendation from MadProgrammer as he knows his Swing and Java backwards and forwards. Also You should avoid use of null layout and use of setBounds(...) for component placement as this makes for very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain.
Edit using MadProgrammer's suggestion, consider allowing your GUI to add PropertyChangeListener to the DocumentFilter. This way, any class can listen for changes in the "state" of the DocumentFilter. Here I've created a boolean state called "valid" that notifies listeners any time the boolean variable changes.
For example:
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
import javax.swing.text.*;
public class NewDemo extends JPanel {
private static final long serialVersionUID = 1L;
private JTextField textField, textField_1;
private static void createAndShowGui() {
NewDemo mainPanel = new NewDemo();
JFrame frame = new JFrame("New Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
public NewDemo() {
setLayout(new GridLayout(1, 0, 5, 5));
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
textField = new JTextField();
textField.setColumns(10);
add(textField);
textField_1 = new JTextField();
textField_1.setColumns(10);
add(textField_1);
MyDocumentFilter docFilter = new MyDocumentFilter(); // !!
((AbstractDocument) textField.getDocument()).setDocumentFilter(docFilter); // !!
docFilter.addPropertyChangeListener(MyDocumentFilter.VALID,
new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
String text = ((Boolean) evt.getNewValue()) ? "Allowed"
: "Not Allowed";
textField_1.setText(text);
}
});
}
}
class MyDocumentFilter extends DocumentFilter {
public static final String VALID = "valid";
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(
this);
private boolean valid = true;
#Override
public void replace(FilterBypass fb, int i, int i1, String string,
AttributeSet as) throws BadLocationException {
for (int n = string.length(); n > 0; n--) {
char c = string.charAt(n - 1);
System.out.println(c);
if (Character.isAlphabetic(c)) {
super.replace(fb, i, i1, String.valueOf(c), as);
setValid(true);
} else {
setValid(false);
}
}
}
#Override
public void remove(FilterBypass fb, int i, int i1)
throws BadLocationException {
super.remove(fb, i, i1);
}
#Override
public void insertString(FilterBypass fb, int i, String string,
AttributeSet as) throws BadLocationException {
super.insertString(fb, i, string, as);
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
boolean oldValue = this.valid;
boolean newValue = valid;
this.valid = valid;
pcSupport.firePropertyChange(VALID, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener l) {
pcSupport.addPropertyChangeListener(propertyName, l);
}
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener l) {
pcSupport.removePropertyChangeListener(propertyName, l);
}
}
It is not the responsibility of the DocumentFilter to modify the state of the UI. Instead, it should provide notification that the state has errored (or what ever else you want to know) and let the delegate make decisions about what should be done.
public interface DocumentFilterListener {
public void documentFilterValidationFailed(DocumentFilter filter, String message);
}
public class MyDocumentFilter extends DocumentFilter {
private DocumentFilterListener filterListener;
public MyDocumentFilter(DocumentFilterListener filterListener) {
this.filteristener = filterListener;
}
#Override
public void replace(FilterBypass fb, int i, int i1, String string, AttributeSet as) throws BadLocationException {
System.out.println("Starting: replace Method");
for (int n = string.length(); n > 0; n--)
char c = string.charAt(n - 1);
System.out.println(c);
if (Character.isAlphabetic(c)) {
System.out.println("In if: replace method");
super.replace(fb, i, i1, String.valueOf(c), as);
} else if (filterListener != null) {
System.out.println("Not allowed:BEFORE");
filterListener.documentFilterValidationFailed(this, "not allowed");
System.out.println("Not allowed:AFTER");
}
}
}
#Override
public void remove(FilterBypass fb, int i, int i1) throws BadLocationException {
System.out.println("In :remove method");
super.remove(fb, i, i1);
}
#Override
public void insertString(FilterBypass fb, int i, String string, AttributeSet as) throws BadLocationException {
System.out.println("In: insterString Method");
super.insertString(fb, i, string, as);
}
}
Then you would simply implement the requirements of the interface
((AbstractDocument) textField.getDocument()).setDocumentFilter(
new MyDocumentFilter(new DocumentFilterListener() {
public void documentFilterValidationFailed(DocumentFilter filter, String message) {
getTextField_1().setText(message);
}
}));
As an example.
I use a JTextArea where using double click I can able to select the word at any place but I don't want to enable edit. Which means text can be entered only at the end of text area and not anywhere in between.
I have tried with mouse listeners like below:
#Override
public void mouseClicked(MouseEvent me) {
if(SwingUtilities.isLeftMouseButton(me)){
System.err.println("clicked");
int pos = textArea.getCaretPosition();
if(pos < textArea.getDocument().getLength()){
textArea.setCaretPosition(textArea.getDocument().getLength());
}
}
}
This makes double click not to select the word. I understand it is because caret position is moved to end. But how can I achieve this?
Check out the Protected Text Component which allows you to protect multiple areas of a Document from change.
Or if you don't need to be able to "select" any of the protected text than a simpler solution is to use a NavigationFilter:
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class NavigationFilterPrefixWithBackspace extends NavigationFilter
{
private int prefixLength;
private Action deletePrevious;
public NavigationFilterPrefixWithBackspace(int prefixLength, JTextComponent component)
{
this.prefixLength = prefixLength;
deletePrevious = component.getActionMap().get("delete-previous");
component.getActionMap().put("delete-previous", new BackspaceAction());
component.setCaretPosition(prefixLength);
}
#Override
public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
fb.setDot(Math.max(dot, prefixLength), bias);
}
#Override
public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
fb.moveDot(Math.max(dot, prefixLength), bias);
}
class BackspaceAction extends AbstractAction
{
#Override
public void actionPerformed(ActionEvent e)
{
JTextComponent component = (JTextComponent)e.getSource();
if (component.getCaretPosition() > prefixLength)
{
deletePrevious.actionPerformed( null );
}
}
}
private static void createAndShowUI()
{
JTextField textField = new JTextField("Prefix_", 20);
textField.setNavigationFilter( new NavigationFilterPrefixWithBackspace(7, textField) );
JFrame frame = new JFrame("Navigation Filter Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(textField);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Okay, this is slightly hacky...
Basically what this does is installs a "protected" DocumentFilter, which will only allow input to put in from a certain point in the Document.
It overrides the JTextArea's insert-break key binding (Enter) and records a marker. The "protected" DocumentFilter then ensures that the content does not precede this point
I was forced to implement a KeyListener on the field to move the cursor to the end of the input, while the DocumentFilter was capable of handling this, it did present some issues with deleting and general usability. This also ensures that the selection is un-highlighted when new content is added...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
public class Terminal {
public static void main(String[] args) {
new Terminal();
}
public Terminal() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTextArea terminal = new JTextArea(20, 40);
ProtectedDocumentFilter.install(terminal);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(terminal));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static interface UserInput {
public int getUserInputStart();
public void setUserInputStart(int lastPoint);
}
public static class DefaultUserInput implements UserInput {
private final JTextArea textArea;
private int userInputStart;
public DefaultUserInput(JTextArea ta) {
textArea = ta;
ActionMap am = ta.getActionMap();
Action action = am.get("insert-break");
am.put("insert-break", new ProxyAction(action));
ta.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (textArea.getCaretPosition() != textArea.getDocument().getLength()) {
textArea.setCaretPosition(textArea.getDocument().getLength());
}
}
});
}
#Override
public void setUserInputStart(int userInputStart) {
this.userInputStart = userInputStart;
}
#Override
public int getUserInputStart() {
return userInputStart;
}
public class ProxyAction extends AbstractAction {
private final Action proxy;
public ProxyAction(Action proxy) {
this.proxy = proxy;
}
#Override
public void actionPerformed(ActionEvent e) {
proxy.actionPerformed(e);
int range = textArea.getCaretPosition() - userInputStart;
userInputStart += range;
}
}
}
public static class ProtectedDocumentFilter extends DocumentFilter {
protected static void install(JTextArea textArea) {
UserInput ui = new DefaultUserInput(textArea);
((AbstractDocument) textArea.getDocument()).setDocumentFilter(new ProtectedDocumentFilter(ui));
}
private UserInput userInput;
public ProtectedDocumentFilter(UserInput userInput) {
this.userInput = userInput;
}
public UserInput getUserInput() {
return userInput;
}
#Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
if (offset < getUserInput().getUserInputStart()) {
offset = fb.getDocument().getLength();
}
System.out.println("Insert");
super.insertString(fb, offset, string, attr);
}
#Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
if (offset >= getUserInput().getUserInputStart()) {
super.remove(fb, offset, length);
}
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
if (offset < getUserInput().getUserInputStart()) {
offset = fb.getDocument().getLength();
length = 0;
}
super.replace(fb, offset, length, text, attrs);
if (text.contains("\n")) {
int lastPoint = offset + text.lastIndexOf("\n");
if (lastPoint > getUserInput().getUserInputStart()) {
getUserInput().setUserInputStart(lastPoint + 1);
}
}
}
}
}
This is just an example, you're going to need to play with it and tweak it to meet your own needs....
This question already has answers here:
Is there any way to accept only numeric values in a JTextField?
(20 answers)
Closed 9 years ago.
I have a JFormattedTextField where the user would input prices, I have this, but if I type a character, it'll let me anyway. I need this text field to only read numbers or , from the keyboard, and ignore if it's a char. How should I change it in order to make it work?
JFormattedTextField formattedTextField = new JFormattedTextField();
formattedTextField.setBounds(25, 330, 56, 20);
contentPanel.add(formattedTextField);
formattedTextField.setValue(new Double(10.0));
You need to set a Formatter:
NumberFormat f = NumberFormat.getNumberInstance();
JFormattedTextField field = new JFormattedTextField(f);
Take a look: Format
and
NumberFormat
Then try this:
PlainDocument doc = new PlainDocument();
doc.setDocumentFilter(new DocumentFilter() {
#Override
public void insertString(FilterBypass fb, int off, String str, AttributeSet attr)
throws BadLocationException
{
fb.insertString(off, str.replaceAll("\\D++", ""), attr); // remove non-digits
}
#Override
public void replace(FilterBypass fb, int off, int len, String str, AttributeSet attr)
throws BadLocationException
{
fb.replace(off, len, str.replaceAll("\\D++", ""), attr); // remove non-digits
}
});
JFormattedTextField field = new JFormattedTextField();
field.setDocument(doc);
A JFormattedTextField can be used for many things, it can be also used to filter dates or phone numbers. You will either need to set a NumberFormater to the TextField or you use the DocumentFilter (works with JTextField only too).
Check this code snippet, that's how you allow only digits in JTextField, by using DocumentFilter, as the most effeciive way :
import java.awt.*;
import javax.swing.*;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.DocumentFilter.FilterBypass;
public class InputInteger
{
private JTextField tField;
private MyDocumentFilter documentFilter;
private void displayGUI()
{
JFrame frame = new JFrame("Input Integer Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel contentPane = new JPanel();
contentPane.setBorder(
BorderFactory.createEmptyBorder(5, 5, 5, 5));
tField = new JTextField(10);
((AbstractDocument)tField.getDocument()).setDocumentFilter(
new MyDocumentFilter());
contentPane.add(tField);
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args)
{
Runnable runnable = new Runnable()
{
#Override
public void run()
{
new InputInteger().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
class MyDocumentFilter extends DocumentFilter
{
#Override
public void insertString(DocumentFilter.FilterBypass fp
, int offset, String string, AttributeSet aset)
throws BadLocationException
{
int len = string.length();
boolean isValidInteger = true;
for (int i = 0; i < len; i++)
{
if (!Character.isDigit(string.charAt(i)))
{
isValidInteger = false;
break;
}
}
if (isValidInteger)
super.insertString(fp, offset, string, aset);
else
Toolkit.getDefaultToolkit().beep();
}
#Override
public void replace(DocumentFilter.FilterBypass fp, int offset
, int length, String string, AttributeSet aset)
throws BadLocationException
{
int len = string.length();
boolean isValidInteger = true;
for (int i = 0; i < len; i++)
{
if (!Character.isDigit(string.charAt(i)))
{
isValidInteger = false;
break;
}
}
if (isValidInteger)
super.replace(fp, offset, length, string, aset);
else
Toolkit.getDefaultToolkit().beep();
}
}
Please have a look at the following code.
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
public class Bean extends JFrame
{
private JTextField field1, field2, field3, field4;
private JLabel text;
private JButton ok, cancel;
private JPanel centerPanel,southPanel, textPanel;
private GridLayout grid;
private FlowLayout flow1, flow2;
public Bean()
{
//Declaring instance Variables
field1 = new JTextField(10);
field2 = new JTextField(5);
field3 = new JTextField(5);
field4 = new JTextField(5);
text = new JLabel("Insert Your Numbers Here");
AbstractDocument d = (AbstractDocument) field1.getDocument();
d.setDocumentFilter(new Bean.Field1Listener());
ok = new JButton("OK");
cancel = new JButton("Cancel");
/***********************Creating the main view*************************/
centerPanel = new JPanel();
grid = new GridLayout(2,1,1,1);
//Adding TextFields
textPanel = new JPanel();
flow1 = new FlowLayout(FlowLayout.CENTER);
textPanel.setLayout(flow1);
textPanel.add(field1);
//Adding Buttons
southPanel = new JPanel();
southPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
southPanel.add(ok);
southPanel.add(cancel);
//Creating Center View
centerPanel.setLayout(grid);
centerPanel.add(text);
centerPanel.add(textPanel);
//Gathering everything together
getContentPane().add(centerPanel,"Center");
getContentPane().add(southPanel,"South");
this.setSize(500,200);
this.validate();
this.setVisible(true);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private class Field1Listener extends DocumentFilter
{
#Override
public void insertString(DocumentFilter.FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException
{
if(fb.getDocument().getLength()+string.length()>5)
{
return;
}
fb.insertString(offset, string, attr);
}
#Override
public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException
{
fb.insertString(offset, "", null);
}
#Override
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attrs)throws BadLocationException
{
if(fb.getDocument().getLength()+text.length()>5)
{
System.out.println("OK");
return;
}
fb.insertString(offset, text, attrs);
}
}
public static void main(String[]args)
{
new Bean();
}
}
In here, I am trying to limit the number of character to 5. OK, it stops inserting anymore characters when it reaches 5, but the case is, it also doesn't allow to delete the inserted characters, replace, or anything. How to correct this issue?
simply change your current remove method:
#Override
public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException
{
fb.insertString(offset, "", null);
}
for this one:
#Override
public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException
{
fb.remove(offset, length);
}
it should now work.
You should make your own class that checks whether you gave more input than the maximum allowed length: See an example on http://www.java2s.com/Tutorial/Java/0240__Swing/LimitJTextFieldinputtoamaximumlength.htm.
I'm somewhat used to the GUI idiom where when I type something wrong in a text input field a balloon pops up from the field with info about what I got wrong / expected input. It remains visible until I type again.
But now I couldn't find any example to point to.
Given a JTextfield do you know of any library/code that would display such a balloon when triggered by my keylistener?
#see http://www.javapractices.com/topic/TopicAction.do?Id=151
Balloon Tip is a library that does that:
balloonTip = new BalloonTip(f, "Tooltip msg");
That was all needed! If you insist on a having it be a tooltip too:
tooltipBalloon = new BalloonTip(someComponent, "I'm a balloon tooltip!");
// Now convert this balloon tip to a tooltip, such that the tooltip shows up after 500 milliseconds and stays visible for 3000 milliseconds
ToolTipUtils.balloonToToolTip(tooltipBalloon, 500, 3000);
The link given by James Poulson probably provides a better solution, but I had to see if this were possible with some simple Java code using a DocumentFilter and a JWindow. Here's one possible way to do this:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Window;
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
public class InfoBalloon extends JPanel {
private static final int PREF_WIDTH = 400;
private static final int PREF_HEIGHT = 300;
private static final String REGEX_TEST = "\\d*";
private static final String ERROR_TEXT = "Please only add numbers to the text field";
private JTextField textField = new JTextField(10);
private JWindow errorWindow;
public InfoBalloon() {
add(new JLabel("Please Enter Number"));
add(textField);
((PlainDocument)textField.getDocument()).setDocumentFilter(new MyNumberDocFilter());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_WIDTH, PREF_HEIGHT);
}
private void showErrorWin() {
if (errorWindow == null) {
JLabel errorLabel = new JLabel(ERROR_TEXT);
Window topLevelWin = SwingUtilities.getWindowAncestor(this);
errorWindow = new JWindow(topLevelWin);
JPanel contentPane = (JPanel) errorWindow.getContentPane();
contentPane.add(errorLabel);
contentPane.setBackground(Color.white);
contentPane.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
errorWindow.pack();
}
Point loc = textField.getLocationOnScreen();
errorWindow.setLocation(loc.x + 20, loc.y + 30);
errorWindow.setVisible(true);
}
private boolean textOK(String text) {
if (text.matches(REGEX_TEST)) {
return true;
}
return false;
}
private class MyNumberDocFilter extends DocumentFilter {
#Override
public void insertString(FilterBypass fb, int offset, String string,
AttributeSet attr) throws BadLocationException {
if (textOK(string)) {
super.insertString(fb, offset, string, attr);
if (errorWindow != null && errorWindow.isVisible()) {
errorWindow.setVisible(false);
}
} else {
showErrorWin();
}
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text,
AttributeSet attrs) throws BadLocationException {
if (textOK(text)) {
super.replace(fb, offset, length, text, attrs);
if (errorWindow != null && errorWindow.isVisible()) {
errorWindow.setVisible(false);
}
} else {
showErrorWin();
}
}
#Override
public void remove(FilterBypass fb, int offset, int length)
throws BadLocationException {
super.remove(fb, offset, length);
if (errorWindow != null && errorWindow.isVisible()) {
errorWindow.setVisible(false);
}
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("Info Balloon");
frame.getContentPane().add(new InfoBalloon());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Constructive or destructive criticism is most welcome!