I am trying to format string in TextField which includes: remove all characters that are not numbers, format numbers using DecimalFormatter and limit number of character in the TextField:
private void IntegerInputChecker() {
ChangeListener<String> integerChecker = new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
String pureValue = newValue;
String formatedText = newValue;
if (pureValue.length() <= 15) {
// Limit the characters in TextField
if (!newValue.matches("\\d*")) {
// Replace everything excepts number
pureValue = newValue.replaceAll("[^\\d]", "");
formatedText = pureValue;
}
if (pureValue.length() > 3) {
// Format number
DecimalFormat formatter = new DecimalFormat( "#,###" );
formatedText = formatter.format(Double.parseDouble(pureValue));
}
else if(pureValue.length() == 3) {
formatedText = pureValue.replaceAll(",", "");
}
}
else {
// Limit the characters in TextField
formatedText = formatedText.substring(0, formatedText.length() - 1);
}
fieldPrice.setText(formatedText);
}
};
fieldPrice.textProperty().addListener(integerChecker);
}
Everything works fine when I type in number but when I try to delete them in order, JavaFX throws an exception (the program still works fine):
java.lang.IllegalArgumentException: The start must be <= the end
For example, If I have 123,456,789 when I delete the number to 123,456 then it throws the above exception.
Thanks in advance.
Related
i'm doing a calculator app and i'm using Decimal Format to format my number.
My problem is i want it to show number like 0,003. But 2 zero after the comma didn't show up until I typing 3. Please help me fix this
Here my code
DecimalFormatSymbols formatSymbolsGermany = DecimalFormatSymbols.getInstance(Locale.GERMANY);
DecimalFormat decimalFormat = new DecimalFormat("#,###,###.########", formatSymbolsGermany);
My code to add "0"
private void zeroOnClick() {
if (tvNumber.getText().toString().length() < 11) {
convertNumber("0");
}
}
private void convertNumber(String number) {
number1 += number;
try {
double a = Double.parseDouble(number1.replace(',', '.'));
tvNumber.setText(decimalFormat.format(a));
delete = true;
} catch (Exception e) {
e.printStackTrace();
}
}
private void addComma(){
if (comma) {
//do nothing
} else {
number1 += ",";
tvNumber.setText(number1);
comma = true;
}
}
The simplest solution would be to just tvNumber.setText(number1); directly without the back-and-forth conversion while editing and display it in the proper format only after the editing is done.
Another possibility would be to artificially append a digit before the conversion if the number contains a decimal point and then remove it afterwards.
String res = number1.replace(',', '.'); // shouldn't this be decimalFormat dependent ?
if( number1.contains(".") ) {
res = decimalFormat.format( Double.parseDouble(res+"5"));
res = res.substring(0,res.length()-1);
} else {
res = decimalFormat.format( Double.parseDouble(res) );
}
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 2 years ago.
I'm trying to code a project that deals with a dumb downed version of an excel sheet. One of the commands I am allowing from the user is assigning a value into specific cells in the format of = . i.e A3 = 72.3%. Once it assigns the value to the cell, it then prints out the entire spreadsheet with the updated changes.
I created different classes to represent different types of cells.
Value Cell is a cell that will contain any double value, Percent Cell is a cell that will contain any percentage value, Text Cell is a cell that will contain text.
While I was trying to run my program, I tried assigning a percentage value to a cell. (A2 = 7.25%)
When the program prints out the entire spreadsheet with the newly assigned A2 percentage cell, it's supposed to truncate the decimal from the percentage and just display 7%.
However, I keep getting this error message
Exception in thread "main" java.lang.NullPointerException
at PercentCell.abbreviatedCellText(PercentCell.java:18)
at Spreadsheet.getGridText(Spreadsheet.java:127)
at Spreadsheet.processCommand(Spreadsheet.java:73)
at TextExcel.main(TextExcel.java:20)
This is part of my code that assigns specific cell types depending on what the user inputs:
//assignment command
} else if (command.contains("=")) {
int eqIndex = command.indexOf("=");
if (!command.substring(eqIndex - 1, eqIndex).equals(" ") || !command.substring(eqIndex + 1, eqIndex + 2).equals(" ")) {
return "Formatting Error: please include a space before and after the ="; //error message
} else {
String[] input = command.split(" ", 3);
SpreadsheetLocation cell = new SpreadsheetLocation(input[0]);
int col = cell.getCol();
int row = cell.getRow();
if (col > COLUMNS || row > ROWS) {
return "Error: cell outside of spreadsheet"; //error message
}
//assigning a textcell
if (input[2].contains("\"")) {
String inputValue = input[2].substring(1, input[2].length() - 1);
TextCell textCell = new TextCell(inputValue);
spreadsheet[row][col] = textCell;
//assigning a percent cell
} else if (input[2].contains("%")) {
PercentCell percentCell = new PercentCell(input[2]);
spreadsheet[row][col] = percentCell;
The percent cell extends super class, Real Cell:
public class RealCell implements Cell {
private String fullText;
public RealCell(String input) {
this.fullText = input;
}
//method that returns the display of the abbreviated cell
public String abbreviatedCellText() {
if (fullText.length() > 10) {
return fullText.substring(0, CELLWIDTH);
} else {
String output = fullText;
while(output.length() < CELLWIDTH) {
output += " ";
}
return output;
}
}
//method that returns the actual value in a real cell
public String fullCellText() {
return fullText;
}
//method that parses the user input into a double
public double getDoubleValue() {
return Double.parseDouble(fullText);
}
}
Here is now the problem part of my code, the Percent Cell class:
public class PercentCell extends RealCell {
private String fullText;
public PercentCell(String input) {
super(input);
}
//method that returns the display of the abbreviated cell, truncating the decimal
public String abbreviatedCellText() {
String value = fullText;
if (value.contains(".")) {
int decIndex = fullText.indexOf(".");
value = value.substring(0, decIndex) + "%";
}
if (value.length() > 10) {
return value.substring(0, CELLWIDTH);
} else {
String output = value;
while(output.length() < CELLWIDTH) {
output += " ";
}
return output;
}
}
//method that parses the user input into a double and returns the percent value into a decimal
public double getDoubleValue() {
double decimal = Double.parseDouble(fullText.substring(0, fullText.length() - 1));
return decimal/100;
}
}
How could I fix this error?
If any clarifications regarding my code are needed (because this is not my entire project) please let me know!
public class PercentCell extends RealCell {
public PercentCell(String input) {
super(input);
}
//method that returns the display of the abbreviated cell, truncating the decimal
public String abbreviatedCellText() {
String value = super.fullCellText();
if (value.contains(".")) {
int decIndex = value.indexOf(".");
value = value.substring(0, decIndex) + "%";
}
if (value.length() > 10) {
return value.substring(0, CELLWIDTH);
} else {
String output = value;
while(output.length() < CELLWIDTH) {
output += " ";
}
return output;
}
}
You used fullText field without giving value and fullText value was always null
I think that was the problem, but let me know please if it helps!
I created a simple javafx program. I want to seprate the digits three by three when I entering the digits in the textfiled. I used two solution which are given in the stackoverflow links(How to format text of TextField? JavaFX , Java 8 U40 TextFormatter (JavaFX) to restrict user input only for decimal number)
but none them are working for me. the first solution(set textformatter) was useless for me(or maybe I couldn't work with it in a right way) but the second one was working but only accept 4 digits and the other numbers that I enterd in the textfield are the in the same style that I enterd them without comma.
I want to seprate every three digits like this: 12,564,546,554
if anyone know the solution please help me to overcome this problem.
thanks.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import java.text.DecimalFormat;
import java.text.ParsePosition;
public class DelimiterExample extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
TextField textField = new TextField();
HBox hBox = new HBox();
//solution one
DecimalFormat format = new DecimalFormat( "#,###" );
textField.setTextFormatter( new TextFormatter<>(c ->
{
if ( c.getControlNewText().isEmpty() )
{
return c;
}
ParsePosition parsePosition = new ParsePosition( 0 );
Object object = format.parse( c.getControlNewText(), parsePosition );
if ( object == null || parsePosition.getIndex() < c.getControlNewText().length() )
{
return null;
}
else
{
return c;
}
}));
// solution two
textField.textProperty().addListener((obs , oldVal , newVal)-> {
if (newVal.matches("\\d*")) {
DecimalFormat formatter = new DecimalFormat("#,###");
String newvalstr = formatter.format(Float.parseFloat(newVal));
//System.out.println(newvalstr);
textField.setText(newvalstr);
}
});
hBox.getChildren().add(textField);
Scene scene = new Scene(hBox , 100 , 100);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Any modification that is not a selection change can be fixed by modifying the chars before the range:
final char seperatorChar = ',';
final Pattern p = Pattern.compile("[0-9" + seperatorChar + "]*");
textField.setTextFormatter(new TextFormatter<>(c -> {
if (!c.isContentChange()) {
return c; // no need for modification, if only the selection changes
}
String newText = c.getControlNewText();
if (newText.isEmpty()) {
return c;
}
if (!p.matcher(newText).matches()) {
return null; // invalid change
}
// invert everything before the range
int suffixCount = c.getControlText().length() - c.getRangeEnd();
int digits = suffixCount - suffixCount / 4;
StringBuilder sb = new StringBuilder();
// insert seperator just before caret, if necessary
if (digits % 3 == 0 && digits > 0 && suffixCount % 4 != 0) {
sb.append(seperatorChar);
}
// add the rest of the digits in reversed order
for (int i = c.getRangeStart() + c.getText().length() - 1; i >= 0; i--) {
char letter = newText.charAt(i);
if (Character.isDigit(letter)) {
sb.append(letter);
digits++;
if (digits % 3 == 0) {
sb.append(seperatorChar);
}
}
}
// remove seperator char, if added as last char
if (digits % 3 == 0) {
sb.deleteCharAt(sb.length() - 1);
}
sb.reverse();
int length = sb.length();
// replace with modified text
c.setRange(0, c.getRangeEnd());
c.setText(sb.toString());
c.setCaretPosition(length);
c.setAnchor(length);
return c;
}));
Please guys don't reinvent the wheel. The class javafx.util.converter.NumberStringConverter do all the needed work for us:
TextField numberField = new TextField();
TextFormatter<Number> textFormatter = new TextFormatter<>(new NumberStringConverter());
numberField.setTextFormatter(textFormatter);
NumberStringConverter is your friend ;), if you want to listen the correct number changes on the textfield, simply use the next textFormatter property (forgive direct method invocations over the textField object):
textFormatter.valueProperty(); //Returns ObjectProperty<Number>
Then you can retrieve any kind of number subclass (number.intValue(), number.floatValue(), etc), and the ChangeListener only will be triggered when the user writes a valid numeric value.
If you want to manually set a number value to the textfield:
float value = 1000f; //or any type of Number subclass
textFormatter.setValue(value);
P.D: NumberStringConverter has a Locale attribute that can be changed to apply the appropriate format of the desired Country.
Thanks again #fabian.I stucked in this problem for two days. I also found that I can have the currency style that I want with this code.Now I have two perfect solution.
textField.setOnKeyTyped(event -> {
String typedCharacter = event.getCharacter();
event.consume();
if (typedCharacter.matches("\\d*")) {
String currentText =
textField.getText().replaceAll("\\.","").replace(",", "");
long longVal = Long.parseLong(currentText.concat(typedCharacter));
textField.setText(new DecimalFormat("#,##0").format(longVal));
}
});
I have an array of y values that I am displaying over the dates of a month. To simplify, for the first week of April, I would have the values {0,200,0,0,500,0,100} over the x values {1,2,3,4,5,6,7}. I am able to display them as a bar chart using MPAndroidChart. I am also able to hide and display the values over each bar using
barChart.getData().setDrawValues(true); //or false when I want to hide
However, I want to display only the number that are non-zero, how would I be able to do so? Any pointers would be appreciated!
I tried creating my formatter the following way:
public class MyYAxisValueFormatter implements IAxisValueFormatter {
private DecimalFormat mFormat;
public MyYAxisValueFormatter() {
// format values to 1 decimal digit
mFormat = new DecimalFormat("###,###,##0.0");
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
String val = "";
if(value != 0)
val = String.valueOf(value);
return mFormat.format(value) + " $";
}
}
and called it using this in my main function:
YAxis yAxis = barChart.getAxisLeft();
yAxis.setValueFormatter(new MyYAxisValueFormatter());
However the values of zero are still displayed.
Try making your own IValueFormatter Interface
public class MyYAxisValueFormatter implements IValueFormatter {
private DecimalFormat mFormat;
public MyYAxisValueFormatter() {
// format values to 1 decimal digit
mFormat = new DecimalFormat("###,###,##0.0");
}
#Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
// "value" represents the position of the label on the axis (x or y)
if(value > 0) {
return mFormat.format(value);
} else {
return "";
}
}
}
try setting value formatter to your barchart.
bar.setValueFormatter(new MyYAxisValueFormatter ());
try this:
private class MyValueFormatter implements ValueFormatter {
#Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
// write your logic here
if(value > 0)
return value+"";
else
return "";
}
}
OR
try this likn it helps you
https://github.com/PhilJay/MPAndroidChart/issues/2402
I want to format user's input and it's okay, but when I try to input zero after dot DecimalFormat removes it.
I use the following code:
DecimalFormat df = new DecimalFormat("#,###.##");
Number n = df.parse(v);
amountEdit.setText(df.format(n));
Example Input/Output:
9.0 -> 9.
9.9 -> 9.9
9.90 -> 9.9
It removes zeros!
EDIT:
I have EditText with TextChangedListener
The idea is to format user's input like 999 999 999.99 (this is max value).
amountEdit.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (data.document.isPaymentPossible) {
if (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator())) || s.toString().contains(".")) {
hasDot = true;
} else {
hasDot = false;
}
}
}
#Override
public void afterTextChanged(Editable s) {
String string = s.toString().replaceAll("\\.", ",");
if (string.equals(",") || string.equals(".")) {
amountEdit.setText("");
return;
}
amountEdit.removeTextChangedListener(this);
payCustomAmount.setEnabled(amountEdit.getText().length() != 0);
try {
if (string.contains(",")) {
try {
String afterDot = string.split(",")[1];
if (afterDot.length() > 2) {
string = string.substring(0, string.length() - 1);
Number n = df.parse(string);
amountEdit.setText(df.format(n).replace(",", "."));
amountEdit.setSelection(amountEdit.getText().length());
amountEdit.addTextChangedListener(this);
showOverPaidText();
return;
}
} catch (Exception e) {
if (BuildConfig.DEBUG) {
SysUtils.logf("PaymentOptions input: " + s + "Exception: " + e);
}
}
} else {
if (string.length() > 11) {
string = string.substring(0, string.length() - 1);
Number n = dfnd.parse(string);
amountEdit.setText(dfnd.format(n));
amountEdit.setSelection(amountEdit.getText().length());
showOverPaidText();
amountEdit.addTextChangedListener(this);
return;
}
}
int inilen, endlen;
inilen = amountEdit.getText().length();
String v = string.replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
int cp = amountEdit.getSelectionStart();
if (hasDot) {
Number n = df.parse(v);
String ss = df.format(n).replace(",", ".");
amountEdit.setText(ss);
} else {
Number n = dfnd.parse(v);
amountEdit.setText(dfnd.format(n));
}
endlen = amountEdit.getText().length();
int sel = (cp + (endlen - inilen));
if (sel > 0 && sel <= amountEdit.getText().length()) {
amountEdit.setSelection(sel);
} else {
amountEdit.setSelection(amountEdit.getText().length() - 1);
}
} catch (NumberFormatException | ParseException e) {
showOverPaidText();
amountEdit.addTextChangedListener(this);
if (BuildConfig.DEBUG) {
SysUtils.logf("PaymentOptions input: " + s + "Exception: " + e);
}
return;
}
showOverPaidText();
amountEdit.addTextChangedListener(this);
return;
}
});
My onCreate contains:
df = new DecimalFormat("#,###.00");
df.setDecimalSeparatorAlwaysShown(true);
dfnd = new DecimalFormat("#,###");
hasDot = false;
It removes zeros!
Well yes, it would - you've specifically used .## which means "only include digits if they're significant". If you want to always have at least one decimal place, use
DecimalFormat df = new DecimalFormat("#,###.0#");
If you always want to have two decimal places, use:
DecimalFormat df = new DecimalFormat("#,###.00");
You should probably consider how you want 0.5 to be formatted, too. Do you want "0.5" or ".5"?
'#' means include this digit if it matters and since 9,0 == 9, the formatting removes it.
If you need a minimum of two digits after decimal then you should replace to format as:
DecimalFormat df = new DecimalFormat("#,###.00");