Android 设置货币输入文本格式的更好方法?
我有一个editText,起始值是$0.00。按1时,它将更改为$0.01。按4,它会涨到0.14美元。按8,1.48美元。按backspace,$0.14等 如果是这样的话,问题是,如果有人手动定位光标,格式就会出现问题。如果他们删除小数点,它就不会回来了。如果他们将光标放在小数点前面并键入2,它将显示$02.00而不是$2.00。例如,如果他们试图删除$,它将删除一个数字 这是我正在使用的代码,如果有任何建议,我将不胜感激Android 设置货币输入文本格式的更好方法?,android,Android,我有一个editText,起始值是$0.00。按1时,它将更改为$0.01。按4,它会涨到0.14美元。按8,1.48美元。按backspace,$0.14等 如果是这样的话,问题是,如果有人手动定位光标,格式就会出现问题。如果他们删除小数点,它就不会回来了。如果他们将光标放在小数点前面并键入2,它将显示$02.00而不是$2.00。例如,如果他们试图删除$,它将删除一个数字 这是我正在使用的代码,如果有任何建议,我将不胜感激 mEditPrice.setRawInputType(Configu
mEditPrice.setRawInputType(Configuration.KEYBOARD_12KEY);
public void priceClick(View view) {
mEditPrice.addTextChangedListener(new TextWatcher(){
DecimalFormat dec = new DecimalFormat("0.00");
@Override
public void afterTextChanged(Editable arg0) {
}
@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().matches("^\\$(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$"))
{
String userInput= ""+s.toString().replaceAll("[^\\d]", "");
if (userInput.length() > 0) {
Float in=Float.parseFloat(userInput);
float percen = in/100;
mEditPrice.setText("$"+dec.format(percen));
mEditPrice.setSelection(mEditPrice.getText().length());
}
}
}
});
我测试了你的方法,但当我使用大量数字时,它失败了。。。我创造了这个:
private String current = "";
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(!s.toString().equals(current)){
[your_edittext].removeTextChangedListener(this);
String cleanString = s.toString().replaceAll("[$,.]", "");
double parsed = Double.parseDouble(cleanString);
String formatted = NumberFormat.getCurrencyInstance().format((parsed/100));
current = formatted;
[your_edittext].setText(formatted);
[your_edittext].setSelection(formatted.length());
[your_edittext].addTextChangedListener(this);
}
}
Kotlin变体:
private var current: String = ""
override fun onTextChanged(
s: CharSequence,
start: Int,
before: Int,
count: Int
) {
if (s.toString() != current) {
discount_amount_edit_text.removeTextChangedListener(this)
val cleanString: String = s.replace("""[$,.]""".toRegex(), "")
val parsed = cleanString.toDouble()
val formatted = NumberFormat.getCurrencyInstance().format((parsed / 100))
current = formatted
discount_amount_edit_text.setText(formatted)
discount_amount_edit_text.setSelection(formatted.length)
discount_amount_edit_text.addTextChangedListener(this)
}
}
实际上,以前提供的解决方案不起作用。如果要输入100.00,则该选项无效 替换:
double parsed = Double.parseDouble(cleanString);
String formato = NumberFormat.getCurrencyInstance().format((parsed/100));
与:
我必须说我对代码做了一些修改。问题是你应该使用BigDecimal的对我来说,它是这样工作的
public void onTextChanged(CharSequence s, int start,
int before, int count) {
if(!s.toString().matches("^\\$(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$"))
{
String userInput= ""+s.toString().replaceAll("[^\\d]", "");
if (userInput.length() > 2) {
Float in=Float.parseFloat(userInput);
price = Math.round(in); // just to get an Integer
//float percen = in/100;
String first, last;
first = userInput.substring(0, userInput.length()-2);
last = userInput.substring(userInput.length()-2);
edEx1.setText("$"+first+"."+last);
Log.e(MainActivity.class.toString(), "first: "+first + " last:"+last);
edEx1.setSelection(edEx1.getText().length());
}
}
}
最好使用InputFilter接口。使用正则表达式处理任何类型的输入都要容易得多。我的货币输入格式解决方案:
public class CurrencyFormatInputFilter implements InputFilter {
Pattern mPattern = Pattern.compile("(0|[1-9]+[0-9]*)(\\.[0-9]{1,2})?");
@Override
public CharSequence filter(
CharSequence source,
int start,
int end,
Spanned dest,
int dstart,
int dend) {
String result =
dest.subSequence(0, dstart)
+ source.toString()
+ dest.subSequence(dend, dest.length());
Matcher matcher = mPattern.matcher(result);
if (!matcher.matches()) return dest.subSequence(dstart, dend);
return null;
}
}
有效期:0.00,0.0,10.00,111.1无效:0,0.000,111,1010.00,01.0
如何使用:
editText.setFilters(new InputFilter[] {new CurrencyFormatInputFilter()});
好的,这里有一个更好的方法来处理货币格式,删除向后击键。 该代码基于上面的@AndroidGuillive'代码。。。 但是,处理一些与向后删除和一些解析异常相关的问题: [更新]以前的解决方案存在一些问题。。。 这是一个更好的解决方案: 而且。。。详情如下: 这种方法更好,因为它使用传统的Android机制。 其思想是在用户存在视图后格式化值 定义一个InputFilter来限制数值–这在大多数情况下是必需的,因为屏幕不够大,无法容纳长文本视图。 这可以是静态内部类,也可以只是另一个普通类:
/** Numeric range Filter. */
class NumericRangeFilter implements InputFilter {
/** Maximum value. */
private final double maximum;
/** Minimum value. */
private final double minimum;
/** Creates a new filter between 0.00 and 999,999.99. */
NumericRangeFilter() {
this(0.00, 999999.99);
}
/** Creates a new filter.
* @param p_min Minimum value.
* @param p_max Maximum value.
*/
NumericRangeFilter(double p_min, double p_max) {
maximum = p_max;
minimum = p_min;
}
@Override
public CharSequence filter(
CharSequence p_source, int p_start,
int p_end, Spanned p_dest, int p_dstart, int p_dend
) {
try {
String v_valueStr = p_dest.toString().concat(p_source.toString());
double v_value = Double.parseDouble(v_valueStr);
if (v_value<=maximum && v_value>=minimum) {
// Returning null will make the EditText to accept more values.
return null;
}
} catch (NumberFormatException p_ex) {
// do nothing
}
// Value is out of range - return empty string.
return "";
}
}
此类将在编辑时删除货币格式-依赖于标准机制。
当用户退出时,将重新应用货币格式
最好定义一些静态变量以最小化实例数:
static final InputFilter[] FILTERS = new InputFilter[] {new NumericRangeFilter()};
static final View.OnFocusChangeListener ON_FOCUS = new AmountOnFocusChangeListener();
最后,在onCreateView(…)中:
您可以重用过滤器,并关注任意数量的EditText视图
以下是Utils类:
public class Utils {
private static final NumberFormat FORMAT_CURRENCY = NumberFormat.getCurrencyInstance();
/** Parses an amount into cents.
* @param p_value Amount formatted using the default currency.
* @return Value as cents.
*/
public static int parseAmountToCents(String p_value) {
try {
Number v_value = FORMAT_CURRENCY.parse(p_value);
BigDecimal v_bigDec = new BigDecimal(v_value.doubleValue());
v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP);
return v_bigDec.movePointRight(2).intValue();
} catch (ParseException p_ex) {
try {
// p_value doesn't have a currency format.
BigDecimal v_bigDec = new BigDecimal(p_value);
v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP);
return v_bigDec.movePointRight(2).intValue();
} catch (NumberFormatException p_ex1) {
return -1;
}
}
}
/** Formats cents into a valid amount using the default currency.
* @param p_value Value as cents
* @return Amount formatted using a currency.
*/
public static String formatCentsToAmount(int p_value) {
BigDecimal v_bigDec = new BigDecimal(p_value);
v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP);
v_bigDec = v_bigDec.movePointLeft(2);
String v_currency = FORMAT_CURRENCY.format(v_bigDec.doubleValue());
return v_currency.replace(FORMAT_CURRENCY.getCurrency().getSymbol(), "").replace(",", "");
}
/** Formats cents into a valid amount using the default currency.
* @param p_value Value as cents
* @return Amount formatted using a currency.
*/
public static String formatCentsToCurrency(int p_value) {
BigDecimal v_bigDec = new BigDecimal(p_value);
v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP);
v_bigDec = v_bigDec.movePointLeft(2);
return FORMAT_CURRENCY.format(v_bigDec.doubleValue());
}
}
我建立在Guilhermes answer的基础上,但我保留了光标的位置,并以不同的方式对待句点-这样,如果用户在句点之后键入,它不会影响句点之前的数字,我发现这提供了一个非常平滑的输入
[yourtextfield].addTextChangedListener(new TextWatcher()
{
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
private String current = "";
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
if(!s.toString().equals(current))
{
[yourtextfield].removeTextChangedListener(this);
int selection = [yourtextfield].getSelectionStart();
// We strip off the currency symbol
String replaceable = String.format("[%s,\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
String cleanString = s.toString().replaceAll(replaceable, "");
double price;
// Parse the string
try
{
price = Double.parseDouble(cleanString);
}
catch(java.lang.NumberFormatException e)
{
price = 0;
}
// If we don't see a decimal, then the user must have deleted it.
// In that case, the number must be divided by 100, otherwise 1
int shrink = 1;
if(!(s.toString().contains(".")))
{
shrink = 100;
}
// Reformat the number
String formated = currencyFormat.format((price / shrink));
current = formated;
[yourtextfield].setText(formated);
[yourtextfield].setSelection(Math.min(selection, [yourtextfield].getText().length()));
[yourtextfield].addTextChangedListener(this);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override
public void afterTextChanged(Editable s)
{
}
});
基于上述一些答案,我创建了一个MoneyTextWatcher,您可以使用它,如下所示:
priceEditText.addTextChangedListener(new MoneyTextWatcher(priceEditText));
下面是课程:
public class MoneyTextWatcher implements TextWatcher {
private final WeakReference<EditText> editTextWeakReference;
public MoneyTextWatcher(EditText editText) {
editTextWeakReference = new WeakReference<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) {
EditText editText = editTextWeakReference.get();
if (editText == null) return;
String s = editable.toString();
if (s.isEmpty()) return;
editText.removeTextChangedListener(this);
String cleanString = s.replaceAll("[$,.]", "");
BigDecimal parsed = new BigDecimal(cleanString).setScale(2, BigDecimal.ROUND_FLOOR).divide(new BigDecimal(100), BigDecimal.ROUND_FLOOR);
String formatted = NumberFormat.getCurrencyInstance().format(parsed);
editText.setText(formatted);
editText.setSelection(formatted.length());
editText.addTextChangedListener(this);
}
}
公共类MoneyTextWatcher实现TextWatcher{
私有最终WeakReference editTextWeakReference;
public MoneyTextWatcher(编辑文本){
editTextWeakReference=新的WeakReference(editText);
}
@凌驾
更改前文本之前的公共void(字符序列s、int start、int count、int after){
}
@凌驾
public void onTextChanged(字符序列、int start、int before、int count){
}
@凌驾
public void PostTextChanged(可编辑){
EditText=editTextWeakReference.get();
if(editText==null)返回;
字符串s=可编辑的.toString();
如果(s.isEmpty())返回;
removeTextChangedListener(此);
字符串cleanString=s.replaceAll(“[$,.]”,“”);
解析的BigDecimal=新的BigDecimal(cleanString).setScale(2,BigDecimal.ROUND_FLOOR).除法(新的BigDecimal(100),BigDecimal.ROUND_FLOOR);
String formatted=NumberFormat.getCurrencyInstance().format(已解析);
editText.setText(格式化);
editText.setSelection(格式化的.length());
editText.addTextChangedListener(此);
}
}
尽管这里有很多答案,但我还是想分享我在其中找到的这段代码,因为我相信这是最可靠、最清晰的答案
class CurrencyTextWatcher implements TextWatcher {
boolean mEditing;
public CurrencyTextWatcher() {
mEditing = false;
}
public synchronized void afterTextChanged(Editable s) {
if(!mEditing) {
mEditing = true;
String digits = s.toString().replaceAll("\\D", "");
NumberFormat nf = NumberFormat.getCurrencyInstance();
try{
String formatted = nf.format(Double.parseDouble(digits)/100);
s.replace(0, s.length(), formatted);
} catch (NumberFormatException nfe) {
s.clear();
}
mEditing = false;
}
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
public void onTextChanged(CharSequence s, int start, int before, int count) { }
}
希望有帮助。我使用它允许用户输入货币,并将其从字符串转换为int,存储在db中,然后再次从int转换回string 我从中获得了这个,并将其更改为符合葡萄牙货币格式
import java.text.NumberFormat;
import java.util.Currency;
import java.util.Locale;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
public class CurrencyTextWatcher implements TextWatcher {
private String current = "";
private int index;
private boolean deletingDecimalPoint;
private final EditText currency;
public CurrencyTextWatcher(EditText p_currency) {
currency = p_currency;
}
@Override
public void beforeTextChanged(CharSequence p_s, int p_start, int p_count, int p_after) {
if (p_after>0) {
index = p_s.length() - p_start;
} else {
index = p_s.length() - p_start - 1;
}
if (p_count>0 && p_s.charAt(p_start)==',') {
deletingDecimalPoint = true;
} else {
deletingDecimalPoint = false;
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable p_s) {
if(!p_s.toString().equals(current)){
currency.removeTextChangedListener(this);
if (deletingDecimalPoint) {
p_s.delete(p_s.length()-index-1, p_s.length()-index);
}
// Currency char may be retrieved from NumberFormat.getCurrencyInstance()
String v_text = p_s.toString().replace("€","").replace(",", "");
v_text = v_text.replaceAll("\\s", "");
double v_value = 0;
if (v_text!=null && v_text.length()>0) {
v_value = Double.parseDouble(v_text);
}
// Currency instance may be retrieved from a static member.
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(new Locale("pt", "PT"));
String v_formattedValue = numberFormat.format((v_value/100));
current = v_formattedValue;
currency.setText(v_formattedValue);
if (index>v_formattedValue.length()) {
currency.setSelection(v_formattedValue.length());
} else {
currency.setSelection(v_formattedValue.length()-index);
}
// include here anything you may want to do after the formatting is completed.
currency.addTextChangedListener(this);
}
}
}
layout.xml
<EditText
android:id="@+id/edit_text_your_id"
...
android:text="0,00 €"
android:inputType="numberDecimal"
android:digits="0123456789" />
如果您的json货币字段是数字类型(而不是字符串),那么它可能是3.1、3.15或仅仅是3。因为json会自动对数字字段进行舍入
在这种情况下,您可能需要对其进行四舍五入以正确显示(并在以后能够在输入字段上使用掩码):
为什么需要这样做
所有答案都指向在输入judding时删除货币符号,因为您收到了美分,因此形成dolar+cents/100=dolar,cents。但是,如果您的json货币字段是数字类型(而不是字符串),它将舍入您的美分,它可能是3、3.1或3.15。我使用implements TextWatcher将类更改为使用巴西货币格式,并在编辑值时调整光标位置 public class MoneyTextWatcher implements TextWatcher { private EditText editText; private String lastAmount = ""; private int lastCursorPosition = -1; public MoneyTextWatcher(EditText editText) { super(); this.editText = editText; } @Override public void onTextChanged(CharSequence amount, int start, int before, int count) { if (!amount.toString().equals(lastAmount)) { String cleanString = clearCurrencyToNumber(amount.toString()); try { String formattedAmount = transformToCurrency(cleanString); editText.removeTextChangedListener(this); editText.setText(formattedAmount); editText.setSelection(formattedAmount.length()); editText.addTextChangedListener(this); if (lastCursorPosition != lastAmount.length() && lastCursorPosition != -1) { int lengthDelta = formattedAmount.length() - lastAmount.length(); int newCursorOffset = max(0, min(formattedAmount.length(), lastCursorPosition + lengthDelta)); editText.setSelection(newCursorOffset); } } catch (Exception e) { //log something } } } @Override public void afterTextChanged(Editable s) { } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { String value = s.toString(); if(!value.equals("")){ String cleanString = clearCurrencyToNumber(value); String formattedAmount = transformToCurrency(cleanString); lastAmount = formattedAmount; lastCursorPosition = editText.getSelectionStart(); } } public static String clearCurrencyToNumber(String currencyValue) { String result = null; if (currencyValue == null) { result = ""; } else { result = currencyValue.replaceAll("[(a-z)|(A-Z)|($,. )]", ""); } return result; } public static boolean isCurrencyValue(String currencyValue, boolean podeSerZero) { boolean result; if (currencyValue == null || currencyValue.length() == 0) { result = false; } else { if (!podeSerZero && currencyValue.equals("0,00")) { result = false; } else { result = true; } } return result; } public static String transformToCurrency(String value) { double parsed = Double.parseDouble(value); String formatted = NumberFormat.getCurrencyInstance(new Locale("pt", "BR")).format((parsed / 100)); formatted = formatted.replaceAll("[^(0-9)(.,)]", ""); return formatted; } } 公共类MoneyTextWatcher实现TextWatcher{ 私人编辑文本; 私有字符串lastAmount=“”; private int lastCursorPosition=-1; public MoneyTextWatcher(编辑文本){ 超级(); this.editText=editText; } @凌驾 public void onTextChanged(字符序列数量、int start、int before、int count){ 如果(!amount.toString()等于(lastAmount)){ String cleanString=clearCurrencyToNumber(amount.toString()); 试一试{ 字符串formattedAmount=转换为货币(cleanString); removeTextChangedListener(此); editText.setText(格式化金额); editText.setSelection(formattedAmount.length()); editText.addTextChangedListener(此); if(lastCursorPosition!=lastAmount.length()&&lastCursorPosition!=-1){
import java.text.NumberFormat;
import java.util.Currency;
import java.util.Locale;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
public class CurrencyTextWatcher implements TextWatcher {
private String current = "";
private int index;
private boolean deletingDecimalPoint;
private final EditText currency;
public CurrencyTextWatcher(EditText p_currency) {
currency = p_currency;
}
@Override
public void beforeTextChanged(CharSequence p_s, int p_start, int p_count, int p_after) {
if (p_after>0) {
index = p_s.length() - p_start;
} else {
index = p_s.length() - p_start - 1;
}
if (p_count>0 && p_s.charAt(p_start)==',') {
deletingDecimalPoint = true;
} else {
deletingDecimalPoint = false;
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable p_s) {
if(!p_s.toString().equals(current)){
currency.removeTextChangedListener(this);
if (deletingDecimalPoint) {
p_s.delete(p_s.length()-index-1, p_s.length()-index);
}
// Currency char may be retrieved from NumberFormat.getCurrencyInstance()
String v_text = p_s.toString().replace("€","").replace(",", "");
v_text = v_text.replaceAll("\\s", "");
double v_value = 0;
if (v_text!=null && v_text.length()>0) {
v_value = Double.parseDouble(v_text);
}
// Currency instance may be retrieved from a static member.
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(new Locale("pt", "PT"));
String v_formattedValue = numberFormat.format((v_value/100));
current = v_formattedValue;
currency.setText(v_formattedValue);
if (index>v_formattedValue.length()) {
currency.setSelection(v_formattedValue.length());
} else {
currency.setSelection(v_formattedValue.length()-index);
}
// include here anything you may want to do after the formatting is completed.
currency.addTextChangedListener(this);
}
}
}
<EditText
android:id="@+id/edit_text_your_id"
...
android:text="0,00 €"
android:inputType="numberDecimal"
android:digits="0123456789" />
yourEditText = (EditText) findViewById(R.id.edit_text_your_id);
yourEditText.setRawInputType(Configuration.KEYBOARD_12KEY);
yourEditText.addTextChangedListener(new CurrencyTextWatcher(yourEditText));
NumberFormat nf = NumberFormat.getCurrencyInstance();
float value = 200 // it can be 200, 200.3 or 200.37, BigDecimal will take care
BigDecimal valueAsBD = BigDecimal.valueOf(value);
valueAsBD.setScale(2, BigDecimal.ROUND_HALF_UP);
String formated = nf.format(valueAsBD);
public class MoneyTextWatcher implements TextWatcher {
private EditText editText;
private String lastAmount = "";
private int lastCursorPosition = -1;
public MoneyTextWatcher(EditText editText) {
super();
this.editText = editText;
}
@Override
public void onTextChanged(CharSequence amount, int start, int before, int count) {
if (!amount.toString().equals(lastAmount)) {
String cleanString = clearCurrencyToNumber(amount.toString());
try {
String formattedAmount = transformToCurrency(cleanString);
editText.removeTextChangedListener(this);
editText.setText(formattedAmount);
editText.setSelection(formattedAmount.length());
editText.addTextChangedListener(this);
if (lastCursorPosition != lastAmount.length() && lastCursorPosition != -1) {
int lengthDelta = formattedAmount.length() - lastAmount.length();
int newCursorOffset = max(0, min(formattedAmount.length(), lastCursorPosition + lengthDelta));
editText.setSelection(newCursorOffset);
}
} catch (Exception e) {
//log something
}
}
}
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
String value = s.toString();
if(!value.equals("")){
String cleanString = clearCurrencyToNumber(value);
String formattedAmount = transformToCurrency(cleanString);
lastAmount = formattedAmount;
lastCursorPosition = editText.getSelectionStart();
}
}
public static String clearCurrencyToNumber(String currencyValue) {
String result = null;
if (currencyValue == null) {
result = "";
} else {
result = currencyValue.replaceAll("[(a-z)|(A-Z)|($,. )]", "");
}
return result;
}
public static boolean isCurrencyValue(String currencyValue, boolean podeSerZero) {
boolean result;
if (currencyValue == null || currencyValue.length() == 0) {
result = false;
} else {
if (!podeSerZero && currencyValue.equals("0,00")) {
result = false;
} else {
result = true;
}
}
return result;
}
public static String transformToCurrency(String value) {
double parsed = Double.parseDouble(value);
String formatted = NumberFormat.getCurrencyInstance(new Locale("pt", "BR")).format((parsed / 100));
formatted = formatted.replaceAll("[^(0-9)(.,)]", "");
return formatted;
}
}
amount.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
Number numberAmount = 0f;
try {
numberAmount = Float.valueOf(amount.getText().toString());
} catch (NumberFormatException e1) {
e1.printStackTrace();
try {
numberAmount = NumberFormat.getCurrencyInstance().parse(amount.getText().toString());
} catch (ParseException e2) {
e2.printStackTrace();
}
}
if (hasFocus) {
amount.setText(numberAmount.toString());
} else {
amount.setText(NumberFormat.getCurrencyInstance().format(numberAmount));
}
}
});
RxTextView
.textChangeEvents(fuel_price) // Observe text event changes
.filter { it.text().isNotEmpty() } // do not accept empty text when event first fires
.flatMap {
val onlyNumbers = Regex("\\d+").findAll(it.text()).fold(""){ acc:String,it:MatchResult -> acc.plus(it.value)}
Observable.just(onlyNumbers)
}
.distinctUntilChanged()
.map { it.trimStart('0') }
.map { when (it.length) {
1-> "00"+it
2-> "0"+it
else -> it }
}
.subscribe {
val digitList = it.reversed().mapIndexed { i, c ->
if ( i == 2 ) "${c},"
else if ( i < 2 ) c
else if ( (i-2)%3==0 ) "${c}." else c
}
val currency = digitList.reversed().fold(""){ acc,it -> acc.toString().plus(it) }
fuel_price.text = SpannableStringBuilder(currency)
fuel_price.setSelection(currency.length)
}
fun EditText.setMaskingMoney(currencyText: String) {
this.addTextChangedListener(object: MyTextWatcher{
val editTextWeakReference: WeakReference<EditText> = WeakReference<EditText>(this@setMaskingMoney)
override fun afterTextChanged(editable: Editable?) {
val editText = editTextWeakReference.get() ?: return
val s = editable.toString()
editText.removeTextChangedListener(this)
val cleanString = s.replace("[Rp,. ]".toRegex(), "")
val newval = currencyText + cleanString.monetize()
editText.setText(newval)
editText.setSelection(newval.length)
editText.addTextChangedListener(this)
}
})
}
interface MyTextWatcher: TextWatcher {
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
}
fun String.monetize(): String = if (this.isEmpty()) "0"
else DecimalFormat("#,###").format(this.replace("[^\\d]".toRegex(),"").toLong())
fun EditText.setMaskingMoney(currencyText: String) {
this.addTextChangedListener(object: MyTextWatcher{
val editTextWeakReference: WeakReference<EditText> = WeakReference<EditText>(this@setMaskingMoney)
override fun afterTextChanged(editable: Editable?) {
val editText = editTextWeakReference.get() ?: return
val s = editable.toString()
editText.removeTextChangedListener(this)
val cleanString = s.replace("[Rp,. ]".toRegex(), "")
val newval = currencyText + cleanString.monetize()
editText.setText(newval)
editText.setSelection(newval.length)
editText.addTextChangedListener(this)
}
})
}
interface MyTextWatcher: TextWatcher {
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
}
fun String.monetize(): String = if (this.isEmpty()) "0"
else DecimalFormat("#,###").format(this.replace("[^\\d]".toRegex(),"").toLong())
yourTextView.setMaskingMoney("Rp. ")
import android.content.Context;import android.graphics.Rect;import android.text.Editable;import android.text.InputFilter;import android.text.InputType;import android.text.TextWatcher;
import android.util.AttributeSet;import android.widget.EditText;import java.math.BigDecimal;import java.math.RoundingMode;
import java.text.DecimalFormat;import java.text.DecimalFormatSymbols;
import java.util.Locale;
/**
* Some note <br/>
* <li>Always use locale US instead of default to make DecimalFormat work well in all language</li>
*/
public class CurrencyEditText extends android.support.v7.widget.AppCompatEditText {
private static String prefix = "VND ";
private static final int MAX_LENGTH = 20;
private static final int MAX_DECIMAL = 3;
private CurrencyTextWatcher currencyTextWatcher = new CurrencyTextWatcher(this, prefix);
public CurrencyEditText(Context context) {
this(context, null);
}
public CurrencyEditText(Context context, AttributeSet attrs) {
this(context, attrs, android.support.v7.appcompat.R.attr.editTextStyle);
}
public CurrencyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
this.setHint(prefix);
this.setFilters(new InputFilter[] { new InputFilter.LengthFilter(MAX_LENGTH) });
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused) {
this.addTextChangedListener(currencyTextWatcher);
} else {
this.removeTextChangedListener(currencyTextWatcher);
}
handleCaseCurrencyEmpty(focused);
}
/**
* When currency empty <br/>
* + When focus EditText, set the default text = prefix (ex: VND) <br/>
* + When EditText lose focus, set the default text = "", EditText will display hint (ex:VND)
*/
private void handleCaseCurrencyEmpty(boolean focused) {
if (focused) {
if (getText().toString().isEmpty()) {
setText(prefix);
}
} else {
if (getText().toString().equals(prefix)) {
setText("");
}
}
}
private static class CurrencyTextWatcher implements TextWatcher {
private final EditText editText;
private String previousCleanString;
private String prefix;
CurrencyTextWatcher(EditText editText, String prefix) {
this.editText = editText;
this.prefix = prefix;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// do nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// do nothing
}
@Override
public void afterTextChanged(Editable editable) {
String str = editable.toString();
if (str.length() < prefix.length()) {
editText.setText(prefix);
editText.setSelection(prefix.length());
return;
}
if (str.equals(prefix)) {
return;
}
// cleanString this the string which not contain prefix and ,
String cleanString = str.replace(prefix, "").replaceAll("[,]", "");
// for prevent afterTextChanged recursive call
if (cleanString.equals(previousCleanString) || cleanString.isEmpty()) {
return;
}
previousCleanString = cleanString;
String formattedString;
if (cleanString.contains(".")) {
formattedString = formatDecimal(cleanString);
} else {
formattedString = formatInteger(cleanString);
}
editText.removeTextChangedListener(this); // Remove listener
editText.setText(formattedString);
handleSelection();
editText.addTextChangedListener(this); // Add back the listener
}
private String formatInteger(String str) {
BigDecimal parsed = new BigDecimal(str);
DecimalFormat formatter =
new DecimalFormat(prefix + "#,###", new DecimalFormatSymbols(Locale.US));
return formatter.format(parsed);
}
private String formatDecimal(String str) {
if (str.equals(".")) {
return prefix + ".";
}
BigDecimal parsed = new BigDecimal(str);
// example pattern VND #,###.00
DecimalFormat formatter = new DecimalFormat(prefix + "#,###." + getDecimalPattern(str),
new DecimalFormatSymbols(Locale.US));
formatter.setRoundingMode(RoundingMode.DOWN);
return formatter.format(parsed);
}
/**
* It will return suitable pattern for format decimal
* For example: 10.2 -> return 0 | 10.23 -> return 00, | 10.235 -> return 000
*/
private String getDecimalPattern(String str) {
int decimalCount = str.length() - str.indexOf(".") - 1;
StringBuilder decimalPattern = new StringBuilder();
for (int i = 0; i < decimalCount && i < MAX_DECIMAL; i++) {
decimalPattern.append("0");
}
return decimalPattern.toString();
}
private void handleSelection() {
if (editText.getText().length() <= MAX_LENGTH) {
editText.setSelection(editText.getText().length());
} else {
editText.setSelection(MAX_LENGTH);
}
}
}
}
<...CurrencyEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
private static String prefix = "VND ";
private static final int MAX_DECIMAL = 3;
android:inputType="numberDecimal"
private String formattedPrice;
private int itemPrice = 0;
EditText itemPriceInput = findViewById(R.id.item_field_price);
itemPriceInput.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
String priceString = itemPriceInput.getText().toString();
if (! priceString.equals("")) {
itemPrice = Double.parseDouble(priceString.replaceAll("[$,]", ""));
formattedPrice = NumberFormat.getCurrencyInstance().format(itemPrice);
itemPriceInput.setText(formattedPrice);
}
}
});
var isEditing = false
RxTextView.textChanges(dollarValue)
.filter { !isEditing }
.filter { it.isNotBlank() }
.map { it.toString().filter { it.isDigit() } }
.map { BigDecimal(it).setScale(2, BigDecimal.ROUND_FLOOR).divide(100.toBigDecimal(), BigDecimal.ROUND_FLOOR) }
.map { NumberFormat.getCurrencyInstance(Locale("pt", "BR")).format(it) }
.subscribe {
isEditing = true
dollarValue.text = SpannableStringBuilder(it)
dollarValue.setSelection(it.length)
isEditing = false
}
fun EditText.addCurrencyFormatter() {
// Reference: https://stackoverflow.com/questions/5107901/better-way-to-format-currency-input-edittext/29993290#29993290
this.addTextChangedListener(object: TextWatcher {
private var current = ""
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (s.toString() != current) {
this@addCurrencyFormatter.removeTextChangedListener(this)
// strip off the currency symbol
// Reference for this replace regex: https://stackoverflow.com/questions/5107901/better-way-to-format-currency-input-edittext/28005836#28005836
val cleanString = s.toString().replace("\\D".toRegex(), "")
val parsed = if (cleanString.isBlank()) 0.0 else cleanString.toDouble()
// format the double into a currency format
val formated = NumberFormat.getCurrencyInstance()
.format(parsed / 100)
current = formated
this@addCurrencyFormatter.setText(formated)
this@addCurrencyFormatter.setSelection(formated.length)
this@addCurrencyFormatter.addTextChangedListener(this)
}
}
})
}
yourEditText = (EditText) findViewById(R.id.edit_text_your_id);
yourEditText.addCurrencyFormatter()
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
if(!s.toString().equals(current)){
amountEditText.removeTextChangedListener(this);
String cleanString = s.toString().replaceAll("[$,.]", "");
try{
double parsed = Double.parseDouble(cleanString);
String formatted = NumberFormat.getCurrencyInstance().format((parsed/100));
current = formatted;
amountEditText.setText(formatted);
amountEditText.setSelection(formatted.length());
} catch (Exception e) {
}
amountEditText.addTextChangedListener(this);
}
}
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
import android.widget.TextView
import java.text.NumberFormat
import java.util.*
fun TextView.currencyFormat() {
addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
removeTextChangedListener(this)
text = if (s?.toString().isNullOrBlank()) {
""
} else {
s.toString().currencyFormat()
}
if(this@currencyFormat is EditText){
setSelection(text.toString().length)
}
addTextChangedListener(this)
}
})
}
fun String.currencyFormat(): String {
var current = this
if (current.isEmpty()) current = "0"
return try {
if (current.contains('.')) {
NumberFormat.getNumberInstance(Locale.getDefault()).format(current.replace(",", "").toDouble())
} else {
NumberFormat.getNumberInstance(Locale.getDefault()).format(current.replace(",", "").toLong())
}
} catch (e: Exception) {
"0"
}
}
fun CurrencyWatcher( editText:EditText) {
editText.addTextChangedListener(object : TextWatcher {
//this will prevent the loop
var changed: Boolean = false
override fun afterTextChanged(p0: Editable?) {
changed = false
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
editText.setSelection(p0.toString().length)
}
@SuppressLint("SetTextI18n")
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
if (!changed) {
changed = true
var str: String = p0.toString().replace(",", "").trim()
var element0: String = str.elementAt(0).toString()
var element1: String = "x"
var element2: String = "x"
var element3: String = "x"
var element4: String = "x"
var element5: String = "x"
var element6: String = "x"
//this variables will store each elements of the initials data for the case we need to move this numbers like: 0,01 to 0,11 or 0,11 to 0,01
if (str.length >= 2) {
element1 = str.elementAt(1).toString()
}
if (str.length >= 3) {
element2 = str.elementAt(2).toString()
}
editText.removeTextChangedListener(this)
//this first block of code will take care of the case
//where the number starts with 0 and needs to adjusta the 0 and the "," place
if (str.length == 1) {
str = "0,0" + str
editText.setText(str)
} else if (str.length <= 3 && str == "00") {
str = "0,00"
editText.setText(str)
editText.setSelection(str.length)
} else if (element0 == "0" && element1 == "0" && element2 == "0") {
str = str.replace("000", "")
str = "0,0" + str
editText.setText(str)
} else if (element0 == "0" && element1 == "0" && element2 != "0") {
str = str.replace("00", "")
str = "0," + str
editText.setText(str)
} else {
//This block of code works with the cases that we need to move the "," only because the value is bigger
//lets get the others elements
if (str.length >= 4) {
element3 = str.elementAt(3).toString()
}
if (str.length >= 5) {
element4 = str.elementAt(4).toString()
}
if (str.length >= 6) {
element5 = str.elementAt(5).toString()
}
if (str.length == 7) {
element6 = str.elementAt(6).toString()
}
if (str.length >= 4 && element0 != "0") {
val sb: StringBuilder = StringBuilder(str)
//set the coma in right place
sb.insert(str.length - 2, ",")
str = sb.toString()
}
//change the 0,11 to 1,11
if (str.length == 4 && element0 == "0") {
val sb: StringBuilder = StringBuilder(str)
//takes the initial 0 out
sb.deleteCharAt(0);
str = sb.toString()
val sb2: StringBuilder = StringBuilder(str)
sb2.insert(str.length - 2, ",")
str = sb2.toString()
}
//this will came up when its like 11,11 and the user delete one, so it will be now 1,11
if (str.length == 3 && element0 != "0") {
val sb: StringBuilder = StringBuilder(str)
sb.insert(str.length - 2, ",")
str = sb.toString()
}
//came up when its like 0,11 and the user delete one, output will be 0,01
if (str.length == 2 && element0 == "0") {
val sb: StringBuilder = StringBuilder(str)
//takes 0 out
sb.deleteCharAt(0);
str = sb.toString()
str = "0,0" + str
}
//came up when its 1,11 and the user delete, output will be 0,11
if (str.length == 2 && element0 != "0") {
val sb: StringBuilder = StringBuilder(str)
//retira o 0 da frente
sb.insert(0, "0,")
str = sb.toString()
}
editText.setText(str)
}
//places the selector at the end to increment the number
editText.setSelection(str.length)
editText.addTextChangedListener(this)
}
}
})
}
val etVal:EditText = findViewById(R.id.etValue)
CurrencyWatcher(etVal)
var current = ""
editText.addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
val stringText = s.toString()
if(stringText != current) {
editText.removeTextChangedListener(this)
val locale: Locale = Locale.UK
val currency = Currency.getInstance(locale)
val cleanString = stringText.replace("[${currency.symbol},.]".toRegex(), "")
val parsed = cleanString.toDouble()
val formatted = NumberFormat.getCurrencyInstance(locale).format(parsed / 100)
current = formatted
editText.setText(formatted)
editText.setSelection(formatted.length)
editText.addTextChangedListener(this)
}
}
})
public class MoneyEditText extends android.support.v7.widget.AppCompatEditText{
public MoneyEditText(Context context) {
super(context);
addTextChangedListener(MoneySplitter());
}
public MoneyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
addTextChangedListener(MoneySplitter());
}
public MoneyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
addTextChangedListener(MoneySplitter());
}
public TextWatcher MoneySplitter() {
TextWatcher textWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try
{
removeTextChangedListener(this);
String value = s.toString();
if (!value.equals(""))
{
if(!TextUtils.isEmpty(value))
setText(formatPrice(Double.parseDouble(value)));
setSelection(getText().toString().length());
}
addTextChangedListener(this);
}
catch (Exception ex)
{
ex.printStackTrace();
addTextChangedListener(this);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
};
return textWatcher;
}
public static String formatPrice(double value){
int DecimalPointNumber = 2;
Locale locale = Locale.getDefault();
DecimalFormat myFormatter = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
StringBuilder sb = new StringBuilder();
if(DecimalPointNumber>0){
for (int i = 0; i < DecimalPointNumber; i++) {
sb.append("#");
}
myFormatter.applyPattern("###,###."+ sb.toString());
}else
myFormatter.applyPattern("###,###"+ sb.toString());
return Currency.getInstance(Locale.getDefault()).getSymbol() + myFormatter.format(value);
}
}
<MoneyEditText
android:id="@+id/txtPrice"
android:layout_width="match_parent"
android:layout_height="64dp"
android:digits="0123456789.,"
android:inputType="numberDecimal"
android:selectAllOnFocus="true"
android:singleLine="true" />
package com.example.liberdade.util
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
import java.lang.ref.WeakReference
import java.math.BigDecimal
import java.text.NumberFormat
import java.util.*
class MoneyTextWatcher : TextWatcher {
private val editTextWeakReference: WeakReference<EditText?>?
private val locale: Locale = Locale("pt", "BR")
//private final Locale locale;
constructor(editText: EditText?, locale: Locale?) {
editTextWeakReference = WeakReference<EditText?>(editText)
//this.locale = if (locale != null) locale else Locale.getDefault()
}
constructor(editText: EditText?) {
editTextWeakReference = WeakReference<EditText?>(editText)
//locale = Locale.getDefault()
}
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
override fun onTextChanged(
s: CharSequence?,
start: Int,
before: Int,
count: Int
) {
}
override fun afterTextChanged(editable: Editable?) {
val editText: EditText = editTextWeakReference?.get() ?: return
editText.removeTextChangedListener(this)
var isNegative = false
var editableString = editable.toString()
if (editable != null) {
if (editableString.contains('-')) {
isNegative = true
if (editable != null) {
editableString = editableString.replace("-","")
}
}
}
val parsed: BigDecimal? = parseToBigDecimal(editableString, locale)
//val parsed: BigDecimal? = parseToBigDecimal(editable.toString(), locale)
var formatted: String = NumberFormat.getCurrencyInstance(locale).format(parsed)
if (isNegative && !(formatted.equals("R\$ 0,00") || formatted.equals("-R\$ 0,00"))) formatted = "-${formatted}"
editText.setText(formatted)
editText.setSelection(formatted.length)
editText.addTextChangedListener(this)
}
private fun parseToBigDecimal(value: String?, locale: Locale?): BigDecimal? {
val replaceable = java.lang.String.format(
"[%s,.\\s]",
NumberFormat.getCurrencyInstance(locale).currency.symbol
)
val cleanString = value!!.replace(replaceable.toRegex(), "")
return BigDecimal(cleanString).setScale(
2, BigDecimal.ROUND_FLOOR
).divide(
BigDecimal(100), BigDecimal.ROUND_FLOOR
)
}
}
//como invocar
//binding.editTextValorCaixa.addTextChangedListener(MoneyTextWatcher(binding.editTextValorCaixa, Locale("pt", "BR")))
android:inputType="numberSigned|numberDecimal"
**
* Use this function from [TextWatcher.afterTextChanged] it will first call [AppCompatEditText.removeTextChangedListener]
* on the TextWatcher you pass, manipulate the text and then register it again after it call setText.
*
* @param fallback The String that we will return if the user is doing illegal adding, like trying to add a third digit after the comma.
* It is best if you will keep the fallback as a member of the class the EditText resides in - and store in it
* @param textWatcher [TextWatcher] It will be used to unregister before manipulating the text.
* @param locale The locale that we will pass to [NumberFormat.getCurrencyInstance] - it will affect the currency sign. default is [Locale.US]
*
* @return A formatted String to use in [AppCompatEditText.setText]
*
*/
fun AppCompatEditText.formatCurrency(@NonNull fallback: String, @NonNull textWatcher: TextWatcher,
locale: Locale = Locale.US) {
removeTextChangedListener(textWatcher)
var original = text.toString()
if (original.startsWith(".")) {
// If the user press on '.-' key on the beginning of the amount - we are getting '.' and we turn it into '-'
setText(original.replaceFirst(".", "-"))
addTextChangedListener(textWatcher)
setSelection(text?.length ?: 0)
return
}
val split = original.split(".")
when (split.size) {
0 -> {
setText(fallback)
addTextChangedListener(textWatcher)
setSelection(text?.length ?: 0)
return
}
1 -> {
if (split[0] == "-") {
setText("-")
addTextChangedListener(textWatcher)
setSelection(text?.length ?: 0)
return
}
}
2 -> {
if (split[1].length > 2) {
setText(fallback)
addTextChangedListener(textWatcher)
setSelection(text?.length ?: 0)
return
}
}
}
// We store the decimal value in a local variable
val decimalSplit = original.split(".")
// flag to indicate that we have a decimal part on the original String.
val hasDecimal = decimalSplit.size > 1
if (hasDecimal) {
original = decimalSplit[0]
}
val isNegative = original.startsWith("-")
val cleanString: String = original.replace("""[$,]""".toRegex(), "")
val result = if (cleanString.isNotEmpty() && cleanString != "-") {
val formatString = original.replace("""[-$,.]""".toRegex(), "")
// Add Commas and Currency symbol.
var result = NumberFormat.getCurrencyInstance(locale).format(formatString.toDouble())
result = result.split('.')[0]
if (isNegative) {
// If it was negative we must add the minus sign.
result = "-${result}"
}
if (hasDecimal) {
// after the formatting the decimal is omitted, we need to append it.
result = "${result}.${decimalSplit[1]}"
}
result
} else {
original
}
setText(result)
addTextChangedListener(textWatcher)
setSelection(text?.length ?: 0)
}
class MyCoolClass{
private var mLastAmount = ""
...
...
private fun addTextWatcherToEt() {
mEtAmount.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
mEtAmount.formatCurrency(mLastAmount, this)
mLastAmount = mEtAmount.text.toString()
// Do More stuff here if you need...
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
}