Related
I want to override getText() of EditText.
I receive this kind of String: "12,345,678"
My purpose is to just remove the commas and return the Editable but when with my code I get an error.
public class AmountEditText extends EditText {
#Override
public Editable getText() {
Editable s = super.getText();
if(s!=null && s.length()>0) {
if (s.toString().contains(",")) {
return new SpannableStringBuilder(s.toString().replace(",", ""));
}
}
return s;
}
private TextWatcher watcher = 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) {
int position = getSelectionStart();
int nbCommaBefore;
int nbCommaAfter;
String str = s.toString();
String finalStr;
String formattedStr;
nbCommaBefore = str.length() - str.replace(",", "").length();
boolean containsDot = false;
if (str.contains(".")) {
containsDot = true;
formattedStr = str.split("\\.")[0];
} else {
formattedStr = str;
}
if (!s.toString().isEmpty()) {
removeTextChangedListener(watcher);
formattedStr = formattedStr.replace(",", "");
formattedStr = formattedStr.replaceAll("(\\d)(?=(\\d{3})+$)", "$1,");
if (containsDot) {
if (str.split("\\.").length != 1) {
finalStr = formattedStr + "." + str.split("\\.")[1].replace(",", "");
} else {
finalStr = formattedStr + ".";
}
} else {
finalStr = formattedStr;
}
nbCommaAfter = finalStr.length() - finalStr.replace(",", "").length();
setText(finalStr);
if (position == str.length()){
setSelection(finalStr.length());
}
else if (position == 0)
{
setSelection(0);
}
else if (nbCommaBefore < nbCommaAfter){
setSelection(position + 1);
}
else if (nbCommaAfter < nbCommaBefore){
setSelection(position - 1);
}
else{
setSelection(position);
}
addTextChangedListener(watcher);
}
}
#Override
public void afterTextChanged(Editable s) {
}
};
public AmountEditText(Context context) {
this(context, null);
}
public AmountEditText(Context context, AttributeSet attrs) {
super(context, attrs);
addTextChangedListener(watcher);
}
public AmountEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
addTextChangedListener(watcher);
}
}
E/MessageQueue-JNI: Exception in MessageQueue callback:
handleReceiveCallback E/MessageQueue-JNI:
java.lang.IndexOutOfBoundsException: setSpan (0 ... 5) ends beyond
length 4
at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1265)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:684)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:677)
at android.widget.SpellChecker$SpellParser.setRangeSpan(SpellChecker.java:532)
at android.widget.SpellChecker$SpellParser.parse(SpellChecker.java:515)
at android.widget.SpellChecker.spellCheck(SpellChecker.java:242)
at android.widget.Editor.updateSpellCheckSpans(Editor.java:679)
at android.widget.Editor.sendOnTextChanged(Editor.java:1249)
at android.widget.TextView.sendOnTextChanged(TextView.java:8191)
at android.widget.TextView.setText(TextView.java:4483)
at android.widget.TextView.setText(TextView.java:4337)
at android.widget.EditText.setText(EditText.java:89)
at android.widget.TextView.setText(TextView.java:4312)
at org.newtonproject.newpay.widgetlib.AmountEditText$1.onTextChanged(AmountEditText.java:74)
I would like to precise that the error doesn't come from my onTextChanged
because everything works well without the getText() override
EDIT : The user can enter number, I will append some commas in order to format the number. But when I override getText() I want to delete these commas in that way I don't have to filter the return of getText() everytime
Ok, I debugged that and found out that the problem was on that line
if (position == str.length()){
setSelection(finalStr.length());
}
lenght() is out of bound for a set selection, since it's 0 based
just change your code with that and it will work properly
if (position == str.length()){
setSelection(finalStr.length() - 1);
}
If needed, full code here (I used AppCompatEditText, but it's the same):
public class AmountEditText extends android.support.v7.widget.AppCompatEditText {
#Override
public Editable getText() {
Editable s = super.getText();
if(s!=null && s.length()>0) {
if (s.toString().contains(",")) {
return new SpannableStringBuilder(s.toString().replace(",", ""));
}
}
return s;
}
private TextWatcher watcher = 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) {
int position = getSelectionStart();
int nbCommaBefore;
int nbCommaAfter;
String str = s.toString();
String finalStr;
String formattedStr;
nbCommaBefore = str.length() - str.replace(",", "").length();
boolean containsDot = false;
if (str.contains(".")) {
containsDot = true;
formattedStr = str.split("\\.")[0];
} else {
formattedStr = str;
}
if (!s.toString().isEmpty()) {
removeTextChangedListener(watcher);
formattedStr = formattedStr.replace(",", "");
formattedStr = formattedStr.replaceAll("(\\d)(?=(\\d{3})+$)", "$1,");
if (containsDot) {
if (str.split("\\.").length != 1) {
finalStr = formattedStr + "." + str.split("\\.")[1].replace(",", "");
} else {
finalStr = formattedStr + ".";
}
} else {
finalStr = formattedStr;
}
nbCommaAfter = finalStr.length() - finalStr.replace(",", "").length();
setText(finalStr);
if (position == str.length()){
setSelection(finalStr.length() - 1);
}
else if (position == 0)
{
setSelection(0);
}
else if (nbCommaBefore < nbCommaAfter){
setSelection(position + 1);
}
else if (nbCommaAfter < nbCommaBefore){
setSelection(position - 1);
}
else{
setSelection(position);
}
addTextChangedListener(watcher);
}
}
#Override
public void afterTextChanged(Editable s) {
}
};
public AmountEditText(Context context) {
this(context, null);
}
public AmountEditText(Context context, AttributeSet attrs) {
super(context, attrs);
addTextChangedListener(watcher);
}
public AmountEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
addTextChangedListener(watcher);
}
}
Let me know if that helped!
In your case, you can not override getText() and resize and using TextWatcher at same time.
Check the android source code below and you will why
SpannableStringBuilder.java
public void setSpan(Object what, int start, int end, int flags) {
setSpan(true, what, start, end, flags, true/*enforceParagraph*/);
}
private void setSpan(boolean send, Object what, int start, int end, int flags,
boolean enforceParagraph) {
checkRange("setSpan", start, end);
}
private void checkRange(final String operation, int start, int end) {
...
int len = length();
if (start > len || end > len) {
throw new IndexOutOfBoundsException(operation + " " +
region(start, end) + " ends beyond length " + len); // here is you exception
}
}
public int length() {
return mText.length - mGapLength;
}
SpellChecker.java
private void setRangeSpan(Editable editable, int start, int end) {
...
editable.setSpan(mRange, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
public void parse(int start, int end) {
...
if (parseEnd > start) {
setRangeSpan((Editable) mTextView.getText(), start, parseEnd); // I think the error happened from here, they use your getText() function here and receive shorter string, but the start, parseEnd still stick with original string
parse();
}
}
Solution .
You can simple defind a new function like getBeautifulText().
Try this one it will help's you
editText.addTextChangedListener(new TextWatcher() {
boolean isEdiging;
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void afterTextChanged(Editable s) {
if (isEdiging) return;
isEdiging = true;
String str = s.toString().replaceAll("[^\\d]", "");
double s1 = 0;
try {
s1 = Double.parseDouble(str);
} catch (NumberFormatException e) {
e.printStackTrace();
}
NumberFormat nf2 = NumberFormat.getInstance(Locale.ENGLISH);
((DecimalFormat) nf2).applyPattern("###,###.###");
s.replace(0, s.length(), nf2.format(s1));
if (s.toString().equals("0")) {
editText.setText("");
}
isEdiging = false;
}
});
Based on the requirements in your question:
The user can enter number, I will append some commas in order to
format the number. But when I override getText() I want to delete
these commas
I believe you could use a much simpler solution involving DecimalFormat:
class Formatter {
private final DecimalFormat f = new DecimalFormat(",###");
private final DecimalFormat o = new DecimalFormat("#");
String withCommas(String in) {
try {
return withCommas(Long.parseLong(in));
} catch (NumberFormatException e) {
e.printStackTrace();
return withCommas(Long.MIN_VALUE);
}
}
String withCommas(long in) {
return f.format(in);
}
Number stripCommas(String in) {
try {
return f.parse(in);
} catch (ParseException e) {
return Long.MIN_VALUE;
}
}
String stripCommasAsString(String in) {
return o.format(stripCommas(in));
}
}
Which gives:
final long num = 12345678L;
final Formatter f = new Formatter();
assertEquals("12,345,678", f.withCommas("12345678"));
assertEquals("12,345,678", f.withCommas(num));
assertEquals(num, f.stripCommas("12,345,678");
assertEquals("12345678", f.stripCommasAsString("12,345,678"));
I want to separate automatically edit text like (9,99,999) like this. I searched for this on the web but I have not found a proper solution for this.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText editText = (EditText) findViewById(R.id.edittext);
editText.addTextChangedListener(new NumberTextWatcherForThousand(editText)); NumberTextWatcherForThousand.trimCommaOfString(editText.getText().toString());
}
}
NumberTextWatcherForThousand
public class NumberTextWatcherForThousand implements TextWatcher {
EditText editText;
public NumberTextWatcherForThousand(EditText editText) {
this.editText = editText;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
try
{
editText.removeTextChangedListener(this);
String value = editText.getText().toString();
if (value != null && !value.equals(""))
{
if(value.startsWith(".")){
editText.setText("0.");
}
if(value.startsWith("0") && !value.startsWith("0.")){
editText.setText("");
}
String str = editText.getText().toString().replaceAll(",", "");
if (!value.equals(""))
editText.setText(getDecimalFormattedString(str));
editText.setSelection(editText.getText().toString().length());
}
editText.addTextChangedListener(this);
return;
}
catch (Exception ex)
{
ex.printStackTrace();
editText.addTextChangedListener(this);
}
}
public static String getDecimalFormattedString(String value)
{
StringTokenizer lst = new StringTokenizer(value, ".");
String str1 = value;
String str2 = "";
if (lst.countTokens() > 1)
{
str1 = lst.nextToken();
str2 = lst.nextToken();
}
String str3 = "";
int i = 0;
int j = -1 + str1.length();
if (str1.charAt( -1 + str1.length()) == '.')
{
j--;
str3 = ".";
}
for (int k = j;; k--)
{
if (k < 0)
{
if (str2.length() > 0)
str3 = str3 + "." + str2;
return str3;
}
if (i == 3)
{
str3 = "," + str3;
i = 0;
}
str3 = str1.charAt(k) + str3;
i++;
}
}
public static String trimCommaOfString(String string) {
if(string.contains(",")){
return string.replace(",","");}
else {
return string;
}
}
}
This will format the text and add commas in thousands place inside your edit text.
#Override
public void afterTextChanged(Editable editable) {
try {
// The comma in the format specifier does the trick
editText.setText(String.format("%,d", Long.parseLong(editable.toString())));
} catch (NumberFormatException e) {
}
}
try this below code :-
import java.text.DecimalFormat;
import java.text.ParseException;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
public class NumberTextWatcher implements TextWatcher {
private DecimalFormat df;
private DecimalFormat dfnd;
private boolean hasFractionalPart;
private EditText et;
public NumberTextWatcher(EditText et)
{
df = new DecimalFormat("#,###.##");
df.setDecimalSeparatorAlwaysShown(true);
dfnd = new DecimalFormat("#,###");
this.et = et;
hasFractionalPart = false;
}
#SuppressWarnings("unused")
private static final String TAG = "NumberTextWatcher";
#Override
public void afterTextChanged(Editable s)
{
et.removeTextChangedListener(this);
try {
int inilen, endlen;
inilen = et.getText().length();
String v = s.toString().replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
Number n = df.parse(v);
int cp = et.getSelectionStart();
if (hasFractionalPart) {
et.setText(df.format(n));
} else {
et.setText(dfnd.format(n));
}
endlen = et.getText().length();
int sel = (cp + (endlen - inilen));
if (sel > 0 && sel <= et.getText().length()) {
et.setSelection(sel);
} else {
// place cursor at the end?
et.setSelection(et.getText().length() - 1);
}
} catch (NumberFormatException nfe) {
// do nothing?
} catch (ParseException e) {
// do nothing?
}
et.addTextChangedListener(this);
}
#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 (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator())))
{
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
}
}
and in your edittext
editText.addTextChangedListener(new NumberTextWatcher(editText));
In build.gradle add following lines
repositories{
maven { url "https://jitpack.io" }
}
dependencies {
compile 'com.github.BlacKCaT27:CurrencyEditText:v1.4.4'
}
Instead of EditText use following Code
<com.blackcat.currencyedittext.CurrencyEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
check this link https://github.com/BlacKCaT27/CurrencyEditText
I want to separate automatically edit text like (9,99,999) like this. i searched for this on web but i am not getting proper solution for this.
can you please help me.thank you stack overflow.
You can use DecimalFormat for this like below Code:
public String formatNumber(double d) {
DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
formatter.applyPattern("#,###");
return formatter.format(d);
}
You Can Pass Pattern as you want.
public static String formatCurrency(String number) {
try {
number = NumberFormat.getNumberInstance(Locale.US).format(Double.valueOf(number));
} catch (Exception e) {
}
return number;
}
This is what i did. Works perfectly
Try this.
public class NumberTextWatcherForThousand implements TextWatcher {
EditText editText;
public NumberTextWatcherForThousand(EditText editText) {
this.editText = editText;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
try
{
editText.removeTextChangedListener(this);
String value = editText.getText().toString();
if (value != null && !value.equals(""))
{
if(value.startsWith(".")){ //adds "0." when only "." is pressed on begining of writting
editText.setText("0.");
}
if(value.startsWith("0") && !value.startsWith("0.")){
editText.setText(""); //Prevents "0" while starting but not "0."
}
String str = editText.getText().toString().replaceAll(",", "");
if (!value.equals(""))
editText.setText(getDecimalFormat(str));
editText.setSelection(editText.getText().toString().length());
}
editText.addTextChangedListener(this);
return;
}
catch (Exception ex)
{
ex.printStackTrace();
editText.addTextChangedListener(this);
}
}
public static String getDecimalFormat(String value)
{
StringTokenizer lst = new StringTokenizer(value, ".");
String str1 = value;
String str2 = "";
if (lst.countTokens() > 1)
{
str1 = lst.nextToken();
str2 = lst.nextToken();
}
String str3 = "";
int i = 0;
int j = -1 + str1.length();
if (str1.charAt( -1 + str1.length()) == '.')
{
j--;
str3 = ".";
}
for (int k = j;; k--)
{
if (k < 0)
{
if (str2.length() > 0)
str3 = str3 + "." + str2;
return str3;
}
if (i == 3)
{
str3 = "," + str3;
i = 0;
}
str3 = str1.charAt(k) + str3;
i++;
}
}
//Trims all the comma of the string and returns
public static String trimCommaOfString(String string) {
if(string.contains(",")){
return string.replace(",","");}
else {
return string;
}
}
}
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
editText.removeTextChangedListener(this);
try {
StringBuilder originalString = new StringBuilder(editable.toString().replaceAll(",", ""));
int indx = 0;
for (int i = originalString.length(); i > 0; i--) {
if (indx % 3 == 0 && indx > 0)
originalString = originalString.insert(i, ",");
indx++;
}
editText.setText(originalString);
editText.setSelection(originalString.length());
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
}
editText.addTextChangedListener(this);
}
});
I want to remove the dollar sign to my amount formatter but its not working I already assigned the replace it it contains an dollar sign. How can I do this?
Here is my code for Text Watcher.
NumberFormat canadaEnglish = NumberFormat.getCurrencyInstance(Locale.CANADA);
public class MoneyTextWatcher implements TextWatcher {
private final WeakReference<EditText> editTextWeakReference;
boolean hasFractionalPart = false;
private EditText editText;
public MoneyTextWatcher(EditText editText) {
editTextWeakReference = new WeakReference<EditText>(editText);
this.editText = editText;
}
#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 (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator()))) {
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
if (mIsInWatcher)
return;
mIsInWatcher = true;
}
#Override
public void afterTextChanged(Editable editable) {
if (editText == null)
return;
String s = editable.toString();
editText.removeTextChangedListener(this);
String cleanString = s.toString().replaceAll("[$,.]", "");
String formatted = "";
System.out.println(cleanString);
BigDecimal parsed = new BigDecimal(cleanString).setScale(2,BigDecimal.ROUND_FLOOR).divide(new BigDecimal(100),BigDecimal.ROUND_FLOOR);
formatted = canadaEnglish.format(parsed).replace("\\$",pesoCurrency);
System.out.println("formatted > " + formatted);
String trimFormatted = formatted.replace("\\$", pesoCurrency);
System.out.println("trimFormatted > " + trimFormatted);
editText.setText(formatted.replace("\\$", ""));
editText.setSelection(formatted.length());
editText.addTextChangedListener(this);
}
}
Try this:
String charToReplace = new String[]{"[","$",",",".","]"};
for(String s: charToReplace)
cleanString = cleanString .replace(s,"");
Here is my MoneyTextWatcher.. Thanks for the response DroidWorm.. i just needed to used replaceAll(args.., args..) in assigning text from my edittext not replace(args.., args..)
public static class MoneyTextWatcher implements TextWatcher {
private EditText editText;
public MoneyTextWatcher(EditText editText) {
this.editText = editText;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
#Override
public void afterTextChanged(Editable editable) {
String formatted = "";
if (editText == null)
return;
String s = editable.toString();
editText.removeTextChangedListener(this);
String cleanString = s.toString().replaceAll("[\u20B1,]", "");
System.out.println("cleanString > " + cleanString);
BigDecimal parsed = new BigDecimal(cleanString.equals("") ? "0.00"
: cleanString).setScale(2, BigDecimal.ROUND_FLOOR).divide(
new BigDecimal(100), BigDecimal.ROUND_FLOOR);
formatted = canadaEnglish.format(parsed)
.replaceAll("\\$", CURRENCY);
//System.out.println("formatted > " + formatted);
editText.setText(formatted.replace("\\$", ""));
editText.setSelection(formatted.length());
editText.addTextChangedListener(this);
}
}
I have an EditText in which the user should input a number including decimals and i want a thousand separator automatically added onto the input number I tried a couple of other methods but some do not allow floating point numbers so i came up with this code which works well only that the string input is not being edited in realtime to one with possible thousand separators and the errors seem to stem from the s.replace();
am2 = new TextWatcher(){
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
public void afterTextChanged(Editable s) {
if (s.toString().equals("")) {
amount.setText("");
value = 0;
}else{
StringBuffer strBuff = new StringBuffer();
char c;
for (int i = 0; i < amount2.getText().toString().length() ; i++) {
c = amount2.getText().toString().charAt(i);
if (Character.isDigit(c)) {
strBuff.append(c);
}
}
value = Double.parseDouble(strBuff.toString());
reverse();
NumberFormat nf2 = NumberFormat.getInstance(Locale.ENGLISH);
((DecimalFormat)nf2).applyPattern("###,###.#######");
s.replace(0, s.length(), nf2.format(value));
}
}
};
This Class solves the problem, allows decimal input and adds the thousand separators.
public class NumberTextWatcher implements TextWatcher {
private DecimalFormat df;
private DecimalFormat dfnd;
private boolean hasFractionalPart;
private EditText et;
public NumberTextWatcher(EditText et)
{
df = new DecimalFormat("#,###.##");
df.setDecimalSeparatorAlwaysShown(true);
dfnd = new DecimalFormat("#,###");
this.et = et;
hasFractionalPart = false;
}
#SuppressWarnings("unused")
private static final String TAG = "NumberTextWatcher";
public void afterTextChanged(Editable s)
{
et.removeTextChangedListener(this);
try {
int inilen, endlen;
inilen = et.getText().length();
String v = s.toString().replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
Number n = df.parse(v);
int cp = et.getSelectionStart();
if (hasFractionalPart) {
et.setText(df.format(n));
} else {
et.setText(dfnd.format(n));
}
endlen = et.getText().length();
int sel = (cp + (endlen - inilen));
if (sel > 0 && sel <= et.getText().length()) {
et.setSelection(sel);
} else {
// place cursor at the end?
et.setSelection(et.getText().length() - 1);
}
} catch (NumberFormatException nfe) {
// do nothing?
} catch (ParseException e) {
// do nothing?
}
et.addTextChangedListener(this);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
public void onTextChanged(CharSequence s, int start, int before, int count)
{
if (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator())))
{
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
}
}
Source: http://blog.roshka.com/2012/08/android-edittext-with-number-format.html
Unfortunately the code did not work as it is in the answer.
It has two problems:
It does not work if the phone locale configuration uses "," as a decimal separator.
It does not work if the number has trailing zeros in the decimal part. Example 1.01.
I went crazy to fix it.
Finally I came to this code that worked well on my cell phone:
NumberTextWatcher.java
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
import android.util.Log;
import android.widget.EditText;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;
public class NumberTextWatcher
implements TextWatcher {
private static final String TAG = "NumberTextWatcher";
private final int numDecimals;
private String groupingSep;
private String decimalSep;
private boolean nonUsFormat;
private DecimalFormat df;
private DecimalFormat dfnd;
private boolean hasFractionalPart;
private EditText et;
private String value;
private String replicate(char ch, int n) {
return new String(new char[n]).replace("\0", "" + ch);
}
public NumberTextWatcher(EditText et, Locale locale, int numDecimals) {
et.setKeyListener(DigitsKeyListener.getInstance("0123456789.,"));
this.numDecimals = numDecimals;
DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
char gs = symbols.getGroupingSeparator();
char ds = symbols.getDecimalSeparator();
groupingSep = String.valueOf(gs);
decimalSep = String.valueOf(ds);
String patternInt = "#,###";
dfnd = new DecimalFormat(patternInt, symbols);
String patternDec = patternInt + "." + replicate('#', numDecimals);
df = new DecimalFormat(patternDec, symbols);
df.setDecimalSeparatorAlwaysShown(true);
df.setRoundingMode(RoundingMode.DOWN);
this.et = et;
hasFractionalPart = false;
nonUsFormat = !decimalSep.equals(".");
value = null;
}
#Override
public void afterTextChanged(Editable s) {
Log.d(TAG, "afterTextChanged");
et.removeTextChangedListener(this);
try {
int inilen, endlen;
inilen = et.getText().length();
String v = value.replace(groupingSep, "");
Number n = df.parse(v);
int cp = et.getSelectionStart();
if (hasFractionalPart) {
int decPos = v.indexOf(decimalSep) + 1;
int decLen = v.length() - decPos;
if (decLen > numDecimals) {
v = v.substring(0, decPos + numDecimals);
}
int trz = countTrailingZeros(v);
StringBuilder fmt = new StringBuilder(df.format(n));
while (trz-- > 0) {
fmt.append("0");
}
et.setText(fmt.toString());
} else {
et.setText(dfnd.format(n));
}
endlen = et.getText().length();
int sel = (cp + (endlen - inilen));
if (sel > 0 && sel <= et.getText().length()) {
et.setSelection(sel);
} else {
// place cursor at the end?
et.setSelection(et.getText().length() - 1);
}
} catch (NumberFormatException | ParseException nfe) {
// do nothing?
}
et.addTextChangedListener(this);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
Log.d(TAG, "beforeTextChanged");
value = et.getText().toString();
}
private int countTrailingZeros(String str) {
int count = 0;
for (int i = str.length() - 1; i >= 0; i--) {
char ch = str.charAt(i);
if ('0' == ch) {
count++;
} else {
break;
}
}
return count;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d(TAG, "onTextChanged");
String newValue = s.toString();
String change = newValue.substring(start, start + count);
String prefix = value.substring(0, start);
String suffix = value.substring(start + before);
if (".".equals(change) && nonUsFormat) {
change = decimalSep;
}
value = prefix + change + suffix;
hasFractionalPart = value.contains(decimalSep);
Log.d(TAG, "VALUE: " + value);
}
}
and then to use it simply to do:
Locale locale = new Locale("es", "AR"); // For example Argentina
int numDecs = 2; // Let's use 2 decimals
TextWatcher tw = new NumberTextWatcher(myEditText, locale, numDecs);
myEditText.addTextChangedListener(tw);
You need to use DecimalFormat class with DecimalFormatSymbols class, check the out following method,
public static String formatAmount(int num)
{
DecimalFormat decimalFormat = new DecimalFormat();
DecimalFormatSymbols decimalFormateSymbol = new DecimalFormatSymbols();
decimalFormateSymbol.setGroupingSeparator(',');
decimalFormat.setDecimalFormatSymbols(decimalFormateSymbol);
return decimalFormat.format(num);
}
you can use kotlin extensions function like this...
fun EditText.onCommaChange(input: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
if (!edit) {
edit = true
if (s.toString() != "₹") {
try {
val flNumber = getCommaLessNumber(s.toString()).toInt()
val fNumber = getFormattedAmount(flNumber)
setText(fNumber)
setSelection(text.length)
input(flNumber.toString())
} catch (e: NumberFormatException) {
Timber.e(e)
}
} else {
setText("")
input("")
}
edit = false
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})}
fun getCommaLessNumber(commaNumber: String): String {
var number = commaNumber.replace("₹", "")
number = number.replace(",".toRegex(), "")
return number}
fun getFormattedAmount(amount: Int): String {
return "₹${String.format("%,d", amount)}"}
fun EditText.text() = this.text.toString()
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import java.text.DecimalFormat;
public class MyNumberWatcher_3Digit implements TextWatcher {
private EditText editText;
private int digit;
public MyNumberWatcher_3Digit(EditText editText) {
this.editText = editText;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
editText.removeTextChangedListener( this );
String s = editText.getText().toString();
s = s.replace( ",", "" ).replace( "٬", "" );
s = replaceNonstandardDigits( s );
if (s.length() > 0) {
DecimalFormat sdd = new DecimalFormat( "#,###" );
Double doubleNumber = Double.parseDouble( s );
String format = sdd.format( doubleNumber );
editText.setText( format );
editText.setSelection( format.length() );
}
editText.addTextChangedListener( this );
}
static String replaceNonstandardDigits(String input) {
if (input == null || input.isEmpty()) {
return input;
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
char ch = input.charAt( i );
if (isNonstandardDigit( ch )) {
int numericValue = Character.getNumericValue( ch );
if (numericValue >= 0) {
builder.append( numericValue );
}
} else {
builder.append( ch );
}
}
return builder.toString();
}
private static boolean isNonstandardDigit(char ch) {
return Character.isDigit( ch ) && !(ch >= '0' && ch <= '9');
}
}
// oncreate activity
input_text_rate.addTextChangedListener(new MyNumberWatcher_3Digit(input_text_rate));
I used this way in Kotlin for a Dialog:
val et = dialog.findViewById(R.id.etNumber) as EditText
et.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
et.removeTextChangedListener(this)
forChanged(et)
et.addTextChangedListener(this)
}
override fun beforeTextChanged(
s: CharSequence,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
}
})
then write a method like this:
private fun forChanged(alpha: EditText) {
val string = alpha.text.toString()
val dec = DecimalFormat("#,###")
if (!TextUtils.isEmpty(string)) {
val textWC = string.replace(",".toRegex(), "")
val number = textWC.toDouble()
alpha.setText(dec.format(number))
alpha.setSelection(dec.format(number).length)
}
}
I have tried solutions but with ending 0 i was having problem, sometimes user just wanted to enter 0.01 or 0.0001,
I don't know if any other have posted same answer or not but if this helps,
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;
import java.text.DecimalFormat;
public class NumberTextWatcher implements TextWatcher {
private DecimalFormat dfnd;
private boolean hasFractionalPart;
private EditText inputView;
public static void bindView(EditText inputView) {
NumberTextWatcher temp = new NumberTextWatcher(inputView);
inputView.addTextChangedListener(temp);
}
public NumberTextWatcher(EditText inputView) {
dfnd = new DecimalFormat("#,###.######");
this.inputView = inputView;
hasFractionalPart = false;
}
#SuppressWarnings("unused")
private static final String TAG = "NumberTextWatcher";
public void afterTextChanged(Editable s) {
Log.d(TAG, "afterTextChanged() called with: s = [" + s + "]");
inputView.removeTextChangedListener(this);
try {
String text = inputView.getText().toString().replace(String.valueOf(dfnd.getDecimalFormatSymbols().getGroupingSeparator()), "");
if(text.charAt(text.length() - 1) == '.')
{
if(getCount(text,'.') >1)
{
text = text.substring(0,text.length()-1);
}
}
String afterDecimalPoint = "";
String beforeDecimalPoint = text;
if (hasFractionalPart || (text.charAt(text.length() - 1) == '0')) {
String[] data = text.split("\\.");
beforeDecimalPoint = data[0];
if (data.length != 2) {
afterDecimalPoint = ".";
} else {
afterDecimalPoint = "." + data[1];
if (data[1].length() >= dfnd.getMaximumFractionDigits()) {
afterDecimalPoint = "." + data[1].substring(0, dfnd.getMaximumFractionDigits());
}
}
}
beforeDecimalPoint = dfnd.format(Double.parseDouble(beforeDecimalPoint));
String finalText = beforeDecimalPoint;
if (hasFractionalPart) {
finalText = beforeDecimalPoint + afterDecimalPoint;
}
inputView.setText(finalText);
inputView.setSelection(finalText.length());
} catch (Exception nfe) {
// do nothing?
nfe.printStackTrace();
}
inputView.addTextChangedListener(this);
}
private int getCount(String someString, char someChar) {
int count = 0;
for (int i = 0; i < someString.length(); i++) {
if (someString.charAt(i) == someChar) {
count++;
}
}
return count;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().contains(String.valueOf(dfnd.getDecimalFormatSymbols().getDecimalSeparator()))) {
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
}
}