Restrict Input of JTextField to Double Numbers? - java

In java , i am trying to make simple currency converter, but for that i need a text field which can restrict input to numbers only and more importantly double numbers. I tried using JFormatedTextField but it only format the input after you have done your input and click elsewhere but i need to restrict TextField to consume() each invalid character while doing input.
Possible Attempts:
Using JFormatedTextField:
JFormatedTextField textField = new JFormatedTextField(new DoubleFormat());
textField.setBounds(190, 49, 146, 33);
frame.getContentPane().add(textField);
textField.setColumns(10);
Using KeyTyped Event:
char c = arg0.getKeyChar();
if(!(Character.isDigit(c) || c == KeyEvent.VK_BACK_SPACE || c== KeyEvent.VK_DELETE)){
arg0.consume();
}
Using KeyTyped Event with regex:
if(!((textField.getText().toString+arg0.getKeyChar()).matches("[0-9]*(.[0-9]*)?"))){
arg0.consume();
}
Second and third attempt were close but then second attempt failed on point values and third attempt always read first character on textField no matter what it is, So any suggestions ? i am not very fond of JAVA GUI so kindly be patient.

If you know how many places before and after decimal point you want, you can also use MaskFormatter. For example:
JFormattedTextField field = new JFormattedTextField(getMaskFormatter("######.##"));
(...)
private MaskFormatter getMaskFormatter(String format) {
MaskFormatter mask = null;
try {
mask = new MaskFormatter(format);
mask.setPlaceholderCharacter('0');
}catch (ParseException ex) {
ex.printStackTrace();
}
return mask;
}
However it will chenge a look of JTextField, so it will be always visible 000000.00 in it.
EDIT
Another way, not too elegant, but in my opinion working. Try with DecumentListener, maybe it will suit your needs:
field = new JFormattedTextField();
field.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
Runnable format = new Runnable() {
#Override
public void run() {
String text = field.getText();
if(!text.matches("\\d*(\\.\\d{0,2})?")){
field.setText(text.substring(0,text.length()-1));
}
}
};
SwingUtilities.invokeLater(format);
}
#Override
public void removeUpdate(DocumentEvent e) {
}
#Override
public void changedUpdate(DocumentEvent e) {
}
});
I used regex: \\d*(\\.\\d{0,2})? because two decimal places is enough for currency.

You would need to use a DocumentFilter. Read the section from the Swing tutorial on Implementing a DocumentFilter for an example to get you started.
Your implementation will be more complex because you will need to take the text already in the Document and then insert the new text in the appropriate location in the String and then invoke Double.parseDouble(...) on the String to make sure it is a valid double value.
If the validation succeeds then you continue with the insertion otherwise you can generate beep.

You can add a key listener to the text field and implement the keyReleased() method to determine if they value in the text field is a double after every key stroke by the user.
public class CurrencyJTF extends JFrame {
JButton jButton = new JButton("Unfocus");
final JFormattedTextField textField = new JFormattedTextField(new DecimalFormat());
double lastDouble = 0.0;
public CurrencyJTF() throws HeadlessException {
textField.setColumns(20);
textField.setText(lastDouble + "");
this.setLayout(new FlowLayout());
this.add(textField);
this.add(jButton);
textField.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
handleKeyReleased();
}
});
}
private void handleKeyReleased() {
String text = textField.getText();
if (text.isEmpty()) return;
try {
lastDouble = Double.parseDouble(text);
} catch (NumberFormatException ex) {
textField.setText(lastDouble + ""); // or set to other values you want
}
}
public static void main(String[] args) {
JFrame frame = new CurrencyJTF();
frame.setVisible(true);
frame.pack();
}
}

