How do you create an edittext entry that formats input in money format only? When the user enters 5, I want the input to look like "$0.05" and when they then enter 3, the input should now look like "$0.53" and finally they enter 6 and the input should look like "$5.36".
ninjasense's complete solution basically works, but it has some issues:
Every time the data of the field is altered in the "onTextChanged" handler, cursor position resets to index 0 on the field, which is a bit annoying to happen when typing in monetary values.
It uses floats for formatting monetary values, which can backfire.
For the first problem I don't have solution yet, for the second one code like this works:
#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]", "");
StringBuilder cashAmountBuilder = new StringBuilder(userInput);
while (cashAmountBuilder.length() > 3 && cashAmountBuilder.charAt(0) == '0') {
cashAmountBuilder.deleteCharAt(0);
}
while (cashAmountBuilder.length() < 3) {
cashAmountBuilder.insert(0, '0');
}
cashAmountBuilder.insert(cashAmountBuilder.length()-2, '.');
cashAmountBuilder.insert(0, '$');
cashAmountEdit.setText(cashAmountBuilder.toString());
}
}
Building off Zds.
For keeping the cursor positioned at the end of the field use this.
cashAmountEdit.setTextKeepState(cashAmountBuilder.toString());
Selection.setSelection(cashAmountEdit.getText(), cashAmountBuilder.toString().length());
You can use a TextWatcher to do that kind of thing.
Extend TextWatcher: http://d.android.com/reference/android/text/TextWatcher.html
public class MyTextWatcher implements TextWatcher {
public void afterTextChanged(Editable arg0) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}
Then add it to your editText with
myEditText.addTextChangedListener(new MyTextWatcher());
I found the TextWatcher to be a bit cumbersome. Instead, you can set the key listener:
setKeyListener(new CalculatorKeyListener());
// Must be called after setKeyListener(), otherwise is overridden
setRawInputType(Configuration.KEYBOARD_12KEY);
And then create a KeyListener which extends NumberKeyListener:
class CalculatorKeyListener extends NumberKeyListener {
#Override
public int getInputType() {
return InputType.TYPE_CLASS_NUMBER;
}
#Override
public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
digitPressed(keyCode - KeyEvent.KEYCODE_0);
} else if (keyCode == KeyEvent.KEYCODE_DEL) {
deletePressed();
}
return true;
}
#Override
protected char[] getAcceptedChars() {
return new char[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}
}
You then need to display the characters correctly, but that's not difficult; just keep track of cents, and then divide or multiply by 10, and use a NumberFormat to get the formatting correct.
Heres my complete solution:
tvValue.setRawInputType(Configuration.KEYBOARD_12KEY);
tvValue.addTextChangedListener(new TextWatcher(){
#Override
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
// TODO Auto-generated method stub
}
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
// TODO Auto-generated method stub
// here i converted to string
if(!s.toString().matches("^\\$(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$"))
{
String userInput= ""+s.toString().replaceAll("[^\\d]", "");
Float in=Float.parseFloat(userInput);
float percen = in/100;
tvValue.setText("$"+percen);
}
}
});
I did this but without decimal and with dot for miles, check the code and add the functionality to support decimals.
MyEditText.addTextChangedListener(new TextWatcher()
{
#Override
public void onTextChanged(CharSequence s, int start, int before, int count){
if(s.toString().length() > 0){
MyEditText.removeTextChangedListener(this);
String numbers = removeCharacters(s.toString());
int money = 0;
try{
money = Integer.parseInt(numbers);
}
catch(Exception ex){
money = 0;
}
MyEditText.setText(getMoney(money));
//Set cursor on correct position
int selection = start;
if(count > 0){
selection++;
if(MyEditText.getText().toString().length() == 2 || MyEditText.getText().toString().length() == 6 || MyEditText.getText().toString().length() == 10){
selection++;
}
}
else{
if(MyEditText.getText().toString().length() == 4 || MyEditText.getText().toString().length() == 8){
selection--;
}
}
if(selection > MyEditText.getText().toString().length()){
selection = MyEditText.getText().toString().length();
}
MyEditText.setSelection(selection);
MyEditText.addTextChangedListener(this);
}
if(s.toString().length() == 1 && count < 1 && start == 1){
MyEditText.removeTextChangedListener(this);
MyEditText.setText("");
MyEditText.addTextChangedListener(this);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after){
}
#Override
public void afterTextChanged(Editable s)
{
}
});
public String removeCharacters(String money){
int i=0;
while (i<money.length())
{
Character c = money.charAt(i);
if (Character.isDigit(c) && c != '.')
{
i++;
}
else
{
money = money.replace(c.toString(), "");
}
}
return money;
}
public String getMoney(int value){
String money = "$";
NumberFormat numberFormatter;
numberFormatter = NumberFormat.getNumberInstance(Locale.GERMAN);
money += numberFormatter.format(value);
return money;
}
This answer is based on Zds' answer (which in turn was based on ninjasense's answer), but this should resolve the cursor position issue:
if(!text.matches("^\\$(\\d{1,2})(\\.\\d{2})?$")) {
int originalCursorPosition = view.getSelectionStart();
int cursorOffset = 0;
boolean cursorAtEnd = originalCursorPosition == text.length();
String userInput= ""+text.replaceAll("[^\\d]", "");
StringBuilder cashAmountBuilder = new StringBuilder(userInput);
while (cashAmountBuilder.length() > 3 && cashAmountBuilder.charAt(0) == '0') {
cashAmountBuilder.deleteCharAt(0);
cursorOffset--;
}
while (cashAmountBuilder.length() < 3) {
cashAmountBuilder.insert(0, '0');
cursorOffset++;
}
cashAmountBuilder.insert(cashAmountBuilder.length() - 2, '.');
cashAmountBuilder.insert(0, '$');
view.setText(cashAmountBuilder.toString());
view.setSelection(cursorAtEnd ? view.getText().length() : originalCursorPosition + cursorOffset);
}
Notes:
The following is in a TextWatcher.onTextChanged
I'm using a different regex than other answers, which keeps the price to < $100
'view' is the editText, 'text' is the string contents
this has worked for me using an EditText with a maxLength of 6 (i.e. $00.00)
Related
I'm trying to make editText, where I am inserting some text. After each three characters,I want to insert dash.
Example:
Type: 123
Result:123-
Now when cursor is behind dash and you press delete, I want to delete dash and character behind dash.
For example:
123-
result after delete key: 12.
How to do it. Thank you for advice.
EDIT
my code is:
EditText editText;
boolean keyDel = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText) findViewById(R.id.editText);
editText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL) {
keyDel = true;
}
return keyDel;
}
});
editText.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 str = s.toString();
System.out.println(str.length());
if (str.length() == 3) {
str = str + "-";
} else if (str.length() == 7) {
str = str + "-";
} else if (str.length() % 4 == 0 && keyDel == true) {
str = str.substring(0, str.length() - 2);
} else {
return;
}
editText.setText(str);
editText.setSelection(editText.getText().length());
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
I found Android 4.4.2 and higer doesn´t support keyevent.
onTextChanged is called everytime you add and remove something. So if your String has length 3, you add your - and the new length is 4. If you press delete (new length is 3 again), onTextChanged is called and - is added again. SO only add something if nothing has been removed from the text.
if (count > before) {
if (count == 3 || count == 7) {
str = str + "-";
} else {
return;
}
input.setText(str);
input.setSelection(input.getText().length());
}
I was inspired by this answer to achieve what you want:
String mTextValue;
Character mLastChar = '\0'; // init with empty character
int mKeyDel;
myEditText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
boolean flag = true;
String eachBlock[] = myEditText.getText().toString().split("-");
for (int i = 0; i < eachBlock.length; i++) {
if (eachBlock[i].length() > 4) {
flag = false;
}
}
if (flag) {
myEditText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL)
mKeyDel = 1;
return false;
}
});
if (mKeyDel == 0) {
if (((myEditText.getText().length() + 1) % 4) == 0) {
myEditText.setText(myEditTex.getText() + "-");
myEditText.setSelection(myEditText.getText().length());
}
mTextValue = myEditText.getText().toString();
} else {
mTextValue = myEditText.getText().toString();
if (mLastChar.equals('-')) {
mTextValue = mTextValue.substring(0, mTextValue.length() - 1);
myEditText.setText(mTextValue);
myEditText.setSelection(mTextValue.length());
}
mKeyDel = 0;
}
} else {
myEditText.setText(mTextValue);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (s.length()>0) {// save the last char value
mLastChar = s.charAt(s.length() - 1);
} else {
mLastChar = '\0';
}
}
#Override
public void afterTextChanged(Editable s) {}
});
I'm rather new to android programming myself. Nevertheless, I think that everytime you call "setText" you are trigging a new onTextChanged event.
In my app I remove the listener, set the text and then add the listener again in order to avoid this problem. But for doing this you'll have to save the reference to the TextWatcher.
I.e. given your Activity extends TextWatcher:
edittext.removeTextChangedListener(this);
editText.setText(str);
edittext.addTextChangedListener(this);
You can also put the cursor at the end using:
myedittext.append("");
I want to format the input which is in the form of number in EditText.The format is 01-133134-124. I wanted first - after 2 number then next dash after 6 numbers.I tried but whenever I pressed delete/backspace because of the wrong entry the format stopped working and no dash is placed after input of 2 number or 6 numbers.Here is the code.Where Enrollement is the EditText field.
Format : 2digitnumber-6dignumber-3dignumber
Enrollement.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Enrollement.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL)
keyDel = 1;
return false;
}
});
if (keyDel == 0) {
int len = Enrollement.getText().length();
if (len == 2) {
int leng = Enrollement.getText().length();
if(leng==2) {
Enrollement.setText(Enrollement.getText() + "-");
Enrollement.setSelection(Enrollement.getText().length());
}
} else if (len == 9) {
int leng = Enrollement.getText().length();
if(leng==9) {
Enrollement.setText(Enrollement.getText() + "-");
Enrollement.setSelection(Enrollement.getText().length());
}
}
} else {
keyDel = 0;
}
}
#Override
public void afterTextChanged(Editable arg0) {
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
});
It would be much easier if you changed your approach a bit. You could be always analyzing unformatted verion of your string after every single change in your EditText.
Try this (remember to set android:maxLength="13" for your EditText):
Enrollement.addTextChangedListener(new TextWatcher() {
final int[] blockLengths = new int[]{2, 6, 3};
String mUnformatted = "";
#Override
public void onTextChanged (CharSequence s,int start, int before, int count){
String unformattedSeq = s.toString().replace("-", "");
if (mUnformatted.length() == unformattedSeq.length()) {
return; //length of text has not changed
}
mUnformatted = unformattedSeq;
//formatting sequence
StringBuilder formatted = new StringBuilder();
int blockIndex = 0;
int currentBlock = 0;
for (int i = 0; i < mUnformatted.length(); ++i) {
if (currentBlock == blockLengths[blockIndex]) {
formatted.append("-");
currentBlock = 0;
blockIndex++;
}
formatted.append(mUnformatted.charAt(i));
currentBlock++;
}
Enrollement.setText(formatted.toString());
Enrollement.setSelection(formatted.length());
}
#Override
public void beforeTextChanged (CharSequence s,int start, int count, int after){
}
#Override
public void afterTextChanged (Editable s){
}
});
This code should work correctly. You could only improve the setSelection part (currently if you delete a number in the middle of your EditText it will move your cursor to the end of the text).
In your onTextChanged method you could do:
String str = YourEditText.getText().toString();
if((str.length()==2 && len <str.length()) || (str.length()==6 && len <str.length())){
YourEditText.append("-"); }
Also,
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
String str = YourEditText.getText().toString();
len = str.length();
}
Adapted from this article.
I need the EditText allow only seven integer and two decimal numbers. Ex: 7777777.99
I try with this Regex, in onTouchListener event, but not working. By the way, this is the correct event to do this??
txtRespNumero.addTextChangedListener(new TextWatcher() {
int count = 0; // Declare as Instance Variable
boolean isSeven = true; // Declare as Instance Variable
public void onTextChanged(CharSequence s, int start, int before,
int count) {
count++;
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
if(isSeven){
if(count == 7){
s.append(".");
isSeven = true;
}
}
if(count < 7){
isSeven = true;
}
}
});
Try it this way...
- Set the EditText Attribute Max Length as 10.
- Then when you accept the EditeText value, convert it into format of 0000000.00 using the below example:
Eg:
double d = 300.0;
DecimalFormat df = new DecimalFormat("0000000.00");
System.out.println(df.format(d));
/////////////////////////////////// Edited Part /////////////////////////////
Another way to do it, just as you want it.........
int count = 0; // Declare as Instance Variable
boolean isSix = true; // Declare as Instance Variable
tx = (EditText) findViewById(R.id.editText_CheckIt);
tx.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
count++;
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
if(isSeven){
if(count == 7){
s.append(".");
isSeven = true;
}
}
if(count < 7){
isSeven = true;
}
}
});
Try the onTextChanged rather, it get called everytime the user enters a numer (In your case) instead of only once when the control is touched). This solution worked for me:
EditText no more than x decimals android
It is a pity that android does not allow you do this directly in the XML though.
I have an edittext, and a textwatcher that watches if SPACE arrived or not. If its a SPACE I would like to delete that instantly. Or if its a space I want to make sure it doesnt appear but indicate somehow (seterror, toast) for the user that space is not allowed.
edittext.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
//---//
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
I cannot define onkeydown in the afterTextChaned method, since it gives me an error.
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
if (keyCode == KeyEvent.KEYCODE_SPACE) {
}
}
So it is not working (syntax error, misplaced construct for the int keyCode.
Thanks you in advance!
The solution is as usually much simpler:
#Override
public void afterTextChanged(Editable s) {
String result = s.toString().replaceAll(" ", "");
if (!s.toString().equals(result)) {
ed.setText(result);
ed.setSelection(result.length());
// alert the user
}
}
This shouldn't have the problems of the previous attempts.
setSelection is there to set the cursor again at the end of your EditText:
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2,
int arg3) {}
#Override
public void beforeTextChanged(CharSequence s, int arg1, int arg2,
int arg3) {}
#Override
public void afterTextChanged(Editable arg0) {
if(editText.getText().toString().contains(" ")){ editText.setText(editText.getText().toString().replaceAll(" " , ""));
editText.setSelection(editText.getText().length());
Toast.makeText(getApplicationContext(), "No Spaces Allowed", Toast.LENGTH_LONG).show();
}
}});
boolean editclicked =false ;
edittext.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
editclicked = false ;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {
editclicked = true;
});
Put this as a separate function:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (editclicked) {
if (keyCode == KeyEvent.KEYCODE_SPACE) {
return false
}
} else {
super.onKeyDown(keyCode, event);
}
}
#Override
public void afterTextChanged(Editable s) {
String result = s.toString().replaceAll("\\s", "");
if (!s.toString().equals(result)) {
int pos = editText.getSelectionStart() - (s.length() - result.length());
editText.setText(result);
editText.setSelection(Math.max(0,Math.min(pos, result.length())));
editText.setError("No spaces allowed");
}
}
\s matches any whitespace character (equal to [\r\n\t\f\v ])
Setting selection like this, allow you to enter or paste text in middle of edittext without loosing cursor position
My relatively simple solution for instant whitespace deletion without removing spannables (styles) in EditText:
Remove at start:
#Override
public void afterTextChanged(Editable s) {
int i;
for (i = 0; i < s.length() && Character.isWhitespace(s.charAt(i)); i++) { ; }
s.replace(0, i, "");
}
Basically that's it, but you can also do:
Remove at start (without interrupting first input):
#Override
public void afterTextChanged(Editable s) {
String text = s.toString();
if(!text.trim().isEmpty()){
int i;
for (i = 0; i < s.length() && Character.isWhitespace(s.charAt(i)); i++) { ; }
s.replace(0, i, "");
}
}
Removing at start and end (allow 1 whitespace at end for convinient input):
#Override
public void afterTextChanged(Editable s) {
int i;
//remove at start
for (i = 0; i < s.length() && Character.isWhitespace(s.charAt(i)); i++) { ; }
s.replace(0, i, "");
//remove at end, but allow one whitespace character
for (i = s.length(); i > 1 && Character.isWhitespace(s.charAt(i-1)) && Character.isWhitespace(s.charAt(i-2)); i--) { ; }
s.replace(i, s.length(), "");
}
For removing the space instantly you can achieve it by two ways.
One simple solution you can set the digits to your edit text.
android:digits="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
second way you can set a filter
EditText.setFilters(new InputFilter[] { filter });
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (Character.isSpaceChar(source.charAt(i))) {
return "";
}
}
return null;
}
}
One more simple way to achieve this using the input Filter
editText.setFilters(new InputFilter[]{new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (source.toString().equalsIgnoreCase(" ")){
return "";
}
return source;
}
}});
This will remove the space entered by the user immediately and gives appearance like space is disabled.
how to make edit text accept input in format
4digitnumber-4dignumber-4dignumber-4dignumber
The code
text.addTextChangedListener(new TextWatcher() {
int len = 0;
String string ;
#Override
public void afterTextChanged(Editable s) {
text.setOnKeyListener(new OnKeyListener()
{ public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_DEL)
{
}
else{
string = text.getText().toString();
len = string.length()+1;
if(len%5==0){text.append("-");}
}
return false; } });
}
});
works fine upon adding, but deleting or editing causes problem.
Now this works fine for soft/hard keyboard for all delete/edit ops.
tx 4 ur help..
package com.and;
import android.app.Activity;
import android.app.AlertDialog;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextWatcher;
import android.text.format.Formatter;
import android.text.method.NumberKeyListener;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;
import android.widget.Toast;
public class ccformat extends Activity {
String a;
int keyDel;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final EditText text = (EditText) findViewById(com.and.R.id.editText1);
text.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
boolean flag = true;
String eachBlock[] = text.getText().toString().split("-");
for (int i = 0; i < eachBlock.length; i++) {
if (eachBlock[i].length() > 4) {
flag = false;
}
}
if (flag) {
text.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL)
keyDel = 1;
return false;
}
});
if (keyDel == 0) {
if (((text.getText().length() + 1) % 5) == 0) {
if (text.getText().toString().split("-").length <= 3) {
text.setText(text.getText() + "-");
text.setSelection(text.getText().length());
}
}
a = text.getText().toString();
} else {
a = text.getText().toString();
keyDel = 0;
}
} else {
text.setText(a);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
}
This is working:
public class EditTextSample extends Activity {
// This regexp has to be improved, it does not detect case where you have
// more than 4 digits in a middle group like: 1234-12345-123
static final Pattern CODE_PATTERN = Pattern.compile("([0-9]{0,4})|([0-9]{4}-)+|([0-9]{4}-[0-9]{0,4})+");
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_text_sample);
final EditText editText = (EditText) findViewById(R.id.input);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
Log.w("", "input" + s.toString());
if (s.length() > 0 && !CODE_PATTERN.matcher(s).matches()) {
String input = s.toString();
String numbersOnly = keepNumbersOnly(input);
String code = formatNumbersAsCode(numbersOnly);
Log.w("", "numbersOnly" + numbersOnly);
Log.w("", "code" + code);
editText.removeTextChangedListener(this);
editText.setText(code);
// You could also remember the previous position of the cursor
editText.setSelection(code.length());
editText.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) {
}
private String keepNumbersOnly(CharSequence s) {
return s.toString().replaceAll("[^0-9]", ""); // Should of course be more robust
}
private String formatNumbersAsCode(CharSequence s) {
int groupDigits = 0;
String tmp = "";
for (int i = 0; i < s.length(); ++i) {
tmp += s.charAt(i);
++groupDigits;
if (groupDigits == 4) {
tmp += "-";
groupDigits = 0;
}
}
return tmp;
}
});
}
}
If you want to just group visually the numbers, but you don't want to alter the value of the EditText adding dashes, you can use this Span approach:
EditText editText = findViewById(R.id.editText);
editText.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) {}
#Override
public void afterTextChanged(Editable editable) {
Object[] paddingSpans = editable.getSpans(0, editable.length(), DashSpan.class);
for (Object span : paddingSpans) {
editable.removeSpan(span);
}
addSpans(editable);
}
private static final int GROUP_SIZE = 4;
private void addSpans(Editable editable) {
final int length = editable.length();
for (int i = 1; i * (GROUP_SIZE) < length; i++) {
int index = i * GROUP_SIZE;
editable.setSpan(new DashSpan(), index - 1, index,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
});
where the DashSpan class looks like this:
/**
* A {#link ReplacementSpan} used for spacing in {#link android.widget.EditText}
* to space things out. Adds '-'s
*/
public class DashSpan extends ReplacementSpan {
#Override
public int getSize(#NonNull Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
float padding = paint.measureText("-", 0, 1);
float textSize = paint.measureText(text, start, end);
return (int) (padding + textSize);
}
#Override
public void draw(#NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
int bottom, #NonNull Paint paint) {
canvas.drawText(text.subSequence(start, end) + "-", x, y, paint);
}
}
This way you will have visually the grouping using the dashes, but the getText() will return the text without any grouping.
To force only numbers you can add the attributes android:digits="0123456789" and android:inputType="number" to the EditText.
This solution is based on the code of this library.
In my case below code is working fine.
editTextCreditCard.addTextChangedListener(new FourDigitCardFormatWatcher());
Add custom class for TextWatcher.
public class FourDigitCardFormatWatcher implements TextWatcher {
private static final char space = ' ';
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
if (s.length() > 0 && (s.length() % 5) == 0) {
final char c = s.charAt(s.length() - 1);
if (space == c) {
s.delete(s.length() - 1, s.length());
}
}
if (s.length() > 0 && (s.length() % 5) == 0) {
char c = s.charAt(s.length() - 1);
if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(space)).length <= 3) {
s.insert(s.length() - 1, String.valueOf(space));
}
}
}
}
Hope this would help you.
It works in all cases, when you insert or remove a character, the format will always be right. Make sure you set
android:inputType="number"
/
myEditText.addTextChangedListener(new TextWatcher() {
private final String space = "-"; // you can change this to whatever you want
private final Pattern pattern = Pattern.compile("^(\\d{4}"+space+"{1}){0,3}\\d{1,4}$"); // check whether we need to modify or not
#Override
public void onTextChanged(CharSequence s, int st, int be, int count) {
String currentText = myEditText.getText().toString();
if (currentText.isEmpty() || pattern.matcher(currentText).matches())
return; // no need to modify
String numbersOnly = currentText.trim().replaceAll("[^\\d.]", "");; // remove everything but numbers
String formatted = "";
for(int i = 0; i < numbersOnly.length(); i += 4)
if (i + 4 < numbersOnly.length())
formatted += numbersOnly.substring(i,i+4)+space;
else
formatted += numbersOnly.substring(i);
myEditText.setText(formatted);
myEditText.setSelection(myEditText.getText().toString().length());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void afterTextChanged(Editable e) {}
});
It seems to me the answers presented here do not work properly with delete, delete from the middle operations, etc.
Here is my code. It doesn't restrict the length of input, but seems to be ok with various insertions and deletions:
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
public class HyphenDelimitTextWatcher implements TextWatcher {
EditText mEditText;
boolean mInside = false;
boolean mWannaDeleteHyphen = false;
boolean mKeyListenerSet = false;
final static String MARKER = "|"; // filtered in layout not to be in the string
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(!mKeyListenerSet) {
mEditText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
try {
mWannaDeleteHyphen = (keyCode == KeyEvent.KEYCODE_DEL
&& mEditText.getSelectionEnd() - mEditText.getSelectionStart() <= 1
&& mEditText.getSelectionStart() > 0
&& mEditText.getText().toString().charAt(mEditText.getSelectionEnd() - 1) == '-');
} catch (IndexOutOfBoundsException e) {
// never to happen because of checks
}
return false;
}
});
mKeyListenerSet = true;
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mInside) // to avoid recursive calls
return;
mInside = true;
int currentPos = mEditText.getSelectionStart();
String string = mEditText.getText().toString().toUpperCase();
String newString = makePrettyString(string);
mEditText.setText(newString);
try {
mEditText.setSelection(getCursorPos(string, newString, currentPos, mWannaDeleteHyphen));
} catch (IndexOutOfBoundsException e) {
mEditText.setSelection(mEditText.length()); // last resort never to happen
}
mWannaDeleteHyphen = false;
mInside = false;
}
#Override
public void afterTextChanged(Editable s) {
}
private String makePrettyString(String string) {
String number = string.replaceAll("-", "");
boolean isEndHyphen = string.endsWith("-") && (number.length()%4 == 0);
return number.replaceAll("(.{4}(?!$))", "$1-") + (isEndHyphen ?"-":"");
}
private int getCursorPos(String oldString, String newString, int oldPos, boolean isDeleteHyphen) {
int cursorPos = newString.length();
if(oldPos != oldString.length()) {
String stringWithMarker = oldString.substring(0, oldPos) + MARKER + oldString.substring(oldPos);
cursorPos = (makePrettyString(stringWithMarker)).indexOf(MARKER);
if(isDeleteHyphen)
cursorPos -= 1;
}
return cursorPos;
}
public HyphenDelimitTextWatcher(EditText editText) {
mEditText = editText;
}
}
Usage:
mSomeEditText.addTextChangedListener(new HyphenDelimitTextWatcher(mSomeEditText));
if you neeed this efect,ou can use this code in EditText
Here is a formatting regex used to show card details in format XXXX XXXX XXXX XXXX
etCreditCardNumber.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) {
etCreditCardNumber.setFloatingLabel(MaterialEditText.FLOATING_LABEL_HIGHLIGHT);
String initial = s.toString();
// remove all non-digits characters
String processed = initial.replaceAll("\\D", "");
// insert a space after all groups of 4 digits that are followed by another digit
processed = processed.replaceAll("(\\d{4})(?=\\d)(?=\\d)(?=\\d)", "$1 ");
//Remove the listener
etCreditCardNumber.removeTextChangedListener(this);
int index = etCreditCardNumber.getSelectionEnd();
if (index == 5 || index == 10 || index == 15)
if (count > before)
index++;
else
index--;
//Assign processed text
etCreditCardNumber.setText(processed);
try {
etCreditCardNumber.setSelection(index);
} catch (Exception e) {
e.printStackTrace();
etCreditCardNumber.setSelection(s.length() - 1);
}
//Give back the listener
etCreditCardNumber.addTextChangedListener(this);
}
#Override
public void afterTextChanged(Editable s) {
}
});