JFormattedTextField, accept values [0.1, 100], uncomfortable writing of numbers - java

I am trying to restrict the input values inside JFormatdedTextField to [0.1, 100] using AbstractFormatter
public AbstractFormatter getFormatter(JFormattedTextField tf) {
NumberFormat f = DecimalFormat.getInstance();
f.setMinimumFractionDigits(1);
f.setMaximumFractionDigits(2);
InternationalFormatter iff= new InternationalFormatter(f);
iff.setAllowsInvalid(false);
iff.setMinimum(0.1);
iff.setMaximum(100.00);
return iff;
}
However, the abstract formatter has a strange behavior. Suppose that I would like to write the following number inside the interval: 0.2.
The abstract formatter blocks the first digit: 0. It is necessary to write 0.2 in 2 phases:
a] 1.2 //or any digit > 0
b] delete 1: 1.2->0.2
Such a behavior is for the user confusing. Is there any way to prevent this uncomfortable writing of numbers?
Thanks for your help.

I think that what you need is a JSPinner:
JSpinner spin = new JSpinner(new SpinnerNumberModel(50 /*or whatever*/, 0.1, 100, 0.1));
or you can just remove the line
iff.setAllowsInvalid(false);
to remove the confusing behavior. The Javadocs for this method say:
Sets whether or not the value being edited is allowed to be invalid
for a length of time (that is, stringToValue throws a ParseException).
It is often convenient to allow the user to temporarily input an
invalid value.
These will let the focus leave the component if the value is invalid and it will be reverted to the previous valid value. If you want to not allow losing focus use an input verifier as in said in the comments. There is an example in the Javadocs of JFormattedTextField: http://docs.oracle.com/javase/8/docs/api/javax/swing/JFormattedTextField.html

Related

How to cast from String to BigDecimal with the separator as a comma? [duplicate]