You can write your own KeyListener something like that:
public class DoubleNumbersKeyListener implements KeyListener {
final HashSet<Character> valid_keys = new HashSet<>();
final ArrayList<Character> sequence = new ArrayList<>();
public DoubleNumbersKeyListener() {
valid_keys.add('.');
valid_keys.add('0');
valid_keys.add('1');
valid_keys.add('2');
valid_keys.add('3');
valid_keys.add('4');
valid_keys.add('5');
valid_keys.add('6');
valid_keys.add('7');
valid_keys.add('8');
valid_keys.add('9');
valid_keys.add((char) KeyEvent.VK_BACK_SPACE);
valid_keys.add((char) KeyEvent.VK_DELETE);
}
#Override
public void keyTyped(KeyEvent event) {
char c = event.getKeyChar();
if (!valid_keys.contains(c)) {
event.consume();
} else {
if (c == KeyEvent.VK_DELETE || c == KeyEvent.VK_BACK_SPACE) {
if (!sequence.isEmpty()) {
char last = sequence.remove(sequence.size() - 1);
if (last == '.') {
valid_keys.add(last);
}
}
} else {
sequence.add(c);
if (c == '.') {
valid_keys.remove(c);
}
}
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
}

Related

Java System.in not working with custom stream

I am currently designing a chat application in Java. Therefore I created my own JFrame. All contents written to System.out are written into a JTextArea and I want to redirect System.in to use my JTextField. I wrote a simple class what should handle this:
public class InputField extends InputStream implements KeyListener {
private JTextField textField;
private String text = null;
public InputField(JTextField textField){
this.textField = textField;
textField.addKeyListener(this);
}
#Override
public int read() throws IOException {
//System.out.println("CALLED!");
while (text == null)
;
int b;
if (Objects.equals(text, "")) {
b = -1;
text = null;
} else {
b = text.charAt(0);
text = text.substring(1, text.length());
}
// System.out.println("GIVING: " + b);
return b;
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
text = textField.getText();
System.out.println("ENTER: "+ text);
textField.setText("");
}
}
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyReleased(KeyEvent e) {}
}
My reading mechanism:
StringBuilder cmd = new StringBuilder();
int b;
try {
while ((b = System.in.read()) != -1)
cmd.append(b);
// Do something with cmd
} catch (IOException e){}
The first time I input any text and press enter, it works just fine. After outputting a message the read() function is called, but if I try to enter more text the read() function isn't called anymore. Any suggestions to fix this?
Take a look at this image.
The first time you hit enter, it sets the text to "". Then this block sets b = -1:
if (Objects.equals(text, "")) {
b = -1;
Which is the value that read returns, which makes your main loop finish.

JFormattedTextfield.selectAll() does not work when formatting the text

I have a lot of different JFormattedTextFields with action and keylisteners. Every Field has a keylistener, so when I press enter I will focus the next JFormattedTextField. The Problem is, for some JFormattedTextFields my code is formatting the input and then sets the text new and for those selectAll() does not work.
JFormattedTextField a = new JFormattedTextField(someDouble);
JFormattedTextField b = new JFormattedTextField(someDouble2);
a.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
leasingfaktor1Field.selectAll();
if(...) {
//do something
a.setText(tausenderPunkt(someValue));
}
}
});
a.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) {
b.requestFocusInWindow();
}
}
});
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
leasingfaktor1Field.selectAll();
if(...) {
//do something
b.setText(tausenderPunkt(someValue));
}
}
});
b.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) {
c.requestFocusInWindow();
}
}
});
The function tausenderPunkt():
public String tausenderPunkt(double value) {
String s = String.format("%1$,.2f", value);
return s;
}
So when my cursor is in field a and i press enter the cursor goes to field b but does not select the text or values. When i do not use setText() i do not have the problem. Somebody has a solution?
Edit: For some JFormattedTextFields the solution was to add selectAll() to the keyAdapter, but not for all.
For example:
b.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) {
c.requestFocusInWindow();
c.selectAll();
}
}
});
Edit2:
The problem seems to be when i create the JFormattedTextFields.
When i do not create them with a value in the constructor it works.
But i have to do.
Before moving to your next text field you should consider handling all the required conditions for the text field you are currently focused on and this would of course include the formatting of values or text supplied to that field. Once all the desired conditions are met then move on to the next text field.
In reality this can all be accomplished through the keyPressed event for your particular situation. There is no need for the actionPerformed event on any of your text fields, for example:
a.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
checkConditions(a, b);
}
}
});
b.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
checkConditions(b, c);
}
}
});
//---------- and so on -------------
Here is a simple method so as to eliminate the need for repetitious code:
private void checkConditions(JFormattedTextField fieldA, JFormattedTextField fieldB) {
// Make sure something is contained within fieldA and
// that it's actually numerical text.
if(!fieldA.getText().isEmpty() &&
fieldA.getText().matches("([-]?)\\d+([,]\\d+)?(([.]\\d+)?)")) {
// Convert the supplied text to Double and
// ensure the desired numerical formating.
String res = (String)tausenderPunkt(Double.parseDouble(fieldA.getText().replace(",","")));
fieldA.setText(res);
// Set Focus to our next text fieldB.
fieldB.requestFocusInWindow();
// Highlight the contents (if any) within the
// next text fieldB.
fieldB.selectAll();
}
// If fieldA is empty or fieldA does not contain
// numerical text then inform User and re-highlight
// the entry in fieldA.
else {
JOptionPane.showMessageDialog (null, "Please Enter Numerical Values Only!",
"Incorrect Entry", JOptionPane.WARNING_MESSAGE);
fieldA.selectAll();
}
}
If you want the contents of your first text field to be highlighted as soon as focus has been established upon it (tabbed to or clicked on) then consider using a FocusGained event for that component or any other component where you desire the same effect.
I Hope this has helped in some way.
EDITED!
So as to handle OP's particular situation.
String str=this.getText();
this.setText(str);
this.selectAll();
You can get the focus owner and remove the focusable feature:
Component focusOwner = FocusManager.getCurrentManager().getFocusOwner();
When you get the component, put this sentence after load it:
component.setFocusable(false);

