custom Edit text Android - android

i have 2 android project.
first project--> custom edit text that i made with custom regular expresion like this
private static final String QUANTITY_REGEX = "^\\d{0,4}(\\,\\d{0,3})?$";
second project --> project that use custom edit text from first project.
after that, i exported the first class to be a library on second project as a custom edittext.
the problem is :
as you can see the regular expression on first project only allowing 4 number to be written first, and 2 digit after "," symbol on edit text. but i want to make the custom edit text to be like this
isFocused: 1234,56
!isFocused : 1.234,56
how to make it possible. thx

Change your regex to ^\d{0,4}(.\d{0,4})?(,\d{0,2})?$
private static final String QUANTITY_REGEX ="^\\d{0,4}(.\\d{0,4})?(,\\d{0,2})?$";
The matches will be
1234
1234.5678
1234.5678,12

here what i`ve figure out... hope can usefull for other programmer
the concept :
1 edit text
when user focused on edittext (txt1), user only can write 4 number , a comma (",") and 3 another number after comma (",") --> 1234,567
when (txt1) onFocused = false (user click to another editText) txt1 show the number like this 1.234,567
here the code
VALIDATION CLASS
public class Validation {
// Regular Expression
// you can change the expression based on your need
private static final String EMAIL_REGEX = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
private static final String PHONE_REGEX = "\\d{3}-\\d{7}";
private static final String CURRENCY_REGEX = "^\\d{0,8}(\\,\\d{0,2})?$";
public String maxLength = Quantity.maxLengthFront;
public String QUANTITY_REGEX = null;
// Error Messages
private static final String REQUIRED_MSG = "required";
private static final String EMAIL_MSG = "invalid email";
private static final String PHONE_MSG = "###-#######";
private static final String CURRENCY_MSG = "invalid currency";
private static final String QUANTITY_MSG = "invalid quantity";
// call this method when you need to check email validation
public static boolean isEmailAddress(EditText editText, boolean required) {
return isValid(editText, EMAIL_REGEX, EMAIL_MSG, required);
}
// call this method when you need to check phone number validation
public static boolean isPhoneNumber(EditText editText, boolean required) {
return isValid(editText, PHONE_REGEX, PHONE_MSG, required);
}
// call this method when you need to check currency validation
public static boolean isCurrency(EditText editText, boolean required) {
return isValid(editText, CURRENCY_REGEX, CURRENCY_MSG, required);
}
public boolean isQuantity(EditText editText, boolean required){
return isValid(editText, QUANTITY_REGEX, QUANTITY_MSG, required);
}
// return true if the input field is valid, based on the parameter passed
public static boolean isValid(EditText editText, String regex, String errMsg, boolean required) {
String text = editText.getText().toString().trim();
// clearing the error, if it was previously set by some other values
editText.setError(null);
// text required and editText is blank, so return false
if ( required && !hasText(editText) ) return false;
// pattern doesn't match so returning false
if (required && !Pattern.matches(regex, text)) {
editText.setError(errMsg);
return false;
};
// pattern doesn't match so returning false
if (!required && !Pattern.matches(regex, text)) {
editText.setError(errMsg);
return false;
};
return true;
}
// check the input field has any text or not
// return true if it contains text otherwise false
public static boolean hasText(EditText editText) {
String text = editText.getText().toString().trim();
editText.setError(null);
// length 0 means there is no text
if (text.length() == 0) {
editText.setError(REQUIRED_MSG);
return false;
}
return true;
}
}
QUANTITY CLASS
public class Quantity extends EditText {
public static String maxLength;
public static String maxLengthFront = "4";
public static String regexCustom;
public boolean statusFocused = false;
public boolean allowBlank;
public String decimalPlaces;
String oldText;
Validation validation = new Validation();
public Quantity(Context context) {
super(context);
}
public Quantity(Context context, AttributeSet attrs) {
super(context, attrs);
// --- Additional custom code --
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.Quantity);
final int N = a.getIndexCount();
for (int i = 0; i < N; ++i) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.Quantity_decimalPlaces_quantity:
decimalPlaces = a.getString(attr);
// ...do something with delimiter...
break;
case R.styleable.Quantity_maxLength_quantity:
maxLength = "5";
// ...do something with fancyText...
doSetMaxLength();
break;
case R.styleable.Quantity_allowBlank_quantity:
allowBlank = a.getBoolean(attr, false);
// ...do something with fancyText...
break;
}
}
a.recycle();
this.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
this.setKeyListener(DigitsKeyListener.getInstance("1234567890-.,"));
this.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
if (statusFocused == true) {
maxLengthFront = "4";
if (validation.isQuantity(Quantity.this, false)) {
// Toast.makeText(getContext(), "Validation True", Toast.LENGTH_SHORT).show();
} else {
// Toast.makeText(getContext(), "Validation False",Toast.LENGTH_SHORT).show();
Quantity.this.setText(oldText);
Quantity.this.setSelection(Quantity.this.getText().length(), Quantity.this.getText().length());
}
} else {
maxLengthFront = "5";
}
validation.QUANTITY_REGEX = "^\\d{0," + maxLengthFront + "}(\\,\\d{0,3})?$";
}
public void beforeTextChanged(CharSequence s, int start, int count,int after) {
oldText = s.toString();
// Toast.makeText(getContext(), "Before change : " + s, Toast.LENGTH_SHORT).show();
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
this.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean gainFocus) {
// onFocus
String s;
if (gainFocus) {
maxLengthFront = "4";
validation.QUANTITY_REGEX = "^\\d{0," + maxLengthFront + "}(\\,\\d{0,3})?$";
s = Quantity.this.getText().toString().replace(".", "");
Quantity.this.setText(s);
statusFocused = true;
}
// onBlur
else {
maxLengthFront = "5";
validation.QUANTITY_REGEX = "^\\d{0," + maxLengthFront + "}(\\.\\d{0,3})?$";
Double number = Double.parseDouble(Quantity.this.getText().toString());
DecimalFormatSymbols symbol = DecimalFormatSymbols.getInstance();
symbol.setGroupingSeparator('.');
symbol.setDecimalSeparator(',');
DecimalFormat formatter = new DecimalFormat("###,###.###", symbol);
Quantity.this.setText(formatter.format(number));
statusFocused = false;
}
}
});
}
public Quantity(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// --- Additional custom code --
}
// #Override
// protected void onTextChanged(CharSequence text, int start,
// int lengthBefore, int lengthAfter) {
// // TODO Auto-generated method stub
// super.onTextChanged(text, start, lengthBefore, lengthAfter);
// Toast.makeText(getContext(), this.getText() + " - " + text.toString(),
// Toast.LENGTH_SHORT).show();
// }
public void doSetMaxLength() {
InputFilter[] FilterArray = new InputFilter[1];
FilterArray[0] = new InputFilter.LengthFilter(Integer.parseInt(maxLength, 10));
this.setFilters(FilterArray);
}
}

Related

How to reposition cursor while editing EditText field which is formatted like US currency- Android

I am formatting edit text field as per US currency format, where while typing number in a field, let's say "12345678" it appears like "12,345,678".
For this I have used TextWatcher and on afterTextChanged(...) method I am formatting the entered text like:
#Override
public void afterTextChanged(Editable editable) {
String str = editable.toString();
String number = str.replaceAll("[,]", "");
if (number.equals(previousNumber) || number.isEmpty()) {
return;
}
previousNumber = number;
DecimalFormat formatter = new DecimalFormat("#,###.##", new DecimalFormatSymbols(Locale.US));
String formattedString = formatter.format(number);
editText.setText(formattedString);
}
Also, I am using onSelectionChanged(...) callback method like:
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
this.setSelection(selStart);
}
But here this 'selStart' doesn't return the actual length of number as it excludes the number of "," in every currency.
For example: for "12,345,678" it returns count as 8 instead of 10.
That's why I am not able to place my cursor at the end of the field.
Following is the code of custom EditText, which I am using:
public class CurrencyEditText extends AppCompatEditText {
private static final int MAX_LENGTH = 16;
private static final int MAX_DECIMAL_DIGIT = 2;
private static String prefix = "";
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);
}
#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);
}
private void handleCaseCurrencyEmpty(boolean focused) {
if (!focused) {
if (getText().toString().equals(prefix)) {
setText("");
}
}
}
private static class CurrencyTextWatcher implements TextWatcher {
private final EditText editText;
DecimalFormat formatter;
private String previousNumber;
private String prefix;
Context mContext;
CurrencyTextWatcher(EditText editText, String prefix) {
this.editText = editText;
this.prefix = prefix;
formatter = new DecimalFormat("#,###.##", new DecimalFormatSymbols(Locale.US));
}
#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 str = editable.toString();
String number = str.replaceAll("[,]", "");
if (number.equals(previousNumber) || number.isEmpty()) {
return;
}
previousNumber = number;
String formattedString = prefix + formatNumber(number);
editText.removeTextChangedListener(this);
editText.setText(formattedString);
//handleSelection();
editText.addTextChangedListener(this);
}
private String formatNumber(String number) {
if (number.contains(".")) {
return formatDecimal(number);
}
return formatInteger(number);
}
private String formatInteger(String str) {
BigDecimal parsed = new BigDecimal(str);
return formatter.format(parsed);
}
private String formatDecimal(String str) {
if (str.equals(".")) {
return "0.";
}
BigDecimal parsed = new BigDecimal(str);
DecimalFormat formatter = new DecimalFormat("#,##0." + getDecimalPattern(str),
new DecimalFormatSymbols(Locale.US));
formatter.setRoundingMode(RoundingMode.DOWN);
return formatter.format(parsed);
}
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_DIGIT; i++) {
decimalPattern.append("0");
}
return decimalPattern.toString();
}
/*private void handleSelection() {
if (editText.getText().length() <= MAX_LENGTH) {
editText.setSelection(editText.getText().length());
}
}*/
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
this.setSelection(selStart);
}
}
I don't want to use this.setSelection(lengthOfTheEnteredText) because it created issue when you edit the field!
What could be the reason that onSelectionChanged(...) does not consider the count of "," present in number?
After exploring more on this issue, I have found the solution. Where I am calculating the cursor position. I have removed onSelectionChanged(...) method from my code and I am handling selection inafterTextChanged(...) method. In the following code I have made changes in afterTextChanged(...) :
#Override
public void afterTextChanged(Editable editable) {
String str = editable.toString();
String number = str.replaceAll("[,]", "");
if (number.equals(previousNumber) || number.isEmpty()) {
return;
}
previousNumber = number;
int startText, endText;
startText = editText.getText().length();
int selectionStart = editText.getSelectionStart();
String formattedString = prefix + formatNumber(number);
editText.removeTextChangedListener(this);
editText.setText(formattedString);
endText = editText.getText().length();
int selection = (selectionStart + (endText - startText));
editText.setSelection(selection);
editText.addTextChangedListener(this);
}

android numberpicker can't read keyboard number input

I'm trying to create a numberpicker to select a month.
It works aslong as I select the value through scrolling or if i use the keyboard to input the month by text (e.g. "jan" for januari)
I also want my users to be able to input '1' to select januari.
From numberpicker source code, it seems this should be possible:
/**
* #return The selected index given its displayed <code>value</code>.
*/
private int getSelectedPos(String value) {
if (mDisplayedValues == null) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
// Ignore as if it's not a number we don't care
}
} else {
for (int i = 0; i < mDisplayedValues.length; i++) {
// Don't force the user to type in jan when ja will do
value = value.toLowerCase();
if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
return mMinValue + i;
}
}
/*
* The user might have typed in a number into the month field i.e.
* 10 instead of OCT so support that too.
*/
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
// Ignore as if it's not a number we don't care
}
}
return mMinValue;
}
The problem is, if I try to input a number, the EditText just stays empty.
This is how I initialise my numberpicker:
//getting the months using Calendar
Calendar cal = Calendar.getInstance();
Map<String, Integer> monthMap = cal.getDisplayNames(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
TreeMap<Integer, String> sorted = new TreeMap<>();
for(Map.Entry<String, Integer> entry : monthMap.entrySet()) {
sorted.put(entry.getValue(), entry.getKey());
}
String[] displayNames = sorted.values().toArray(new String[]{});
mMonthPicker.setMinValue(0);
mMonthPicker.setMaxValue(11);
mMonthPicker.setDisplayedValues(displayNames);
mMonthPicker.setWrapSelectorWheel(false);
I tried setting the inputtype for the edittext to InputType.TYPE_NULL using the answer given here but that didn't change anything.
The Edit Text stays empty if I try to input a number.
I finally figured it out. The numberpicker didn't allow numbers for month input because of the filter on the EditText.
I solved it by copying some code of the NumberKeyListener from numberpicker source code and adjusting it so it would accept numeric input.
Then i added this filter on the EditText, which I look up by going through the childviews and checking if the current view is an EditText.
I found the code for looking up the EditText in the answer here: NumberPicker doesn't work with keyboard
my code looks like this:
mInputText = findInput(mMonthPicker);
mInputText.setFilters(new InputFilter[]{ new InputTextFilter() });
this is my filter:
/**
* Filter for accepting only valid indices or prefixes of the string
* representation of valid indices.
*/
class InputTextFilter extends NumberKeyListener {
/**
* The numbers accepted by the input text's {#link android.view.LayoutInflater.Filter}
*/
private final char[] DIGIT_CHARACTERS = new char[] {
// Latin digits are the common case
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
// Arabic-Indic
'\u0660', '\u0661', '\u0662', '\u0663', '\u0664', '\u0665', '\u0666', '\u0667', '\u0668'
, '\u0669',
// Extended Arabic-Indic
'\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8'
, '\u06f9',
// Hindi and Marathi (Devanagari script)
'\u0966', '\u0967', '\u0968', '\u0969', '\u096a', '\u096b', '\u096c', '\u096d', '\u096e'
, '\u096f',
// Bengali
'\u09e6', '\u09e7', '\u09e8', '\u09e9', '\u09ea', '\u09eb', '\u09ec', '\u09ed', '\u09ee'
, '\u09ef',
// Kannada
'\u0ce6', '\u0ce7', '\u0ce8', '\u0ce9', '\u0cea', '\u0ceb', '\u0cec', '\u0ced', '\u0cee'
, '\u0cef'
};
// XXX This doesn't allow for range limits when controlled by a
// soft input method!
public int getInputType() {
return InputType.TYPE_CLASS_TEXT;
}
#Override
protected char[] getAcceptedChars() {
return DIGIT_CHARACTERS;
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
Log.v("filter", "source:" + source.toString());
CharSequence filtered = String.valueOf(source.subSequence(start, end));
Log.v("filter", "filtered:" + filtered.toString());
if (TextUtils.isEmpty(filtered)) {
return "";
}
String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
+ dest.subSequence(dend, dest.length());
String str = String.valueOf(result).toLowerCase();
try{
int value = Integer.parseInt(str);
if(1 <= value && value <= 12) {
return source;
}
} catch(NumberFormatException e) {
//continue with the checking
}
for (String val : mMonthPicker.getDisplayedValues()) {
String valLowerCase = val.toLowerCase();
if (valLowerCase.startsWith(str)) {
final int selstart = result.length();
final int selend = val.length();
mInputText.post(new Runnable() {
#Override
public void run() {
mInputText.setSelection(selstart, selend);
}
});
return val.subSequence(dstart, val.length());
}
}
return "";
}
}
and this is the code to find the EditText:
private EditText findInput(ViewGroup np) {
int count = np.getChildCount();
for (int i = 0; i < count; i++) {
final View child = np.getChildAt(i);
if (child instanceof ViewGroup) {
findInput((ViewGroup) child);
} else if (child instanceof EditText) {
return (EditText) child;
}
}
return null;
}
Just to elaborate onto this, here is what I did to get the EditText input value (expanding on SnyersK code). The custom edit text can also be set to disable keyboard input and have a min and max value in xml:
xml use :
<com.octane.smartlink.widgets.SmartLinkNumberPicker
android:id="#+id/distance_tenths_of_distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
max="9"
min="0"/>
Android widget class:
public class SmartLinkNumberPicker extends NumberPicker {
//declare needed variables
private EditText numberEditText;
private boolean shouldDisableEditText = true; //set to true to disable edit text input and focus
public SmartLinkNumberPicker(Context context) {
super(context);
}
public SmartLinkNumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public SmartLinkNumberPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
numberEditText = populateEditText((ViewGroup) this);
if(numberEditText != null && shouldDisableEditText)
numberEditText.setFocusable(false);
processAttributeSet(attrs);
}
private EditText populateEditText(ViewGroup viewGroup) {
int count = viewGroup.getChildCount();
for(int i = 0; i < count; i++) {
final View child = viewGroup.getChildAt(i);
if (child instanceof ViewGroup)
populateEditText((ViewGroup) child);
else if (child instanceof EditText)
return (EditText) child;
}
return null;
}
/*
* used to set min, max attributes in xml
* set by min = "0" and max = "9"
*/
private void processAttributeSet(AttributeSet attrs) {
this.setMinValue(attrs.getAttributeIntValue(null, "min", 0));
this.setMaxValue(attrs.getAttributeIntValue(null, "max", 0));
}
/*
* always returns a valid number since number input is only allowed
* this will capture value set by either plus/minus buttons or edit text input
*/
#Override
public int getValue() {
return Integer.valueOf(numberEditText.getEditableText().toString());
}
}

How to automatically add commas to large numbers in Android?

I have a TextView that holds a number value (which is constantly being updated). Is there a method I can use to automatically add a comma if the number increases?
This is my current code:
String number = textView.getText().toString();
double amount = Double.parseDouble(number);
DecimalFormat formatter = new DecimalFormat("#,###");
String formatted = formatter.format(amount);
textView.setText(formatted);
Takes an integer and returns a string formatted to the U.S. Locale
private String getFormatedAmount(int amount){
return NumberFormat.getNumberInstance(Locale.US).format(amount);
}
In: 10 Out: 10
In: 100 Out: 100
In: 1000 Out: 1,000
In: 10000 Out: 10,000
In: 100000 Out: 100,000
In: 1000000 Out: 1,000,000
You can use DecimalFormat as it even supports locales (some places use . instead of ,)
String number = "1000500000.574";
double amount = Double.parseDouble(number);
DecimalFormat formatter = new DecimalFormat("#,###.00");
String formatted = formatter.format(amount);
You can implements TextWatcher
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;
}
}
}
To use it you can use
editText.addTextChangedListener(new NumberTextWatcher(editText));
Source : Roshka Dev Team

How to Automatically add thousand separators as number is input in EditText

Im creating a convertor application. I want to set the EditText so that when the user is inputting the number to be converted, a thousand separator (,) should be added automatically in realtime to the number once it increments by 3 figures: thousand, million, billion etc.
And when erased to below 4 figures the number goes back to normal.
Any help?
Even-though It's late. Intended for future visitors.
Fetures of the following codes
Puts thousand separator in EditText as it's text changes.
adds 0. Automatically when pressed period (.) At First.
Ignores 0 input at Beginning.
Just copy the following
Class named
NumberTextWatcherForThousand which implements TextWatcher
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import java.util.StringTokenizer;
/**
* Created by skb on 12/14/2015.
*/
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) {
// String returnString;
if(string.contains(",")){
return string.replace(",","");}
else {
return string;
}
}
}
Use This Class on your EditText as follows
editText.addTextChangedListener(new NumberTextWatcherForThousand(editText));
To get the input as plain Double Text
Use the trimCommaOfString method of the same class like this
NumberTextWatcherForThousand.trimCommaOfString(editText.getText().toString())
Git
You can use String.format() in a TextWatcher. The comma in the format specifier does the trick.
This does not work for floating point input. And be careful not to set an infinite loop with the TextWatcher.
public void afterTextChanged(Editable view) {
String s = null;
try {
// The comma in the format specifier does the trick
s = String.format("%,d", Long.parseLong(view.toString()));
} catch (NumberFormatException e) {
}
// Set s back to the view after temporarily removing the text change listener
}
public static String doubleToStringNoDecimal(double d) {
DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
formatter.applyPattern("#,###");
return formatter.format(d);
}
This sample app deconstructs formatting numbers clearly.
To summarize the link above, use a TextWatcher and in the afterTextChanged() method format the EditText view with the following logic:
#Override
public void afterTextChanged(Editable s) {
editText.removeTextChangedListener(this);
try {
String originalString = s.toString();
Long longval;
if (originalString.contains(",")) {
originalString = originalString.replaceAll(",", "");
}
longval = Long.parseLong(originalString);
DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);
formatter.applyPattern("#,###,###,###");
String formattedString = formatter.format(longval);
//setting text after format to EditText
editText.setText(formattedString);
editText.setSelection(editText.getText().length());
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
}
editText.addTextChangedListener(this);
}
I know i am very late to the party but it may be very useful for future users. My answer is an extension of Shree Krishna's answer.
Improvements:
Thousands separators and Decimal markers are locale aware i.e. they are used accordingly to the Locale of the device.
The cursor position doesn't change after deleting or adding elements in the middle also (In his answer cursor was reset to the end).
The overall quality of the code has been improved specially the getDecimalFormattedString method.
Code:
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import java.text.DecimalFormat;
/**
* Created by srv_twry on 4/12/17.
* Source: https://stackoverflow.com/a/34265406/137744
* The custom TextWatcher that automatically adds thousand separators in EditText.
*/
public class ThousandSeparatorTextWatcher implements TextWatcher {
private DecimalFormat df;
private EditText editText;
private static String thousandSeparator;
private static String decimalMarker;
private int cursorPosition;
public ThousandSeparatorTextWatcher(EditText editText) {
this.editText = editText;
df = new DecimalFormat("#,###.##");
df.setDecimalSeparatorAlwaysShown(true);
thousandSeparator = Character.toString(df.getDecimalFormatSymbols().getGroupingSeparator());
decimalMarker = Character.toString(df.getDecimalFormatSymbols().getDecimalSeparator());
}
#Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
cursorPosition = editText.getText().toString().length() - editText.getSelectionStart();
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override
public void afterTextChanged(Editable s) {
try {
editText.removeTextChangedListener(this);
String value = editText.getText().toString();
if (value != null && !value.equals("")) {
if (value.startsWith(decimalMarker)) {
String text = "0" + decimalMarker;
editText.setText(text);
}
if (value.startsWith("0") && !value.startsWith("0" + decimalMarker)) {
int index = 0;
while (index < value.length() && value.charAt(index) == '0') {
index++;
}
String newValue = Character.toString(value.charAt(0));
if (index != 0) {
newValue = value.charAt(0) + value.substring(index);
}
editText.setText(newValue);
}
String str = editText.getText().toString().replaceAll(thousandSeparator, "");
if (!value.equals("")) {
editText.setText(getDecimalFormattedString(str));
}
editText.setSelection(editText.getText().toString().length());
}
//setting the cursor back to where it was
editText.setSelection(editText.getText().toString().length() - cursorPosition);
editText.addTextChangedListener(this);
} catch (Exception ex) {
ex.printStackTrace();
editText.addTextChangedListener(this);
}
}
private static String getDecimalFormattedString(String value) {
String[] splitValue = value.split("\\.");
String beforeDecimal = value;
String afterDecimal = null;
String finalResult = "";
if (splitValue.length == 2) {
beforeDecimal = splitValue[0];
afterDecimal = splitValue[1];
}
int count = 0;
for (int i = beforeDecimal.length() - 1; i >= 0 ; i--) {
finalResult = beforeDecimal.charAt(i) + finalResult;
count++;
if (count == 3 && i > 0) {
finalResult = thousandSeparator + finalResult;
count = 0;
}
}
if (afterDecimal != null) {
finalResult = finalResult + decimalMarker + afterDecimal;
}
return finalResult;
}
/*
* Returns the string after removing all the thousands separators.
* */
public static String getOriginalString(String string) {
return string.replace(thousandSeparator,"");
}
}
This solution has some advantage over other answers. For example, it keeps the user's cursor position even if they edit the beginning or middle of the number. Other solutions always jump the cursor to the end of the number. It handles decimals and whole numbers, as well as locales that use characters other than . for the decimal separator and , for the thousands grouping separator.
class SeparateThousands(val groupingSeparator: String, val decimalSeparator: String) : TextWatcher {
private var busy = false
override fun afterTextChanged(s: Editable?) {
if (s != null && !busy) {
busy = true
var place = 0
val decimalPointIndex = s.indexOf(decimalSeparator)
var i = if (decimalPointIndex == -1) {
s.length - 1
} else {
decimalPointIndex - 1
}
while (i >= 0) {
val c = s[i]
if (c == groupingSeparator[0] ) {
s.delete(i, i + 1)
} else {
if (place % 3 == 0 && place != 0) {
// insert a comma to the left of every 3rd digit (counting from right to
// left) unless it's the leftmost digit
s.insert(i + 1, groupingSeparator)
}
place++
}
i--
}
busy = false
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
}
Then in xml:
<EditText
android:id="#+id/myNumberField"
android:digits=",.0123456789"
android:inputType="numberDecimal"
.../>
And finally register the watcher:
findViewById(R.id.myNumberField).addTextChangedListener(
SeparateThousands(groupingSeparator, decimalSeparator))
To handle . vs , in different locales use groupingSeparator and decimalSeparator, which can come from DecimalFormatSymbols or localized strings.
I just wanted comma to be placed and this is working for me:
String.format("%,.2f", myValue);
Here is my ThousandNumberEditText class
public class ThousandNumberEditText extends android.support.v7.widget.AppCompatEditText {
// TODO: 14/09/2017 change it if you want
private static final int MAX_LENGTH = 20;
private static final int MAX_DECIMAL = 3;
public ThousandNumberEditText(Context context) {
this(context, null);
}
public ThousandNumberEditText(Context context, AttributeSet attrs) {
this(context, attrs, android.support.v7.appcompat.R.attr.editTextStyle);
}
public ThousandNumberEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
addTextChangedListener(new ThousandNumberTextWatcher(this));
setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
setFilters(new InputFilter[] { new InputFilter.LengthFilter(MAX_LENGTH) });
setHint("0"); // TODO: 14/09/2017 change it if you want
}
private static class ThousandNumberTextWatcher implements TextWatcher {
private EditText mEditText;
ThousandNumberTextWatcher(EditText editText) {
mEditText = 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) {
String originalString = editable.toString();
String cleanString = originalString.replaceAll("[,]", "");
if (cleanString.isEmpty()) {
return;
}
String formattedString = getFormatString(cleanString);
mEditText.removeTextChangedListener(this);
mEditText.setText(formattedString);
mEditText.setSelection(mEditText.getText().length());
mEditText.addTextChangedListener(this);
}
/**
* Return the format string
*/
private String getFormatString(String cleanString) {
if (cleanString.contains(".")) {
return formatDecimal(cleanString);
} else {
return formatInteger(cleanString);
}
}
private String formatInteger(String str) {
BigDecimal parsed = new BigDecimal(str);
DecimalFormat formatter;
formatter = new DecimalFormat("#,###");
return formatter.format(parsed);
}
private String formatDecimal(String str) {
if (str.equals(".")) {
return ".";
}
BigDecimal parsed = new BigDecimal(str);
DecimalFormat formatter;
formatter =
new DecimalFormat("#,###." + getDecimalPattern(str)); //example patter #,###.00
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() - 1 - str.indexOf(".");
StringBuilder decimalPattern = new StringBuilder();
for (int i = 0; i < decimalCount && i < MAX_DECIMAL; i++) {
decimalPattern.append("0");
}
return decimalPattern.toString();
}
}
}
Using
<.ThousandNumberEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
You can use this method:
myEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String input = s.toString();
if (!input.isEmpty()) {
input = input.replace(",", "");
DecimalFormat format = new DecimalFormat("#,###,###");
String newPrice = format.format(Double.parseDouble(input));
myEditText.removeTextChangedListener(this); //To Prevent from Infinite Loop
myEditText.setText(newPrice);
myEditText.setSelection(newPrice.length()); //Move Cursor to end of String
myEditText.addTextChangedListener(this);
}
}
#Override
public void afterTextChanged(final Editable s) {
}
});
And to get original text use this:
String input = myEditText.getText().toString();
input = input.replace(",", "");
Since i had the same problem i decided to find a solution to it
Find my function below i hope it helps people finding solution
securityDeposit.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start,
int before, int count) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
if (s.toString().trim().length() > 0) {
int rentValue = Integer.parseInt(s.toString()
.replaceAll(",", ""));
StringBuffer rentVal = new StringBuffer();
if (rentValue > 10000000) {
s.clear();
s.append("10,000,000");
} else {
if (s.length() == 4) {
char x[] = s.toString().toCharArray();
char y[] = new char[x.length + 1];
for (int z = 0; z < y.length; z++) {
if (z == 1) {
y[1] = ',';
} else {
if (z == 0)
y[z] = x[z];
else {
y[z] = x[z - 1];
}
}
}
for (int z = 0; z < y.length; z++) {
rentVal = rentVal.append(y[z]);
}
s.clear();
s.append(rentVal);
}
}
}
}
});
you can use this code in many ways in your program, you give it a string and it separate each three from right and place space there.
private String Spacer(String number){
StringBuilder strB = new StringBuilder();
strB.append(number);
int Three = 0;
for(int i=number.length();i>0;i--){
Three++;
if(Three == 3){
strB.insert(i-1, " ");
Three = 0;
}
}
return strB.toString();
}// end Spacer()
u can change it a bit and use it ontextchangelistener.
good luck
The answers here lack a method to handle actual user input, such as deleting characters or copying and pasting. This is an EditText field. If you want to add formatting in, you need to support editing that formatted value.
This implementation still has a deficiency depending on your use case. I didn't care about decimal values and assumed I would only be handling whole numbers. There's enough of how to handle that on this page and how to handle actual internationalization that I'll leave that as an exercise to the reader. If you need to do that, it shouldn't be too difficult to add "." to the regular expression to keep the decimal; you'll just have to be careful to acknowledge the numeral string still has a non numerical character.
This is designed to be used throughout multiple activities. New it once, give it your edit text and your data model and ignore it. The model binding can be removed if you don't need it.
public class EditNumberFormatter implements TextWatcher {
private EditText watched;
private Object model;
private Field field;
private IEditNumberFormatterListener listener;
private ActiveEdit activeEdit;
/**
* Binds an EditText to a data model field (Such as a room entity's public variable)
* Whenever the edit text is changed, the text is formatted to the local numerical format.
*
* Handles copy/paste/backspace/select&delete/typing
*
* #param model An object with a public field to bind to
* #param fieldName A field defined on the object
* #param watched The edit text to watch for changes
* #param listener Another object that wants to know after changes & formatting are done.
*/
public EditNumberFormatter(Object model, String fieldName, EditText watched, IEditNumberFormatterListener listener) {
this.model = model;
this.watched = watched;
this.listener = listener;
try {
field = model.getClass().getDeclaredField(fieldName);
} catch(Exception e) { }
watched.addTextChangedListener(this);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
activeEdit = new ActiveEdit(s.toString(), start, count);
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
activeEdit.recordChangedText(s.toString(),count);
}
#Override
public void afterTextChanged(Editable s) {
this.watched.removeTextChangedListener(this);
activeEdit.processEdit(); // Override the user's edit of the formatted string with what the user intended to do to the numeral.
watched.setText(activeEdit.getCurrentFormattedString());
watched.setSelection(activeEdit.getCursorPosition());
updateDataModel(activeEdit.getCurrentRawValue());
listener.FormatUpdated(watched.getId(), activeEdit.getCurrentRawValue(), activeEdit.getCurrentFormattedString());
this.watched.addTextChangedListener(this);
}
private void updateDataModel(int rawValue) {
try {
field.set(model, rawValue);
} catch (IllegalAccessException e) { }
}
/**
* Tracks the active editing of an EditText formatted for integer input
*/
private class ActiveEdit {
private String priorFormattedString;
private String currentFormattedString;
private String currentNumericalString;
private int currentRawValue;
private boolean removal;
private boolean addition;
private int changeStart;
private int removedCount;
private int additionCount;
private int numeralCountBeforeSelection;
private int numeralCountAdded;
private int numeralCountRemoved;
/**
* Call in beforeEdit to begin recording changes
*
* #param beforeEdit string before edit began
* #param start start position of edit
* #param removed number of characters removed
*/
public ActiveEdit(String beforeEdit, int start, int removed) {
removal = (removed > 0);
priorFormattedString = beforeEdit;
changeStart = start;
removedCount = removed;
numeralCountBeforeSelection = countNumerals(priorFormattedString.substring(0, changeStart));
numeralCountRemoved = countNumerals(priorFormattedString.substring(changeStart, changeStart + removedCount));
}
/**
* Call in onTextChanged to record new text and how many characters were added after changeStart
*
* #param afterEdit new string after user input
* #param added how many characters were added (same start position as before)
*/
public void recordChangedText(String afterEdit, int added) {
addition = (added > 0);
additionCount = added;
numeralCountAdded = countNumerals(afterEdit.substring(changeStart, changeStart + additionCount));
currentNumericalString = afterEdit.replaceAll("[^0-9]", "");
}
/**
* Re-process the edit for our particular formatting needs.
*/
public void processEdit() {
forceRemovalPastFormatting();
finalizeEdit();
}
/**
* #return Integer value of the field after an edit.
*/
public int getCurrentRawValue() {
return currentRawValue;
}
/**
* #return Formatted number after an edit.
*/
public String getCurrentFormattedString() {
return currentFormattedString;
}
/**
* #return Cursor position after an edit
*/
public int getCursorPosition() {
int numeralPosition = numeralCountBeforeSelection + numeralCountAdded;
return positionAfterNumeralN(currentFormattedString,numeralPosition);
}
/**
* If a user deletes a value, but no numerals are deleted, then delete the numeral proceeding
* their cursor. Otherwise, we'll just add back the formatting character.
*
* Assumes formatting uses a single character and not multiple formatting characters in a row.
*/
private void forceRemovalPastFormatting() {
if (removal && (!addition) && (numeralCountRemoved == 0)) {
String before = currentNumericalString.substring(0, numeralCountBeforeSelection - 1);
String after = currentNumericalString.substring(numeralCountBeforeSelection);
currentNumericalString = before + after;
numeralCountRemoved++;
numeralCountBeforeSelection--;
}
}
/**
* Determine the result of the edit, including new display value and raw value
*/
private void finalizeEdit() {
currentFormattedString = "";
currentRawValue = 0;
if (currentNumericalString.length() == 0) {
return; // There is no entry now.
}
try {
currentRawValue = Integer.parseInt(currentNumericalString);
} catch (NumberFormatException nfe) {
abortEdit(); // Value is not an integer, return to previous state.
return;
}
currentFormattedString = String.format("%,d", currentRawValue);
}
/**
* Current text, same as the old text.
*/
private void abortEdit() {
currentFormattedString = priorFormattedString;
currentNumericalString = currentFormattedString.replaceAll("[^0-9]", "");
numeralCountRemoved = 0;
numeralCountAdded = 0;
try {
currentRawValue = Integer.parseInt(currentNumericalString);
} catch (Exception e) { currentRawValue = 0; }
}
/**
* Determine how many numerical characters exist in a string
* #param s
* #return the number of numerical characters in the string
*/
private int countNumerals(String s) {
String newString = s.replaceAll("[^0-9]", "");
return newString.length();
}
/**
* Determine how to place a cursor after the Nth Numeral in a formatted string.
* #param s - Formatted string
* #param n - The position of the cursor should follow the "Nth" number in the string
* #return the position of the nth character in a formatted string
*/
private int positionAfterNumeralN(String s, int n) {
int numeralsFound = 0;
if (n == 0) {
return 0;
}
for (int i = 0; i < s.length(); i++) {
if(s.substring(i,i+1).matches("[0-9]")) {
if(++numeralsFound == n) {
return i + 1;
}
}
}
return s.length();
}
}
}
At a highlevel, what that does is:
Determine which numbers were actually in the string after it was edited
Process the edit to the numeral version of the string if the numbers weren't edited
Convert the numeral back to a formatted string
Determine, where the cursor should be based on where editing began and how much text was added
It also nicely handles edge cases like completely deleted input, integer overflow and erroneous input.
You can use a custom TextInputEditText :
public class NumberTextInputEditText extends TextInputEditText {
public NumberTextInputEditText(#NonNull Context context) {
super(context);
}
public NumberTextInputEditText(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public NumberTextInputEditText(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
addTextChangedListener(textWatcher);
}
public String formatNumber(double number) {
DecimalFormat decimalFormat = new DecimalFormat("#,###");
return decimalFormat.format(number);
}
public TextWatcher textWatcher = 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) {
removeTextChangedListener(this);
String text = getText().toString();
String format = "";
if (!TextUtils.isEmpty(text)) {
try {
format = formatNumber(Double.parseDouble(new BigDecimal(text.replaceAll(",", "")).toString()));
} catch (NumberFormatException e) {
format = "";
}
setText(format);
setSelection(format.length());
}
addTextChangedListener(this);
}
};}
just use it like a view in your layout:
<com.your.package.name.NumberTextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Here i have tested my application code. text-watcher how to add comma in currency thousand, lake currency.
private TextWatcher textWatcherAmount = 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) {
String initial = s.toString();
if (inputEdtHawalaRate == null) return;
if (!TextUtils.isEmpty(initial)) {
initial = initial.replace(",", "");
NumberFormat formatter = new DecimalFormat("##,##,###");
inputEdtHawalaRate.removeTextChangedListener(this);
double myNumber = Double.parseDouble(initial);
String processed = formatter.format(myNumber);
//Assign processed text
inputEdtHawalaRate.setText(processed);
try {
inputEdtHawalaRate.setSelection(processed.length());
} catch (Exception e) {
e.printStackTrace();
}
//Give back the listener
inputEdtHawalaRate.addTextChangedListener(this);
}
}
#Override
public void afterTextChanged(Editable s) {
}
};
if (inputEdtHawalaRate != null) {
inputEdtHawalaRate.addTextChangedListener(textWatcherAmount);
}
// getting amount on double type varaible (On textwatcher editetxt value get).
String amount = Objects.requireNonNull(inputEdtHawalaRate.getText()).toString().trim();
double hawalaAmount = 0.0;
String[] a = amount.split(",");
finalAmount = TextUtils.join("", a);
hawalaAmount = Double.parseDouble(finalAmount);
I was looking for a locale aware solution since we have customers across the globe. So I built upon dr0pdb's answer.
Here's a TextWatcher class (in kotlin) I have created to solve this.
https://github.com/abhilashd-locus/edittext-locale-aware-thousands-separator
Features:
Add thousands separator dynamically as the user types
Enable editing in between the string and not only at the ends
Style of thousands separation is based upon the locale (eg: 100,000 vs 1,00,000)
Symbol of thousands separator and decimal marker is based on the locale (eg: 100,000.00 vs 100.000,00)
Supports all languages and locales
Disadvantages:
Does not support copy/paste operations
In right-to-left languages (eg. Arabic), the cursor jumps to the end on deleting the first number
.
// ThousandsSeparatorTextWatcher.kt --> add this TextWatcher to the
// EditText you want to add the functionality of dynamic locale aware thousands separator
class ThousandsSeparatorTextWatcher(private var editText: EditText?, private val callback: TextChangedCallback) : TextWatcher {
//keeping a count of the digits before the cursor to reset the cursor at the correct place
private var digitsBeforeCursor = -1
private val thousandSeparator: Char = DecimalFormatSymbols(Locale.getDefault()).groupingSeparator
private val decimalMarker: Char = DecimalFormatSymbols(Locale.getDefault()).decimalSeparator
init {
editText?.apply {
addTextChangedListener(this#ThousandsSeparatorTextWatcher)
//disabling copy/paste to avoid format and parse errors
disableTextSelection(this)
//diabling text selection
isLongClickable = false
setTextIsSelectable(false)
//ensuring correct input type
keyListener = DigitsKeyListener.getInstance("0123456789$decimalMarker");
}
}
private fun disableTextSelection(editText: EditText) {
editText.customSelectionActionModeCallback = object : android.view.ActionMode.Callback {
override fun onActionItemClicked(mode: android.view.ActionMode?, item: MenuItem?) = false
override fun onCreateActionMode(mode: android.view.ActionMode?, menu: Menu?) = false
override fun onPrepareActionMode(mode: android.view.ActionMode?, menu: Menu?) = false
override fun onDestroyActionMode(mode: android.view.ActionMode?) {}
}
}
/***
* We are going to calculate the number of numeric digits before the cursor when user starts editing
* We will keep a count of this number to reset the cursor to the correct position after editing is complete
*/
override fun beforeTextChanged(sequenceBeforeEdit: CharSequence, startPos: Int, count: Int, after: Int) {
val textBeforeEdit = sequenceBeforeEdit.toString()
if (textBeforeEdit.isEmpty()) {
//in an empty string, cursor position is at 1 if a character is being added (after == 1)
//if a character is not being added, cursor position remains at the beginning
digitsBeforeCursor = if (after == 0) -1 else 1
return
}
digitsBeforeCursor = if (after == 0) {
//if characters are being removed
//count will always be 1 since we have disabled selection (in which case count will be equal to the number of characters selected)
val textBeforeNewCursor = textBeforeEdit.substring(0, startPos)
textBeforeNewCursor.count { it != thousandSeparator }
} else {
//if characters are being added
//after will always be 1 since we have disabled pasting (in which case after will be equal to the number of characters being pasted)
if (startPos == textBeforeEdit.length) {
//if adding a character to the end of the string
textBeforeEdit.count { it != thousandSeparator } + 1
} else {
//if adding a character in between the string
val textBeforeNewCursor = textBeforeEdit.substring(0, startPos + 1)
textBeforeNewCursor.count { it != thousandSeparator }
}
}
}
override fun onTextChanged(textAfterEdit: CharSequence, start: Int, before: Int, count: Int) {}
/***
* We will get the numeric value in the editText after stripping all the formatting
* We will then reformat this number to add the correct thousands separation and decimal marker according to the locale
* We then set the cursor to the correct position as we calculated in beforeTextChanged()
*/
override fun afterTextChanged(editable: Editable) {
val text = editable.toString()
//if the EditText is cleared, trigger callback with a null value to indicate an empty field
if (text.isEmpty()) {
digitsBeforeCursor = -1
callback.onChanged(null)
return
}
//get the double value of the entered number
val numberValue = getNumberFromFormattedCurrencyText(text)
//re-format the number to get the correct separation format and symbols
var newText = getCurrencyFormattedAmountValue(numberValue)
//If user was inputting decimal part of the number, reformatting will return a string without decimal point.
//So we need to add it back after the reformatting is complete
if (text.endsWith(decimalMarker)) {
newText += decimalMarker
} else if (text.endsWith(decimalMarker + "0")) {
newText += decimalMarker + "0"
}
//removing the listener to prevent infinite triggers
editText?.removeTextChangedListener(this)
//set the reformatted text
editText?.setText(newText)
//send the number typed to the callback
callback.onChanged(numberValue)
//set the cursor to the right position after reformatting the string
if (digitsBeforeCursor != -1) {
var numbersParsed = 0
for (i in newText.indices) {
if (newText[i] != thousandSeparator) {
numbersParsed++
}
if (numbersParsed == digitsBeforeCursor) {
editText?.setSelection(i + 1)
break
}
}
digitsBeforeCursor = -1
}
//add the listener back
editText?.addTextChangedListener(this)
}
/***
* Function to remove the listener and release reference to the EditText
*/
fun removeWatcherFromEditText() {
editText?.removeTextChangedListener(this)
editText = null
}
interface TextChangedCallback {
fun onChanged(newNumber: Double?)
}
companion object{
#JvmStatic
fun getNumberFromFormattedCurrencyText(formattedText: String?) = formattedText?.let {
val numberFormat = NumberFormat.getNumberInstance(Locale.getDefault())
try {
numberFormat.parse(it)?.toDouble()
} catch (exception: ParseException) {
0.0
}
} ?: 0.0
#JvmStatic
fun getCurrencyFormattedAmountValue(amount: Double?) = amount?.let {
val numberFormat = NumberFormat.getNumberInstance(Locale.getDefault())
numberFormat.maximumFractionDigits = 2
numberFormat.format(amount)
} ?: ""
}
}
I know it's late but maybe can help
fun generate_seprators(input: String?): String? {
var input = input
var result = ""
var float_section = ""
if (input == null) input = ""
var temp = input.trim { it <= ' ' }
temp = temp.replace(",", "")
var input_array = temp.split(".")
var decimal_section = input_array[0]
if(input_array.size>1)
float_section = input_array[1]
if (decimal_section.length > 3) {
var num = 0
for (i in decimal_section.length downTo 1) {
if (num == 3) {
num = 0
result = ",$result"
}
num++
result = decimal_section.substring(i - 1, i) + result
}
if(float_section!="")
result = "$result.$float_section"
} else {
result = decimal_section.replace(",", "")
if(float_section!="")
result = "$result.$float_section"
}
return result
}

How to set custom (arial )type face via xml not via a java code

I already aware of this:-
Typeface typefaceArial= Typeface.createFromAsset(context.getAssets(), "arial.ttf");
but when I create the following class it works but it gives low memory warning issue.
public class MidColorTextView extends TextView
{
private CharSequence text;
private String token;
private static Context context;
private String colorSpan;
private int colorCode;
private static Typeface typefaceArial;
public MidColorTextView( Context context , AttributeSet attrs )
{
super(context, attrs);
this.context=null;
this.context = context;
for(int i = 0; i < attrs.getAttributeCount(); i ++ )
{
// Log.i(TAG, attrs.getAttributeName(i));
/*
* Read value of custom attributes
*/
this.text = attrs.getAttributeValue("http://schemas.android.com/apk/res/com.lht", "text");
this.token = attrs.getAttributeValue("http://schemas.android.com/apk/res/com.lht", "token");
this.colorSpan = attrs.getAttributeValue("http://schemas.android.com/apk/res/com.lht", "colorSpan");
// Log.i("TAG", "token " + token);
// Log.i("TAG", "text " + text);
// Log.i("TAG", "colorSpan " + colorSpan);
}
init();
}
private void init ()
{
if(text.charAt(0) == '#')
{
String tempText = (String) text.subSequence(1, text.length());
this.text = Html.fromHtml(getResources().getString(Integer.parseInt(tempText)));
}
if(token.charAt(0) == '#')
{
String tempText = (String) token.subSequence(1, token.length());
this.token = getResources().getString(Integer.parseInt(tempText));
}
if(colorSpan.charAt(0) == '#')
{
String tempText = (String) colorSpan.subSequence(1, colorSpan.length());
this.colorSpan = getResources().getString(Integer.parseInt(tempText));
}
setColorCode(Color.parseColor(colorSpan));
CharSequence textWitoutToken = null;
String tempString = text.toString();
// ---------checking whether text containg token or not.
if(tempString.contains(token))
{
textWitoutToken = setSpanBetweenTokens(text, token, new ForegroundColorSpan(colorCode));
}
else
{
textWitoutToken = text;
}
textContent = null;
setText(textWitoutToken);
setTypefaceArial ();
setTypeface(getTypefaceArial ());
}
public int getColorCode ()
{
return colorCode;
}
public void setColorCode ( int colorCode )
{
this.colorCode = colorCode;
}
private CharSequence textContent;
public static Typeface getTypefaceArial ()
{
return typefaceArial;
}
public static void setTypefaceArial ()
{
MidColorTextView.typefaceArial= Typeface.createFromAsset(context.getAssets(), "arial.ttf");
}
}
I solved this issue via using singleton class.
I am giving the complete code so that it can help others.
1. Define this in XML:
xmlns:lht="http://schemas.android.com/apk/res/com.lht" android:id="#+id/basicLayout"
<com.xyz.util.MidColorTextView
xyz:token="#"
xyz:colorSpan="#color/BrightRed"
xyz:text="#string/AppraisingStaffBottomText_imanage"
style="#style/contentDescriptionText" />
2. Create class MidColorTextView
package com.xyz.util;
public class MidColorTextView extends TextView {
private CharSequence text;
private String token;
private Context context;
private String colorSpan;
private int colorCode;
public MidColorTextView( Context context , AttributeSet attrs ) {
super(context, attrs);
this.context = context;
for(int i = 0; i < attrs.getAttributeCount(); i ++ ) {
// Log.i(TAG, attrs.getAttributeName(i));
/*
* Read value of custom attributes
*/
this.text = attrs.getAttributeValue("http://schemas.android.com/apk/res/com.xyz", "text");
this.token = attrs.getAttributeValue("http://schemas.android.com/apk/res/com.xyz", "token");
this.colorSpan = attrs.getAttributeValue("http://schemas.android.com/apk/res/com.xyz", "colorSpan");
// Log.i("TAG", "token " + token);
// Log.i("TAG", "text " + text);
// Log.i("TAG", "colorSpan " + colorSpan);
}
init();
}
private void init () {
if(text.charAt(0) == '#') {
String tempText = (String) text.subSequence(1, text.length());
this.text = Html.fromHtml(getResources().getString(Integer.parseInt(tempText)));
}
if(token.charAt(0) == '#') {
String tempText = (String) token.subSequence(1, token.length());
this.token = getResources().getString(Integer.parseInt(tempText));
}
if(colorSpan.charAt(0) == '#')
{
String tempText = (String) colorSpan.subSequence(1, colorSpan.length());
this.colorSpan = getResources().getString(Integer.parseInt(tempText));
}
setColorCode(Color.parseColor(colorSpan));
CharSequence textWitoutToken = null;
String tempString = text.toString();
// ---------checking whether text containg token or not.
if(tempString.contains(token))
{
textWitoutToken = setSpanBetweenTokens(text, token, new ForegroundColorSpan(colorCode));
}
else
{
textWitoutToken = text;
}
textContent = null;
setText(textWitoutToken);
setTypeface(FontManager.getInstance(context).getTypefaceArial ());
}
public void setText ( CharSequence text , String token , int color )
{
setText(setSpanBetweenTokens(text, token, new ForegroundColorSpan(color)));
setTypeface(FontManager.getInstance(context).getTypefaceArial ());
}
public int getColorCode ()
{
return colorCode;
}
public void setColorCode ( int colorCode )
{
this.colorCode = colorCode;
}
private CharSequence textContent;
public CharSequence setSpanBetweenTokens ( CharSequence text , String token , CharacterStyle... cs )
{
// Start and end refer to the points where the span will apply
int tokenLen = token.length();
int start = text.toString().indexOf(token) + tokenLen;
int end = text.toString().indexOf(token, start);
if(start > - 1 && end > - 1)
{
// Copy the spannable string to a mutable spannable string
SpannableStringBuilder ssb = new SpannableStringBuilder(text);
for(CharacterStyle c : cs)
{
ssb.setSpan(c, start, end, 0);
}
// Delete the tokens before and after the span
ssb.delete(end, end + tokenLen);
ssb.delete(start - tokenLen, start);
text = ssb;
textContent = ssb;
String tempString = textContent.toString();
if(tempString.contains(token))
{
setSpanBetweenTokens(textContent, token, new ForegroundColorSpan(colorCode));
}
}
return textContent;
}
}
3. Create class FontManager
public class FontManager {
private Typeface typefaceArial;
private Context context;
private static FontManager instance = null;
private FontManager(Context context) {
this.context = context;
this.typefaceArial= Typeface.createFromAsset(context.getAssets(), "arial.ttf");
}
public synchronized static FontManager getInstance(Context context) {
if(instance == null) {
instance = new FontManager(context);
}
return instance;
}
public Typeface getTypefaceArial () {
return typefaceArial;
}
}
This will solve all your problems.
setSpanBetweenTokens is used for the color text between specific tokens.
Here is a string resource to test on:
<string name="AppraisingStaffBottomText_imanage">The meeting<br><br>PAST<br>Allow the employee to give you
their view of their positive
progress over the past period, focus them on this with open
questions, such as:<br><i>#\"What has been your important contribution over the past
6
months?\"#</i><br><i>#\"What have your learned about your role?\"#</i><br>
<i>#\"What has been your important success?\"#</i><br>Don\'t rake over past mistakes,
don\'t focus on poor performance
- you cannot change that, reserve those discussions future
development - see below<br><br>PRESENT<br>Using open questions, help staff to identify
their true strengths,
capabilities, attributes, skills and attitudes. Create a
comprehensive picture of them as a strategic contributor and
resource.<br>What are your skills, and to what level?<br><i>#\"What have you added as
capabilities over the past months?\"#</i><br><i>#\"What do you find are your
most useful personal attributes in
your role?\"#</i><br><br>FUTURE<br>The future is the period where changes in
capability and
performance can be made.<br>This discussion is where your people can figure out -
with your
help - what development they need to reach your performance
standards and their career goals. It begins with understanding
their career goals, so ....<br><i>#\"What are your goals?\"#</i><br><i>#\"What
development will be needed?\"#</i>
<br><i>#\"You have seen over the past months that you may need more skill in these
areas .......................... what should we do about
that?\"#</i><br><br>Finally: Agree a specific development plan that includes
training/experience in the areas where more skill is needed. Fix dates
in the diary<br><br>The Manager\'s role is one of Mentor and Guide; not
Judge and
Jury</string>
public class MidColorTextView extends TextView {
private String token;
private static Context context;
private String colorSpan;
private int colorCode;
private static Typeface typefaceArial;
public MidColorTextView( Context context , AttributeSet attrs )
{
super(context, attrs);
this.context = context;
init();
}
private void init ()
{
setTypefaceArial ();
setTypeface(getTypefaceArial ());
}
public int getColorCode ()
{
return colorCode;
}
public void setColorCode ( int colorCode )
{
this.colorCode = colorCode;
}
private CharSequence textContent;
public static Typeface getTypefaceArial ()
{
return typefaceArial;
}
public static void setTypefaceArial ()
{
MidColorTextView.typefaceArial= Typeface.createFromAsset(context.getAssets(), "arial.ttf");
}
}
You can apply the typeface as a style
But a specific font, only in java code: ApiDemo
To understand what is causing the low memory error,
make sure it is not a problem with that particular font (for example, try this font, which has been used in this example of a class extending TextView.
reduce the non-specific code and test your class with a bare minimum code (i.e., typefaceArial=Typeface.createFromAsset(context.getAssets(), "arial.ttf") and this.text ="Testing text"; as part of the constructor, eliminating all the attrs.getAttributeValue() methods and specially the init() method, etc.)
Once you get those two changes, you will be able to tell whether is the Typeface assignment a problem, or something else.

Categories

Resources