This question already has answers here:
Java BigDecimal can have comma instead dot?
(3 answers)
Closed 3 months ago.
I have a method to set a BigDecimal number that is given as String:
private Client mapClient(Client client){
ClientRequest clientRequest = new ClientRequest();
// Code
clientRequest.setCashAmount(castStringToBigDecimal(client.getCashAmount()));
// More Code
}
My castStringToBigDecimal method is the follosing:
public BigDecimal castStringToBigDecimal(String value){
BigDecimal response = null;
if(value != null && !value.equals("")){
value = value.replaceAll("[.]", ",");
response = new BigDecimal(value);
}
return response;
}
An example of the input value is "1554.21"
I need that the bigDecimal separator to be a comma, not a dot. But this is giving me an exception.
EDIT
The value is the following:
And the exception is:
java.lang.NumberFormatException: Character , is neither a decimal digit number, decimal point, nor "e" notation exponential mark.
BigDecimal doesn't represent a rendering. In other words, whether to use a comma or a dot as separator is not part of the properties a BigDecimal object has.
Hence, you do not want to call .replaceAll. (And separately, you'd want .replace(".", ",") - replace replaces all, and replaceAll also replaces all and interprets the first arg as a regex, and is therefore needlessly confusing here). Just pass it with the dot.
To render a BigDecimal, don't just sysout it, that will always show a dot and there is nothing you can do about that. toString() is almost never the appropriate tool for the job of rendering data to a user - it's a debugging aid, nothing more. Use e.g. String.format("%f"), specifying the appropriate locale. Or use NumberFormat. The javadoc of BigDecimal explicitly spells this out.
There are various other issues with your code:
"cast" is the technical name for the syntactic construct: (Type) expr; - and this construct does 3 utterly different things, hence using it to describe a task, i.e. use it in a method name, is a very bad idea. In particular, only one of the 3 things it does converts anything, and you clearly use it here in the 'convert something' meaning. This is misleading; only if it's all primitives does the cast operator convert, and BigDecimal isn't primitive. Call it convertTo or whatever you please, not "cast".
BigDecimal is an extremely complicated tool for the job and usually not the right tool if you want to represent financial data. Instead, represent the atomary unit in a long and call the appropriate rendering method whenever you need to show it to a user. For example, for euros, the atomary unit is the eurocent. If something costs €1,50, you'd store "150", in a long. Before you think: But, wait, I want to divide, and then I'd lose half a cent! - yes, well, you can't exactly send your bank a request to transfer half a cent, either. Also, try to divide 4 cents by 3 with a BigDecimal and see what happens. Dividing financial amounts is tricky no matter what you use, BD isn't a catch-all solution to this problem.
I looked up the source code for Java 8's implementation of BigDecimal (https://github.com/frohoff/jdk8u-dev-jdk/blob/master/src/share/classes/java/math/BigDecimal.java), and the period character is hard-coded in that source as the decimal point. I would not have thought this of a language for which internationalization has been so thoroughly designed in, but there it is, line 466.
Given that the author(s) of BigDecimal failed to take locale into account in such a basic way -- the use of comma instead of period as the decimal separator in Europe is well-known -- I'd have to say you cannot use that BigDecimal constructor on unaltered Strings that are otherwise formatted correctly but which (might) have a comma separator. There are other options -- the previous SO post referred to in one of the comments has one -- but it appears you cannot convert your String this way.
(One minor point -- you are not "casting" anything. That word has a specific meaning in OO programming, and a more specific one in Java, and has very little to do with your question. It is incorrect to refer to conversion as casting.)

How to dynamically change card number input mask (Android, Java)

I need to change input mask dynamically. For example, if user inputs 13 digits then one mask, if 20 then another.
I am using redmadrobot:inputmask. Here is my code
ArrayList<String> affineFormats = new ArrayList<>();
affineFormats.add("[0000] [000] [000] [000]");
affineFormats.add("[0000] [0000] [0000] [0000] [0000]");
String format = "[0000] [000] [000] [000]";
MaskedTextChangedListener listener = new PolyMaskTextChangedListener(
format,
affineFormats,
true,
etCardNumber,
null,
new MaskedTextChangedListener.ValueListener() {
#Override
public void onTextChanged(boolean b, String s) {
//here some code
}
});
etCardNumber.addTextChangedListener(listener);
But when I enter the card number is used the last one added is formatted according to affineFormats. Please help me fix this problem.
From your code it looks like you are using a slightly outdated version of our library.
In v.4 we already have PolyMaskTextChangedListener merged with the MaskedTextChangedListener. We also introduced a handy utility called AffinityCalculationStrategy, which might actually help with your problem.
From our Wiki:
Affinity calculation strategy
Affinity is an integer number, which represents the similarity between the input and the current mask. Thus, the mask with the highest affinity is picked to format the output.
Affinity calculation strategy is a text field listener property allowing to alter the math behind the affinity calculation.
...
AffinityCalculationStrategy.EXTRACTED_VALUE_CAPACITY— this strategy comes in handy when the mask format radically changes depending on the extracted value length.
(and your digits are the extracted value)

How to make JFormattedTextField accept integer without decimal point (comma)?

I used JFormattedTextField withNumberFormat in this way:
-Creat a JFormattedTextField refernce
JFormattedTextField integerField;
-Create a NumberFormat refernce
NumberFormat integerFieldFormatter;
-In the constructor:
integerFieldFormatter = NumberFormat.getIntegerInstance();
integerFieldFormatter.setMaximumFractionDigits(0);
integerField = new JFormattedTextField(integerFieldFormatter );
integerField.setColumns(5);
..........
I meant to use it with integer numbers only, but when I type numbers like 1500 it is converted after losing focus to 1,500 , and exception thrown this is the first line of it:
Exception in thread "AWT-EventQueue-0"
java.lang.NumberFormatException: For input string: "1,500"
When I use JTextField instead of JFormattedTextField All integers accepted normally, But the reason why I want to use JFormattedTextField is to benefit from its input restriction advantages.
I realize this is an old question but I just stumbled upon it through the same issue. As the other answers seemed like workarounds to me, I took a closer look at the NumberFormat methods.
I found that the easiest approach would actually be to simply deactivate grouping on the NumberFormat instance:
NumberFormat integerFieldFormatter = NumberFormat.getIntegerInstance();
integerFieldFormatter.setGroupingUsed(false);
That way no group delimiters will appear in the textfield output.
Of course you will also not be able to use them for your input, but that was not intended by the question, right?
Also for an integer instance of NumberFormat you don't need to explicitly setMaximumFractionDigits(0), as that is part of what getIntegerInstance() does for you.
I discovered the solution to my problem; Here it is:
The exact problem is that when I use JFormattedTextField with NumberFormat, the JFormattedTextField adds comma ',' before any next 3 digits for example
1000 rendered as 1,000
10000 rendered as 10,000
1000000 rendered as 1,000,000
When I read an integer value from JFormattedTextField usign this line of code
int intValue = Integer.parseInt(integerField.getText());
The comma is read as part of the string; 1000 read as 1,000 and this string value cannot be converted to integer value, and so exception is thrown.
Honestly the solution is in this Answer but I will repeat it here
use str.replaceAll(",","")
int intValue = Integer.parseInt(integerField.getText().replaceAll(",", ""));
This will replace any comma charachter ',' in the returned string and will be converted normally to int as expected.
Regards
You can do it in (at least) 2 ways:
Using a keyListener
Using DocumentFilter
if you want to use KeyListener:
KeyListener listener = new KeyAdapter(){
public void keyTyped(KeyEvent e){
if(e.getKeyCode()<KeyEvent.VK_0||e.getKeyCode()>KeyEvent.VK_9{//input<'0' or input>'9'?
e.consume();//delete the typed char
}
}
}
yourTextField.addKeyListener(listener);
to use the DocumentFilter check this link: How to allow introducing only digits in jTextField?
EDIT: i forgot to say this. As MadProgrammer said in the first comment to this answer, KeyListener is not the proper way to do it, because
You do not know in what order KeyListeners will be notified of the event and the key may have already gone to the text component before it reaches you (or it could have been consumed before it reaches you)
EDIT #2: ANOTHER QUICK WAY
MaskFormatter formatter = new MaskFormatter("#####");
JFormattedTextField field = new JFormattedTextField(formatter);
And the trick should be done. with this you can insert up to 5 digits in tour textfield, more '#' in the string parameter for the formatter = more digits can be typed by the user
Try this, This is a complete solution for creating and validating number JTextField
NumberFormat format = NumberFormat.getInstance();
format.setGroupingUsed(false);//Remove comma from number greater than 4 digit
NumberFormatter sleepFormatter = new NumberFormatter(format);
sleepFormatter.setValueClass(Integer.class);
sleepFormatter.setMinimum(0);
sleepFormatter.setMaximum(3600);
sleepFormatter.setAllowsInvalid(false);
sleepFormatter.setCommitsOnValidEdit(true);// committ value on each keystroke instead of focus lost
JFormattedTextField textFieldSleep = new JFormattedTextField(sleepFormatter);
textFieldSleep.setText("0");

How to set jFormattedTextField so it only allows 2 numbers?

I'm a beginner in Java, and NetBeans. I'm trying to make a simple program where you introduce 2 numbers and their sum gets divided by two. However, I'm using JFormattedTExtFields and I don't know how to customize the allowed input in them. Basically I'm trying to find out how to:
Only allow numbers to be entered in JFormmatedTextField;
Only allow a certain amount of numbers;
You could use a NumberFormat and specify the maximum number of integer digits with setMaximumIntegerDigits.
Here's a nice article.
Basically you can do something like:
NumberFormat f = NumberFormat.getNumberInstance();
f.setMaximumIntegerDigits(maxDigitsAmount);
JFormattedTextField field = new JFormattedTextField(f);
The Format should guarantee that the inserted String satisfy the format. Anyway even if a number is supplied, the textfield will store it as a String. So if you need your original Integer you need to rebuild it like suggested #noise:
Integer i = Integer.toString(field.getText());

Java string to double conversion

I've been reading up on the net about the issues with handling float and double types in java. Unfortunately, the image is still not clear. Hence, i'm asking here direct. :(
My MySQL table has various DECIMAL(m,d) columns. The m may range from 5 to 30. d stays a constant at 2.
Question 1.
What equivalent data-type should i be using in Java to work (i.e store, retrieve, and process) with the size of the values in my table? (I've settled with double - hence this post).
Question 2.
While trying to parse a double from a string, i'm getting errors
Double dpu = new Double(dpuField.getText());
for example -
"1" -> java.lang.NumberFormatException: empty String
"10" -> 1.0
"101" -> 10.0
"101." -> 101.0
"101.1" -> 101.0
"101.19" -> 101.1
What am i doing wrong? What is the correct way to convert a string to a double value?
And what measures should i take to perform operations on such values?
EDIT
This is the code -
System.out.println(dpuField.getText());
Double dpu = new Double(dpuField.getText());
System.out.println(dpu);
Yes, the problem lies with getText() reporting the wrong value of the dpuField.
This method is called on the JTextField keyTyped event. So what's going wrong here?
EDIT 2
Looking at :
http://journals.ecs.soton.ac.uk/java/tutorial/post1.0/ui/keylistener.html
Apparently, keyTyped() does not give me the keycode. I'll have to switch to keyRealeased()
What equivalent data-type should i be using in Java to work (i.e store, retrieve, and process) with the size of the values in my table? (I've settled with double - hence this post).
Since it's a DECIMAL field, you should prefer java.math.BigDecimal. You can store it in DB using PreparedStatement#setBigDecimal() and you can retrieve it from DB using ResultSet#getBigDecimal().
While trying to parse a double from a string, i'm getting errors
This can't be true. The problem lies somewhere else. Maybe it is just not returning the data you expect to be returned or you are not using/debugging the values you expect them to be.
if you need exact precision without rounding errors, you should use a BigDecimal.
Your code looks OK - could it be that dpuField.getText() somehow cuts the last character from the string values you list above?
Update: you say
Yes, the problem lies with getText() reporting the wrong value of the dpuField. This method is called on the JTextField keyTyped event.
Could it be that getText() returns the value of the field before the last typed key is actually appended to it?
For decimal, I believe you risk losing precision if you don't use a BigDecimal on the Java side, as some decimal fractions can't be stored as a binary fraction.
Prefer Double.valueOf(String) over the constructor, but that's a valid way. Something else must be going on (i.e. I doubt those are the actual String values you're passing in).
Question1: It's bad idea to map DECIMAL columns to Double, usually the BigDecimal is the correct type. http://java.sun.com/j2se/1.3/docs/guide/jdbc/getstart/mapping.html#1055175
Question 2: You are doing something wrong; print the String value before converting.

Categories