Barcode scanner inserting character by character

Ive been trying a while and cant work out a way to get the text out of a textbox only after the barcode scanner has finished scanning. I am using Swing Framework and Java. My Code works if the text is pasted (Ctrl + V) into the JTextbox but the barcode scanner just does not work as my methods run for every couple of characters of the barcode in turn.
textField.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
// warn();
}
public void removeUpdate(DocumentEvent e) {
// warn();
}
public void insertUpdate(DocumentEvent e) {
warn();
}
public void warn() {
input = textField.getText();
SwingUtilities.invokeLater(doUpdate);
}
});
Where input is a variable which I use
The doUpdate method which gets executed is here
final Runnable doUpdate = new Runnable() {
#Override
public void run() {
System.out.println(input + " Is being processed");
textField.setText("");
System.out.println(input.length());
lblStatus.setIcon(new ImageIcon(Main.class
.getResource("/com/daniel/status2.png")));
// frame.getContentPane().add(lblStatus2, BorderLayout.EAST);
if (input.length() <= 4) {
lblStatus.setIcon(new ImageIcon(Main.class
.getResource("/com/daniel/status3.png")));
return;
}
// TODO START
if (!content.containsKey(input)) {
content.put(input, "1");
} else {
Integer i = Integer.valueOf(content.get(input));
i++;
lblStatus.setIcon(new ImageIcon(Main.class.getResource(i
+ ".png")));
playSound(i + "");
if (i.equals(6)) {
i = 0;
}
content.put(input, String.valueOf(i));
}
System.out.println(content.get(input));
Properties properties = new Properties();
for (Entry<String, String> entry : content.entrySet()) {
properties.put((String) entry.getKey(), entry.getValue());
}
try {
properties.store(new FileOutputStream(path
+ "data.properties"), null);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
All Im asking is how do I run a method with a String from the text box only after the barcode reader has finished
It's been a while since I used a barcode reader, but when I did each scan was a line. That is, each scan was a sequence of characters terminated by the new line character. So, you could test the input and only invoke doUpdate when you detect the new line character. That said, you might need to set a property on the text box to allow the new line character and remove it when detected.
You need someway to inject a delay between each update of the DocumentListener and the time your inspect/use the value from the text field.
A simple solution is to use a Swing Timer with a short delay (you can play around with this to find the threashold which is most useful to you). Each time the DocumentListener is notified, you simply reset the timer (start it again), so that it won't trigger until after the delay you specified has occurred, at which time you can read the text from the field.
public class TestPane extends JPanel {
private Timer updateTimer;
private JTextField field;
private JLabel label;
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
updateTimer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(field.getText());
}
});
updateTimer.setRepeats(false);
label = new JLabel("...");
field = new JTextField(14);
field.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
processUpdate();
}
#Override
public void removeUpdate(DocumentEvent e) {
processUpdate();
}
#Override
public void changedUpdate(DocumentEvent e) {
processUpdate();
}
});
add(field, gbc);
add(label, gbc);
}
protected void processUpdate() {
updateTimer.restart();
}
}
You could test this using Robot, which can be used to inject key strokes into the event queue with a specified delay, or just run your scanner at it.
See How to use Swing Timers for more details
You should also attach a ActionListener to the field and see if the scanner triggers the ActionListener, which would be a simpler solution over all

Java How to get ascii char from key code

I want to get ascii char from each key that user inputs into JComponent.
<JComponentName>.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
char ch = getAsciiCharFromKeyCode(e.getKeyCode());
}
});
private char getAsciiCharFromKeyCode(int keyCode) {
// this implementation is what I'm interested in
}
When I press 'e' (English 'e') and 'у' (Cyrillic 'u'), I get the same key code (0x45 or KeyEvent.VK_E). Is there some way to implement getAsciiCharFromKeyCode function without writing my own hash map like this:
HashMap<Integer, Character> keyCodeToChar = new HashMap<Integer, Character>();
keyCodeToChar.put(KeyEvent.VK_E, 'e');
?
As Pshermo already mentioned the method you are looking for is e.getKeyChar() however, it is only meaningfull in the method keyTyped as explained here
Your code would modified look like this:
<JComponentName>.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
char ch = e.getKeyChar();
}
});
Check out the tutorial Oracle: How to Write a Key Listener for more information on how to use KeyEvent to grab key info.
Oracle says:
For key-typed events you can obtain the key character value as well as any modifiers used.
Note:
You should not rely on the key character value returned from getKeyChar unless it is involved in a key-typed event.
You may need to set the locale to accept language specific keyboard characters.
class MyFrame extends JFrame {
private JTextArea txtara;
private JLabel lbl;
public MyFrame() {
super();
this.getContentPane().setLayout();
this.getInputContext().selectInputMethod(new Locale("ru")); // Russian
txtara = new JTextArea(5, 20);
lbl = new JLabel("Key: ");
txtara.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
lbl.setText("Key: " + e.getKeyChar()); // Show typed character
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
};
this.getContentPane().add(txtara, BorderLayout.CENTER);
this.getContentPane().add(lbl, BorderLayout.SOUTH);
}
}
Disclaimer: This code has not been compiled...

