У меня есть editText, начальное значение - 0,00 доллара. Когда вы нажимаете 1, он меняется на 0,01 доллара. Нажмите 4, он перейдет к 0,14 доллара. Нажмите 8, $ 1,48. Нажмите клавишу Backspace, $ 0,14 и т. Д.
Это работает, проблема в том, что если кто-то вручную устанавливает курсор, возникают проблемы с форматированием. Если бы они удалили десятичную дробь, она больше не вернется. Если они поместят курсор перед десятичной запятой и введут 2, вместо 2,00 долларов будет отображено 02 доллара США. Например, если они попытаются удалить знак $, вместо этого будет удалена цифра.
Вот код, который я использую, буду признателен за любые предложения.
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); } }
Вариант Котлина:
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) } }
источник
String replaceable = String.format("[%s,.]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol()); String cleanString = s.toString().replaceAll(replaceable, "");
String replaceable = String.format("[%s,.\\s]", NumberFormat.getCurrencyInstance().getCurrency().getSymbol());
onTextChanged() and rather to do so in
afterTextChanged () `double
10.10 / 100 = 0,1
вы не можете пройти мимо нее.Основываясь на некоторых из приведенных выше ответов, я создал 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); } }
источник
java.lang.NumberFormatException: Bad offset/length
.replaceAll("[^0-9]", "")
, у приведенного выше ограничение составляет 9.999.999 -_-Вот мой обычай
CurrencyEditText
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); } } } }
Используйте его в XML, например
<...CurrencyEditText android:layout_width="match_parent" android:layout_height="wrap_content" />
Вы должны отредактировать 2 константы ниже, чтобы они подходили для вашего проекта
private static String prefix = "VND "; private static final int MAX_DECIMAL = 3;
Демо на github
источник
formatter.setRoundingMode(RoundingMode.DOWN);
вformatDecimal
методе.$.
, когда мы получаем необработанное значение as.
и анализируем его до Double, это дает NFE. Для того, чтобы исправить, я сделал ,formatDecimal()
чтобы вернутьсяprefix + "0.";
и изменился#,###.
к#,##0.
внутреннемуformatDecimal()
. Это также выглядит лучше, когда пользователь вводит только десятичные знаки. Он отображается как$0.25
вместо$.25
.Фактически, ранее предложенное решение не работает. Это не сработает, если вы хотите ввести 100.00.
Заменить:
double parsed = Double.parseDouble(cleanString); String formato = NumberFormat.getCurrencyInstance().format((parsed/100));
С участием:
BigDecimal parsed = new BigDecimal(cleanString).setScale(2,BigDecimal.ROUND_FLOOR).divide(new BigDecimal(100),BigDecimal.ROUND_FLOOR); String formato = NumberFormat.getCurrencyInstance().format(parsed);
Должен сказать, что я внес некоторые изменения в свой код. Дело в том, что вы должны использовать BigDecimal
источник
Я меняю класс с помощью инструментов TextWatcher для использования форматов валюты Бразилии и регулировки положения курсора при редактировании значения.
источник
Я основывался на ответе Гильермеса, но я сохраняю положение курсора, а также по-разному обрабатываю периоды - таким образом, если пользователь вводит после периода, это не влияет на числа до периода, который, как я считаю, дает очень плавный ввод .
[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) { } });
источник
Несмотря на то, что здесь много ответов, я хотел бы поделиться этим кодом, который я нашел здесь, поскольку я считаю его наиболее надежным и чистым ответом.
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) { } }
Надеюсь, это поможет.
источник
replaceAll("\\D", "")
удалит все, что не является цифрой, поэтому «100,00 долларов» и «10 000 долларов» станут «10000». Похоже, вы рассчитываете, что на входе будут центы. Так что, если это гарантировано, отлично, но если нет, думаю, будут проблемы.Хорошо, вот лучший способ справиться с форматами валют - нажатие клавиши "удалить-назад". Код основан на приведенном выше коде @androidcurious ... Но решает некоторые проблемы, связанные с обратным удалением и некоторыми исключениями синтаксического анализа: http://miguelt.blogspot.ca/2013/01/textwatcher-for-currency-masksformatting .html [ОБНОВЛЕНИЕ] У предыдущего решения были некоторые проблемы ... Это лучшее решение: http://miguelt.blogspot.ca/2013/02/update-textwatcher-for-currency.html И ... вот Детали:
Этот подход лучше, поскольку он использует обычные механизмы Android. Идея состоит в том, чтобы форматировать значения после того, как пользователь создал представление.
Определите InputFilter для ограничения числовых значений - это требуется в большинстве случаев, потому что экран недостаточно велик для размещения длинных представлений EditText. Это может быть статический внутренний класс или просто другой простой класс:
/** 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 ""; } }
Определите класс (внутренний статический или просто класс), который будет реализовывать View.OnFocusChangeListener. Обратите внимание, что я использую класс Utils - его реализацию можно найти в разделе «Суммы, налоги».
/** Used to format the amount views. */ class AmountOnFocusChangeListener implements View.OnFocusChangeListener { @Override public void onFocusChange(View p_view, boolean p_hasFocus) { // This listener will be attached to any view containing amounts. EditText v_amountView = (EditText)p_view; if (p_hasFocus) { // v_value is using a currency mask - transfor over to cents. String v_value = v_amountView.getText().toString(); int v_cents = Utils.parseAmountToCents(v_value); // Now, format cents to an amount (without currency mask) v_value = Utils.formatCentsToAmount(v_cents); v_amountView.setText(v_value); // Select all so the user can overwrite the entire amount in one shot. v_amountView.selectAll(); } else { // v_value is not using a currency mask - transfor over to cents. String v_value = v_amountView.getText().toString(); int v_cents = Utils.parseAmountToCents(v_value); // Now, format cents to an amount (with currency mask) v_value = Utils.formatCentsToCurrency(v_cents); v_amountView.setText(v_value); } } }
Этот класс удалит формат валюты при редактировании - полагаясь на стандартные механизмы. Когда пользователь выходит, денежный формат применяется повторно.
Лучше определить несколько статических переменных, чтобы минимизировать количество экземпляров:
static final InputFilter[] FILTERS = new InputFilter[] {new NumericRangeFilter()}; static final View.OnFocusChangeListener ON_FOCUS = new AmountOnFocusChangeListener();
Наконец, внутри onCreateView (...):
Вы можете повторно использовать FILTERS и ON_FOCUS в любом количестве представлений 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()); } }
источник
Я использовал реализацию, на которую ссылался Натан Ли, и предложенное регулярное выражение Kayvan N и user2582318, чтобы удалить все символы, кроме цифр, чтобы создать следующую версию:
fun EditText.addCurrencyFormatter() { // Reference: /programming/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: /programming/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) } } }) }
Это функция расширения в Kotlin, которая добавляет TextWatcher в TextChangedListener EditText.
Чтобы использовать его, просто:
Я надеюсь, что это помогает.
источник
Я получил это отсюда и изменил его, чтобы он соответствовал формату португальской валюты.
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" />
Заставить его работать
yourEditText = (EditText) findViewById(R.id.edit_text_your_id); yourEditText.setRawInputType(Configuration.KEYBOARD_12KEY); yourEditText.addTextChangedListener(new CurrencyTextWatcher(yourEditText));
источник
Для меня это сработало так
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, 10, 010.00, 01.0
Как использовать:
editText.setFilters(new InputFilter[] {new CurrencyFormatInputFilter()});
источник
Я использовал это, чтобы позволить пользователю вводить валюту и преобразовывать ее из строки в int для хранения в db и снова возвращаться из int в строку
https://github.com/nleigh/Restaurant/blob/master/Restaurant/src/uk/co/nathanleigh/restaurant/CurrencyFormat.java
источник
Если ваше поле валюты json имеет числовой тип (а не String), оно может иметь вид 3.1, 3.15 или просто 3. Поскольку json автоматически округляет числовые поля.
В этом случае вам может потребоваться округлить его для правильного отображения (и для возможности использовать маску в поле ввода позже):
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);
Зачем это нужно?
Все ответы указывают на то, что при вводе текста нужно убрать символы валюты, считая, что вы получаете центы, и таким образом получается, что доллар + центы / 100 = доллар, цент. Но если ваше поле валюты json имеет числовой тип (а не строку), оно будет округлять ваши центы, это может быть 3, 3,1 или 3,15.
источник
come as 3.1 , 3.15 or just 3. Because json automatically round number fields
- это не имеет ничего общего с округлением !другой подход, но основанный на ответе Гильерме . Этот подход полезен, когда языковой стандарт вашей страны недоступен или если вы хотите использовать собственные символы валюты. Эта реализация предназначена только для положительных недесятичных чисел.
этот код находится в Котлине, первый делегат
setMaskingMoney
дляEditText
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) } }) }
Затем
MyTextWatcher
интерфейс должен быть расширен сTextWatcher
. Поскольку нам нужен толькоafterTextChanged
метод, другие методы необходимо переопределить в этом интерфейсе.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())
и где-то в методе onCreate:
yourTextView.setMaskingMoney("Rp. ")
источник
После слишком долгого поиска и неудач с Doubles, BigDecimals и т. Д. Я сделал этот код. Работает Plug And Play. Это в котлине. Итак, чтобы помочь другим застрявшим, как я, пойдем.
Код в основном представляет собой функцию, которая помещает textWatcher и настраивает кому в нужное место.
Сначала создайте эту функцию:
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)
источник
Посмотрев на большинство должностей StackOverflow по - разному для достижения этой цели с помощью
TextWatcher
,InputFilter
или библиотека , как CurrencyEditText я остановился на этом простом решении , используяOnFocusChangeListener
.Логика состоит в том, чтобы преобразовать
EditText
его в число, когда он сфокусирован, и отформатировать его обратно, когда он теряет фокус.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)); } } });
источник
Я реализовал версию Kotlin + Rx.
Это для бразильской валюты (например, 1,500.00 - 5,21 - 192,90), но вы можете легко адаптировать его для других форматов.
Надеюсь, кто-то еще сочтет это полезным.
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) }
источник
CurrencyTextWatcher.java
public class CurrencyTextWatcher implements TextWatcher { private final static String DS = "."; //Decimal Separator private final static String TS = ","; //Thousands Separator private final static String NUMBERS = "0123456789"; //Numbers private final static int MAX_LENGTH = 13; //Maximum Length private String format; private DecimalFormat decimalFormat; private EditText editText; public CurrencyTextWatcher(EditText editText) { String pattern = "###" + TS + "###" + DS + "##"; decimalFormat = new DecimalFormat(pattern); this.editText = editText; this.editText.setInputType(InputType.TYPE_CLASS_NUMBER); this.editText.setKeyListener(DigitsKeyListener.getInstance(NUMBERS + DS)); this.editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(MAX_LENGTH)}); } @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 value = editable.toString(); if (!value.isEmpty()) { value = value.replace(TS, ""); try { format = decimalFormat.format(Double.parseDouble(value)); format = format.replace("0", ""); } catch (Exception e) { System.out.println(e.getMessage()); } editText.setText(format); } editText.addTextChangedListener(this); } }
EditTextCurrency.java
public class EditTextCurrency extends AppCompatEditText { public EditTextCurrency(Context context) { super(context); } public EditTextCurrency(Context context, AttributeSet attrs) { super(context, attrs); addTextChangedListener(new CurrencyTextWatcher(this)); } }
источник
Вот как мне удалось отобразить валюту в EditText, который было легко реализовать и который хорошо работает для пользователя без возможности повсюду сумасшедших символов. Это не будет пытаться выполнить какое-либо форматирование, пока EditText не перестанет фокусироваться. Пользователь по-прежнему может вернуться и внести какие-либо изменения, не нарушая форматирования. Я использую переменную formattedPrice только для отображения, а переменную itemPrice как значение, которое я сохраняю / использую для вычислений.
Кажется, это действительно хорошо работает, но я занимаюсь этим всего несколько недель, поэтому любая конструктивная критика приветствуется!
Представление EditText в xml имеет следующий атрибут:
android:inputType="numberDecimal"
Глобальные переменные:
private String formattedPrice; private int itemPrice = 0;
В методе onCreate:
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); } } });
источник
Если кого-то интересует способ сделать это с помощью RxBinding и Kotlin:
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 }
источник
просто дополнительный комментарий к утвержденному ответу. Вы можете получить сбой при перемещении курсора в поле редактирования текста из-за синтаксического анализа. Я сделал оператор try catch, но реализовал свой собственный код.
@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" } }
источник
Версия Котлина :
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); } }
а затем используйте этот блок как свой editText
<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")))
источник