Unable to get JFormattedTextField to highlight on mouse click focus event

I have been trying with no luck to get a JFormattedTextField to highlight on mouse click. I have been able to get it to work fine while tabbing through fields, however I would like to highlight everything on clicking.
I am only able to highlight on mouse click if I click and hold for about 1.5-2 seconds on the text field; I have no idea why.
I've searched and tried a few fixes including extending the class;
class HFTextField extends JFormattedTextField
{
HFTextField(MaskFormatter formatter)
{
super(formatter);
}
#Override
protected void processFocusEvent(FocusEvent e)
{
super.processFocusEvent(e);
if (e.getID() == FocusEvent.FOCUS_GAINED)
{
this.selectAll();
}
}
}
I am also defining a (rather verbose!) FocusListener which uses SwingUtilities.invokelater;
public static FocusListener CreateHighlightTextFieldFocusListener(final JTextField text_field)
{
FocusListener fl =
new FocusAdapter()
{
public void focusGained(FocusEvent evt)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
text_field.selectAll();
}
});
}
};
return fl;
}
and this is the function that creates formatted text fields;
public static JTextField CreateFormattedTextField(int x, int y, int width, int height,
Method action_method, Method changed_method, Method remove_method,
Method update_method, String mask_formatter, String banned_chars)
{
MaskFormatter formatter = null;
try {
formatter = new MaskFormatter(mask_formatter);
} catch (ParseException e) {
assert(false);
}
if(banned_chars != null)
formatter.setInvalidCharacters(banned_chars);
JTextField text_field = new HFTextField(formatter);
text_field.setBounds(x, y, width, height);
if(action_method != null)
{
text_field.addActionListener(CreateTextFieldActionListener(action_method, text_field));
}
text_field.getDocument().addDocumentListener(
CreateTextFieldDocumentListener(changed_method, remove_method,
update_method, text_field));
text_field.addFocusListener(CreateHighlightTextFieldFocusListener(text_field));
return text_field;
Any help would be greatly appreciated!
maybe you have got problems with EDT,
how method you use for/how you added value to JTextField
works with JTextField, JFormateddTextField, with JComboBox too, and with AutoCompleted funcionalies http://www.java2s.com/Code/Java/Swing-JFC/AutocompleteTextField.htm
private FocusListener focsListener = new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
dumpInfo(e);
}
#Override
public void focusLost(FocusEvent e) {
//dumpInfo(e);
}
private void dumpInfo(FocusEvent e) {
//System.out.println("Source : " + name(e.getComponent()));
//System.out.println("Opposite : " + name(e.getOppositeComponent()));
//System.out.println("Temporary: " + e.isTemporary());
Component c = e.getComponent();
if (c instanceof JFormattedTextField) {
((JFormattedTextField) c).requestFocus();
((JFormattedTextField) c).setText(((JFormattedTextField) c).getText());
((JFormattedTextField) c).selectAll();
} else if (c instanceof JTextField) {
((JTextField) c).requestFocus();
((JTextField) c).setText(((JTextField) c).getText());
((JTextField) c).selectAll();
}
}
private String name(Component c) {
return (c == null) ? null : c.getName();
}
};
Try the following code
yourTextField.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusGained(java.awt.event.FocusEvent evt) {
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
yourTextField.selectAll();
}
});
}
});
I hate to give a simple answer, but have you tried using the MouseListener interface (or MouseAdapter class)?
Have you tried something like this:
fieldName.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JTextComponent text = (JTextComponent) e.getSource();
text.selectAll();
}
});
Also, I would not recommend doing this asynchronously.
If you want specialized behavior for a mouse click, then add a MouseAdapter to your JTextFiled, and in the mouseClicked event handler, explicitly alter the background.
basically you can use this code (not sure that for each formatter and input masks), but for Number, Date and String you can use following, with ensure that this JFormattedTextField doesn't implements AutoCompleted
myTextField.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
myTextField.requestFocus();
myTextField.setText(myTextField.getText());
myTextField.selectAll();
}
#Override
public void focusLost(FocusEvent e) {
}
});
sure you can pack that into InvokeLate...

Categories