I have following issue. I have EditText and TextWatcher which format input text according to some rules. In method afterTextChanged() I format it. Then I have formatted string and I want to replace old value of EditText by formatted value.
Next we have two options:
use EditText.setText()
use Editable.replace()
If we use first option, EditText works very slowly and looses symbols.
But If we use second method, Editable doesn't replace old text, but append new text to old text.
Maybe someone had similar issue?
Upd: using Editable.clear() then Editable.append() or insert() have similar effect
Code:
public static class LoginWatcher implements TextWatcher {
private EditText target;
private LoginFilter loginFilter = new LoginFilter();
private int lastLength;
private boolean wasPhoneNumber = false;
private AsYouTypeFormatter formatter;
private boolean isFormattingStopped;
public LoginWatcher(OnLoginEnterListener onLoginInputListener, EditText target) {
listener = onLoginInputListener;
this.target = target;
lastLength = target.getText().length();
formatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(Locale.getDefault().getCountry());
}
#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 (isFormattingStopped) {
return;
}
if (count > 0 && hasSeparator(s, start, count)) {
stopFormatting();
}
}
#Override
public void afterTextChanged(Editable s) {
target.removeTextChangedListener(this);
boolean isSymbolsChecked = loginFilter.check(s.toString());
boolean isEmail = StringUtils.isEmailValid(s.toString());
boolean isPhoneNumber = isPhoneNumber(s.toString());
if (lastLength <= s.length()) {
if (isPhoneNumber && !isFormattingStopped) {
String formatted = reformat(s, Selection.getSelectionEnd(s));
if (formatted != null) {
target.setText(formatted);
target.setSelection(target.getText().length());
}
} else if (wasPhoneNumber) {
String unformatted = unFormatPhoneNumber(s.toString());
target.setText(unformatted); // or s.clear(); s.append();
target.setSelection(target.getText().length());
}
}
lastLength = s.length();
wasPhoneNumber = isPhoneNumber;
if (isFormattingStopped) {
isFormattingStopped = s.length() != 0;
}
target.addTextChangedListener(this);
}
private String unFormatPhoneNumber(String s) {
char[] chars = s.toCharArray();
if (s.isEmpty()) {
return s;
}
if (chars[0] == '+') {
boolean isPhoneNumber = true;
for (int i = 1; i < chars.length; ++i) {
if (!Character.isDigit(chars[i])) {
isPhoneNumber = false;
break;
}
}
if (isPhoneNumber) {
return s;
}
}
return s.replaceAll("[\\+\\(\\)\\s\\-]+", "");
}
private String reformat(CharSequence s, int cursor) {
int curIndex = cursor - 1;
String formatted = null;
formatter.clear();
char lastNonSeparator = 0;
boolean hasCursor = false;
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (PhoneNumberUtils.isNonSeparator(c)) {
if (lastNonSeparator != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor);
hasCursor = false;
}
lastNonSeparator = c;
}
if (i == curIndex) {
hasCursor = true;
}
}
if (lastNonSeparator != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor);
}
return formatted;
}
private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
return hasCursor ? formatter.inputDigitAndRememberPosition(lastNonSeparator)
: formatter.inputDigit(lastNonSeparator);
}
private boolean isPhoneNumber(String s) {
return !TextUtils.isEmpty(s) && Patterns.PHONE.matcher(s).matches();
}
private boolean hasSeparator(final CharSequence s, final int start, final int count) {
for (int i = start; i < start + count; i++) {
char c = s.charAt(i);
if (!PhoneNumberUtils.isNonSeparator(c)) {
return true;
}
}
return false;
}
private void stopFormatting() {
isFormattingStopped = true;
formatter.clear();
}
}
Try to use the methods provided by Editable
#Override
public void afterTextChanged(Editable s) {
target.removeTextChangedListener(this);
boolean isSymbolsChecked = loginFilter.check(s.toString());
boolean isEmail = StringUtils.isEmailValid(s.toString());
boolean isPhoneNumber = isPhoneNumber(s.toString());
if (lastLength <= s.length()) {
if (isPhoneNumber && !isFormattingStopped) {
String formatted = reformat(s, Selection.getSelectionEnd(s));
if (formatted != null) {
s.replace(0, s.length(), formatted)
target.setSelection(formatted.length());
}
} else if (wasPhoneNumber) {
String unformatted = unFormatPhoneNumber(s.toString());
s.replace(0, s.length(), formatted)
target.setSelection(formatted.length());
}
}
lastLength = s.length();
wasPhoneNumber = isPhoneNumber;
if (isFormattingStopped) {
isFormattingStopped = s.length() != 0;
}
target.addTextChangedListener(this);
}
Related
I am using this TextWatcher for .addtextOnChangeListener, the output of the string is ex: "123,456,77" i want it to be "123.456,77" if i use the replace method on "et" in the "afterTextChanged" method, the number isn't even formatting. With the code below the listener works and everything, i just don't know how to replace the "," with "." until the decimals.
If you think to just change the pattern ("###,##0,00") it doesn't work
This is the TextWatcher I have for the EditText
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Locale;
public class NumberTextWatcher implements TextWatcher {
private DecimalFormat df;
private DecimalFormat dfnd;
private boolean hasFractionalPart;
private EditText et;
public NumberTextWatcher(EditText et)
{
df = new DecimalFormat("###,##0,00");
df.setDecimalSeparatorAlwaysShown(true);
dfnd = new DecimalFormat("###,##0,00");
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) {
int index = s.toString().indexOf(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator()));
int trailingZeroCount = 0;
if (index > -1)
{
for (index++; index < s.length(); index++) {
if (s.charAt(index) == '0')
trailingZeroCount++;
else {
trailingZeroCount = 0;
}
}
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
{
if (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator())))
{
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
}
}
}
Use this:
DecimalFormat decimalFormat=new DecimalFormat();
DecimalFormatSymbols decimalFormatSymbols=DecimalFormatSymbols.getInstance();
decimalFormatSymbols.setDecimalSeparator(',');
decimalFormatSymbols.setGroupingSeparator('.');
decimalFormat.setDecimalFormatSymbols(decimalFormatSymbols);
String formattedNumber=decimalFormat.format(123456.78);// prints 123.456,78
Рow to use if statements between two intent activities? I have two different activities collecting data from external sensors. I would like to control some GPIO pins of Activity1 based on the sensor values of Activity2.
Activity 1
public class RealTimeData extends AppCompatActivity {
private static final String TAG = "sensor_data";
private static final String FRAGMENT_DIALOG = "dialog";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_real_time_data);
Resources res = getResources();
GetAllResources();
OpenSerialPort();
}
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
private void GetAllResources() {
pressure1 = (TextView) findViewById(R.id.pressure);
temp1 = (TextView) findViewById(R.id.temperature);
co2ppm1 = (TextView) findViewById(R.id.co2);
humidity1 = (TextView) findViewById(R.id.humidity);
pressure1data = (TextView) findViewById(R.id.pressureData);
temp1data = (TextView) findViewById(R.id.temperatureData);
co2ppm1data = (TextView) findViewById(R.id.co2Data);
humidity1data = (TextView) findViewById(R.id.humidityData);
}
private sensorDataApplication sensordata = new sensorDataApplication();
private UartApplication[] mSerialport = {null, null};
private void SetValues()
{
TextView tv =(TextView) findViewById(R.id.pressureData);
tv.setText(String.valueOf(sensordata.get_pressure_value(0)));
tv =(TextView) findViewById(R.id.temperatureData);
tv.setText(String.valueOf(sensordata.get_temperature_value(0)));
tv =(TextView) findViewById(R.id.co2Data);
tv.setText(String.valueOf(sensordata.get_co2_value(0)));
tv =(TextView) findViewById(R.id.humidityData);
tv.setText(String.valueOf(sensordata.get_humidity_value(0)));
}
private void OpenSerialPort() {
new sensorDataApplication();
try {
int i =0;
for(String devicePath : Configs.uartdevicePath) {
mSerialport[i] = new UartApplication(new File(devicePath), mReaderCallback);
i++;
}
} catch (SecurityException e) {
ErrorMessage.newInstance(getString(R.string.error_serial))
.show(getFragmentManager(), FRAGMENT_DIALOG);
} catch (IOException e) {
ErrorMessage.newInstance(getString(R.string.error_unknown))
.show(getFragmentManager(), FRAGMENT_DIALOG);
} catch (InvalidParameterException e) {
ErrorMessage.newInstance(getString(R.string.error_uart_config))
.show(getFragmentManager(), FRAGMENT_DIALOG);
}
}
private final UartApplication.ReaderCallback mReaderCallback=
new UartApplication.ReaderCallback() {
#Override
public void onDataReceived(final byte[] buffer, final int size) {
runOnUiThread(new Runnable() {
public void run() {
if (pressure1 != null) {
String received_str = new String(buffer, 0, size);
//uartRx.append(received_str);
sensordata.parse_and_update_sensordata(received_str);
SetValues();
Log.e(TAG,"received packet "+received_str);
}
}
});
}
};
TextWatcher mUartTxCallback = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(max(start,before) < s.length()) {
String changedStr = s.toString().substring(max(start, before), s.length());
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable var1) {
}
};
private void CloseSerialPort(int idx)
{
mSerialport[idx].closeSerialPort();
}
private void WriteSerialPort(String writeString, int idx)
{
mSerialport[idx].writeData(writeString);
}
private static byte[] hexStringToByteArray(String s) {
int len = s.length();
s.toUpperCase();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
private static String toHexString(byte[] ba) {
StringBuilder str = new StringBuilder();
for (int i = 0; i < ba.length; i++)
str.append(String.format("%02x", ba[i]));
return str.toString().toUpperCase();
}
private TextView pressure1;
private TextView temp1;
private TextView co2ppm1;
private TextView humidity1;
private TextView pressure1data;
private TextView temp1data;
private TextView co2ppm1data;
private TextView humidity1data;
}
Activity 2
public class ManualControl extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
List<gpioApplication> mGPIO = new ArrayList<>();
private static final String FRAGMENT_DIALOG = "dialog";
private int[] mGPIOList = null;
private CheckBox[] cbGPIO;
private final gpioApplication.InterruptCallback mGPIOCallback =
new gpioApplication.InterruptCallback() {
#Override
public void onDataReceived(final int GPIOnum, final int value) {
runOnUiThread(new Runnable() {
public void run() {
//Do Nothing
CheckBox cbnGPIO = GetCheckBoxGpio(GPIOnum);
cbnGPIO.setOnCheckedChangeListener(null);
if (cbnGPIO != null)
cbnGPIO.setChecked(value > 0 ? true : false);
cbnGPIO.setOnCheckedChangeListener(mGPIOCheckBoxCallback);
}
});
}
};
private final CompoundButton.OnCheckedChangeListener mGPIOCheckBoxCallback =
new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//do stuff
int gpioID = Integer.parseInt(buttonView.getHint().toString());
int GPIOValue = buttonView.isChecked() == true ? 1 : 0;
if (gpioID < mGPIOList.length) {
gpioApplication gpio = mGPIO.get(gpioID);
gpio.GPIOWrite(GPIOValue);
}
}
};
private CheckBox GetCheckBoxGpio(int GPIOnum) {
if (GPIOnum < mGPIOList.length)
return cbGPIO[GPIOnum];
return null;
}
private void GetAllResources() {
cbGPIO = new CheckBox[20];
cbGPIO[9] = (CheckBox) findViewById(R.id.checkBox_GPIO92);
cbGPIO[0] = (CheckBox) findViewById(R.id.checkBox_GPIO16);
cbGPIO[1] = (CheckBox) findViewById(R.id.checkBox_GPIO17);
cbGPIO[6] = (CheckBox) findViewById(R.id.checkBox_GPIO69);
cbGPIO[2] = (CheckBox) findViewById(R.id.checkBox_GPIO23);
for (int i = 0; i < mGPIOList.length; i++) {
if (i == 9 || i == 0 || i == 1 || i == 6 || i == 2) {
GetCheckBoxGpio(i).setText(Integer.toString(mGPIOList[i]));
GetCheckBoxGpio(i).setEnabled(true);
}
}
}
private final CompoundButton.OnCheckedChangeListener mIntCheckBoxCallback =
new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//do stuff
int gpioID = Integer.parseInt(buttonView.getHint().toString());
boolean IntValue = buttonView.isChecked();
if (gpioID < mGPIOList.length) {
/*Use IntValue Re-configure GPIO Interrupt Here*/
mGPIO.get(gpioID).ConfigureInterrupt(IntValue);
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manual_control);
try {
mGPIOList = gpio_list;
GetAllResources();
for (int i = 0; i < mGPIOList.length; i++) {
GPIOOpen(i);
}
ConfigureCallbacks();
GetAllResources();
} catch (Exception e) {
ErrorMessage.newInstance(e.getLocalizedMessage())
.show(getFragmentManager(), FRAGMENT_DIALOG);
}
}
private void ConfigureCallbacks() {
ConfigureGPIOCheckboxCallback();
}
private void ConfigureGPIOCheckboxCallback() {
for (int i = 0; i < mGPIOList.length; i++) {
if (i == 9 || i == 0 || i == 1 || i == 6 || i == 2) {
cbGPIO[i].setOnCheckedChangeListener(mGPIOCheckBoxCallback);
}
}
}
private void GPIOOpen(int GPIOnum) {
if (GPIOnum == 9 || GPIOnum == 0 || GPIOnum == 1 || GPIOnum == 6 || GPIOnum == 2) {
mGPIO.add(new gpioApplication());
try {
mGPIO.get(GPIOnum).GPIOOpen(GPIOnum, mGPIOList[GPIOnum], mGPIOCallback);
} catch (SecurityException e) {
ErrorMessage.newInstance(getString(R.string.error_gpio))
.show(getFragmentManager(), FRAGMENT_DIALOG);
} catch (IOException e) {
ErrorMessage.newInstance(getString(R.string.error_unknown))
.show(getFragmentManager(), FRAGMENT_DIALOG);
} catch (InvalidParameterException e) {
ErrorMessage.newInstance(getString(R.string.error_gpio_config))
.show(getFragmentManager(), FRAGMENT_DIALOG);
}
} else {
mGPIO.add(null);
}
}
}
In the above codes the GPIO pins must be controlled by the co2ppm1 sensor values (If the co2ppm1 sensor value is above 2000, it must turn ON GPIO 9 pin).
Instead of 5118710, it should be 511-8710. I'd like to add a dash after the user the user inputted 3 digits already in the EditText. The maximum length of the EditText is 7 digits only.
After I figured out the above problem, I've got stuck in coding again. When I already inputted 3 digits, it appends dash (that's what I'd like to happen) but my problem here is that the next 3 digits also appends dash (Like this: 511-871-)... Please help me with this. thanks!
txt_HomeNo.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
boolean flag = true;
String eachBlock[] = txt_HomeNo.getText().toString().split("-");
for (int i = 0; i < eachBlock.length; i++) {
if (eachBlock[i].length() > 3) {
flag = false;
}
}
if (flag) {
txt_HomeNo.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 (((txt_HomeNo.getText().length() + 1) % 4) == 0) {
if (txt_HomeNo.getText().toString().split("-").length <= 3) {
txt_HomeNo.setText(txt_HomeNo.getText() + "-");
txt_HomeNo.setSelection(txt_HomeNo.getText().length());
}
}
a = txt_HomeNo.getText().toString();
} else {
a = txt_HomeNo.getText().toString();
keyDel = 0;
}
} else {
txt_HomeNo.setText(a);
}
}
The most straightforward solution is to use PhoneNumberFormattingTextWatcher which will format the number according to the system locale.
XML:
<EditText
android:id="#+id/phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/enter_phone_number"
android:inputType="phone" />
Add addTextChangedListener() in your class:
EditText phoneNumber = (EditText)findViewById(R.id.phone_number);
phoneNumber.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
Implement the following modified addTextChangedListener for txt_HomeNo. The code below is checking if the length of the text entered is 3 and if it is then add the - to it. Not a very robust solution but it works!
txt_HomeNo.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
txt_HomeNo.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) {
int len = txt_HomeNo.getText().length();
if(len == 3) {
txt_HomeNo.setText(txt_HomeNo.getText() + "-");
txt_HomeNo.setSelection(txt_HomeNo.getText().length());
}
} else {
keyDel = 0;
}
}
#Override
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
});
I have a few small changes to the solution of neo108 so it can work with both soft keyboard and hard keyboard, in my code for example the edittext will follow the rule to automatically add " " at position 5 and 9.
txtPhone.addTextChangedListener(new TextWatcher() {
int keyDel;
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
txtPhone.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
if (keyCode == KeyEvent.KEYCODE_DEL) {
keyDel = 1;
}
return false;
}
});
String currentString = txtPhone.getText().toString();
int currentLength = txtPhone.getText().length();
if (currentLength == 5 || currentLength == 9) {
keyDel = 1;
}
if (keyDel == 0) {
if (currentLength == 4 || currentLength == 8) {
txtPhone.setText(txtPhone.getText() + " ");
txtPhone.setSelection(txtPhone.getText().length());
}
} else {
if (currentLength != 5 && currentLength != 9) {
keyDel = 0;
} else if ((currentLength == 5 || currentLength == 9)
&& !" ".equals(currentString.substring(currentLength - 1, currentLength))) {
txtPhone.setText(currentString.substring(0, currentLength - 1) + " "
+ currentString.substring(currentLength - 1, currentLength));
txtPhone.setSelection(txtPhone.getText().length());
}
}
}
I implemented a custom TextWatcher; this handles 10 and 11 digit phone numbers (i.e. 1-555-867-5309 and 555-867-5309). Allows adds, deletions, inserts, mass removal while maintaining proper cursor position.
public class CustomPhoneTextWatcher implements TextWatcher {
private final EditText editText;
private String previousString;
public CustomPhoneTextWatcher(EditText editText) {
this.editText = editText;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
// if the previous editable ends with a dash and new is shorter than previous
// additionally remove preceding character
if (previousString != null && previousString.endsWith("-") && editable.toString().length() < previousString.length()) {
previousString = editable.toString();
String removedCharacterPriorToDash = editable.toString().substring(0, editable.length() - 1);
editText.setText(removedCharacterPriorToDash);
int position = editText.length();
Editable etext = editText.getText();
Selection.setSelection(etext, position);
} else {
previousString = editable.toString();
String numericString = StringUtils.removeNonnumeric(editable.toString());
int stringLength = numericString.length();
boolean startsWithOne = numericString.startsWith("1");
numericString = numericString.substring(0, Math.min(stringLength, 10 + (startsWithOne ? 1 : 0)));
int lastHyphenIndex = 6 + (startsWithOne ? 1 : 0);
int secondToLastHyphenIndex = 3 + (startsWithOne ? 1 : 0);
if (stringLength >= lastHyphenIndex) {
numericString = numericString.substring(0, lastHyphenIndex) + "-" + numericString.substring(lastHyphenIndex, numericString.length());
}
if (stringLength >= secondToLastHyphenIndex) {
numericString = numericString.substring(0, secondToLastHyphenIndex) + "-" + numericString.substring(secondToLastHyphenIndex, numericString.length());
}
if (numericString.startsWith("1")) {
numericString = numericString.substring(0, 1) + "-" + numericString.substring(1, numericString.length());
}
if (!numericString.equals(editable.toString())) {
editText.setText(numericString);
int position = editText.length();
Editable etext = editText.getText();
Selection.setSelection(etext, position);
}
}
}
}
StringUtils.removeNonnumeric(editable.toString()) is a call to this method:
public static String removeNonnumeric(String text) {
return text.replaceAll("[^\\d]", "");
}
Thanks for the all above answer.
The editText.setOnKeyListener() will never invoke when your device has only soft keyboard.
If we strictly follow the rule to add "-", then this code not always show desire result.
editText.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
but above code is best solution for formatting phone no.
Apart from above this solution, I write a code which work on all types of condition::
phoneNumber.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 s) {
if (len > phoneNumber.getText().length() ){
len--;
return;
}
len = phoneNumber.getText().length();
if (len == 4 || len== 8) {
String number = phoneNumber.getText().toString();
String dash = number.charAt(number.length() - 1) == '-' ? "" : "-";
number = number.substring(0, (len - 1)) + dash + number.substring((len - 1), number.length());
phoneNumber.setText(number);
phoneNumber.setSelection(number.length());
}
}
});
this line of code required to add "-" on 3rd & 6th position of number.
if (len == 4 || len== 8)
Do it yourself by using OnEditTextChangedListener and insert dash by counting number of chars, Counting Chars in EditText Changed Listener
import android.text.Editable;
import android.text.Selection;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;
/**
* Auto-formats a number using -.
* Ex. 303-333-3333
* Ex. 1-303-333-3333
* Doesn't allow deletion of just -
*/
public class PhoneNumberFormattingTextWatcher implements TextWatcher {
private static final String TAG = "PhoneNumberTextWatcher";
private final EditText editText;
private String previousNumber;
/**
* Indicates the change was caused by ourselves.
*/
private boolean mSelfChange = false;
public PhoneNumberFormattingTextWatcher(EditText editText) {
this.editText = editText;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
// if the previous editable ends with a dash and new is shorter than previous
// additionally remove preceding character
Log.i(TAG, "Previous String: " + previousNumber);
//if self change ignore
if (mSelfChange) {
Log.i(TAG, "Ignoring self change");
mSelfChange = false;
return;
}
String phoneNumber = removeNonnumeric(editable.toString());
int stringLength = phoneNumber.length();
//empty case
if(stringLength == 0) {
mSelfChange = true;
editText.setText("");
return;
}
boolean startsWithOne = phoneNumber.charAt(0) == '1';
int maxLength = 10 + (startsWithOne ? 1 : 0);
//too large
if(stringLength > maxLength) {
Log.i(TAG, "String length is greater than max allowed, using previous string: " + previousNumber);
mSelfChange = true;
editText.setText(previousNumber);
Editable etext = editText.getText();
Selection.setSelection(etext, previousNumber.length());
return;
}
phoneNumber = formatPhoneNumber(phoneNumber);
if(previousNumber != null && phoneNumber.length() == previousNumber.length()) {
//user deleting last character, and it is a -
if(phoneNumber.endsWith("-")) {
phoneNumber = phoneNumber.substring(0, phoneNumber.length()-2);
}
}
mSelfChange = true;
previousNumber = phoneNumber;
editText.setText(phoneNumber);
Editable etext = editText.getText();
Selection.setSelection(etext, phoneNumber.length());
}
private String formatPhoneNumber(String phoneNumber) {
int stringLength = phoneNumber.length();
//check if starts with 1, if it does, dash index is increased by 1
boolean startsWithOne = phoneNumber.charAt(0) == '1';
//if the length of the string is 6, add another dash
int lastHyphenIndex = 6 + (startsWithOne ? 1 : 0);
if (stringLength >= lastHyphenIndex) {
phoneNumber = phoneNumber.substring(0, lastHyphenIndex) + "-" + phoneNumber.substring(lastHyphenIndex, phoneNumber.length());
}
//if the length of the string is 3, add a dash
int secondToLastHyphenIndex = 3 + (startsWithOne ? 1 : 0);
if (stringLength >= secondToLastHyphenIndex) {
phoneNumber = phoneNumber.substring(0, secondToLastHyphenIndex) + "-" + phoneNumber.substring(secondToLastHyphenIndex, phoneNumber.length());
}
//If the number starts with 1, add a dash after 1
if (phoneNumber.startsWith("1")) {
phoneNumber = phoneNumber.substring(0, 1) + "-" + phoneNumber.substring(1, phoneNumber.length());
}
return phoneNumber;
}
private static String removeNonnumeric(String text) {
return text.replaceAll("[^\\d]", "");
}
}
I have a DecimalFormat with the following pattern "#,###.###"
But when I want to type for example 50.05 it does not allow to, as I parse the string to number every time a text changes and when I call
df.parse("50.0");
it returns 50. and cuts the 0
Any ideas what can I do to be able to type numbers like 50.05 ?
The full code of my class is presented below. Sorry if it is too verbose:
private class NumberTextWatcher implements TextWatcher {
#SuppressWarnings("unused")
private static final String TAG = "NumberTextWatcher";
private DecimalFormat df;
private DecimalFormat dfnd;
private boolean hasFractionalPart;
private EditText et;
NumberTextWatcher(EditText et) {
df = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH);
df.applyLocalizedPattern(hasThousandSeparator ? "#,###.###" : "####.###");
df.setDecimalSeparatorAlwaysShown(true);
df.setMaximumFractionDigits(maxDecimal);
dfnd = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH);
dfnd.applyLocalizedPattern(hasThousandSeparator ? "#,###" : "####");
dfnd.setMaximumFractionDigits(maxDecimal);
this.et = et;
hasFractionalPart = false;
}
#Override
public void afterTextChanged(Editable s) {
et.removeTextChangedListener(this);
try {
int inilen, endlen;
inilen = et.getText().length();
String sign = hasSign ? s.toString().startsWith("-") ? "-" : "+" : null;
String sStr = hasSignAttached(s) ? s.toString().substring(1) : s.toString();
String v = sStr.replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
if(v.length() == 0){
return;
}
if(hasFractionalPart){
String[] vSplit = v.split("\\.");
String intStr = vSplit[0];
String decStr = vSplit.length > 1 ? vSplit[1] : "";
if(intStr.length() > maxNumbers)
intStr = intStr.substring(0, maxNumbers);
if(decStr.length() > maxDecimal)
decStr = decStr.substring(0, maxDecimal);
v = String.format("%s.%s", intStr, decStr);
} else {
if(v.length() > maxNumbers)
v = v.substring(0, maxNumbers);
}
Number n = df.parse(v);
int cp = et.getSelectionStart();
String formatted;
if (hasFractionalPart) {
formatted = df.format(n);
} else {
formatted = dfnd.format(n);
}
et.setText(hasSign ? String.format("%s%s", sign, formatted) : formatted);
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 (Exception e) {
ExceptionTracker.trackException(e);
} finally {
et.addTextChangedListener(this);
}
}
private boolean hasSignAttached(Editable s){
return s.toString().startsWith("+") || s.toString().startsWith("-");
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(hasSign && start == 1 && count == 1){
et.setText("");
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
hasFractionalPart = s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator()));
}
}
this code solves the problem:
import java.text.DecimalFormat;
public class DecimalFormatExample {
public static void main(String[] args) {
DecimalFormat dformat = new DecimalFormat("#,###.###");
dformat.setMinimumFractionDigits(1);
String someNumber = "50.05";
Double someNumberDouble = Double.valueOf(someNumber);
System.out.println(dformat.format(someNumberDouble)); // 50.05
someNumber = "50.0";
someNumberDouble = Double.valueOf(someNumber);
System.out.println(dformat.format(someNumberDouble)); // 50.0
}
}
Ok, I have done a small hack to fix this. May be it will help someone in the future, or someone will suggest a better solution.
So I added the following checks:
boolean decHasZeroAtEnd = false;
.......
if(decStr.endsWith("0")){
decHasZeroAtEnd = true;
}
.......
if(decHasZeroAtEnd){
decHasZeroAtEnd = false;
formatted = String.format("%s0", formatted);
}
And the whole code is the following:
private class NumberTextWatcher implements TextWatcher {
#SuppressWarnings("unused")
private static final String TAG = "NumberTextWatcher";
private DecimalFormat df;
private DecimalFormat dfnd;
private boolean hasFractionalPart;
private EditText et;
NumberTextWatcher(EditText et) {
df = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH);
df.applyLocalizedPattern(hasThousandSeparator ? "#,###.###" : "####.###");
df.setDecimalSeparatorAlwaysShown(true);
df.setMaximumFractionDigits(maxDecimal);
dfnd = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH);
dfnd.applyLocalizedPattern(hasThousandSeparator ? "#,###" : "####");
dfnd.setMaximumFractionDigits(maxDecimal);
this.et = et;
hasFractionalPart = false;
}
#Override
public void afterTextChanged(Editable s) {
et.removeTextChangedListener(this);
try {
int inilen, endlen;
inilen = et.getText().length();
String sign = hasSign ? s.toString().startsWith("-") ? "-" : "+" : null;
String sStr = hasSignAttached(s) ? s.toString().substring(1) : s.toString();
String v = sStr.replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
if(v.length() == 0){
return;
}
boolean decHasZeroAtEnd = false;
if(hasFractionalPart){
String[] vSplit = v.split("\\.");
String intStr = vSplit[0];
String decStr = vSplit.length > 1 ? vSplit[1] : "";
if(intStr.length() > maxNumbers)
intStr = intStr.substring(0, maxNumbers);
if(decStr.length() > maxDecimal)
decStr = decStr.substring(0, maxDecimal);
if(decStr.endsWith("0")){
decHasZeroAtEnd = true;
}
v = String.format("%s.%s", intStr, decStr);
} else {
if(v.length() > maxNumbers)
v = v.substring(0, maxNumbers);
}
Number n = df.parse(v);
int cp = et.getSelectionStart();
String formatted;
if (hasFractionalPart) {
formatted = df.format(n);
} else {
formatted = dfnd.format(n);
}
if(decHasZeroAtEnd){
decHasZeroAtEnd = false;
formatted = String.format("%s0", formatted);
}
Log.d("testt", "formatted " + formatted);
et.setText(hasSign ? String.format("%s%s", sign, formatted) : formatted);
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 (Exception e) {
ExceptionTracker.trackException(e);
} finally {
et.addTextChangedListener(this);
}
}
private boolean hasSignAttached(Editable s){
return s.toString().startsWith("+") || s.toString().startsWith("-");
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(hasSign && start == 1 && count == 1){
et.setText("");
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
hasFractionalPart = s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator()));
}
}
i'm newbie in Android and i want to do a mask for an EditText with a decimal number with 3 decimal places(ex: 0.658), i need a mask that user doesn't need write the ".", only the numbers, like a conventional mask for currency.
I'm trying create a TextWatcher based in this:
public static TextWatcher currency(final EditText editText) {
return new TextWatcher() {
String current = "";
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!s.toString().equals(current)) {
editText.removeTextChangedListener(this);
String cleanString = s.toString();
if(count != 0) {
String substr = cleanString.substring(cleanString.length() - 2);
if (substr.contains(".") || substr.contains(",")) {
cleanString += "0";
}
}
cleanString = cleanString.replaceAll("[R$,.]", "");
double parsed = Double.parseDouble(cleanString);
Locale locale = new Locale("pt", "BR");
String formatted = NumberFormat.getCurrencyInstance(locale).format((parsed / 100));
formatted = formatted.replaceAll("[R$]", "");
current = formatted;
editText.setText(formatted);
editText.setSelection(formatted.length());
editText.addTextChangedListener(this);
}
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void afterTextChanged(Editable s) {}
};
}
But without success.
There are a better way to do this?
Thanks.
Try to use MaskedEditText from github.
I had a similar problem. So I started looking for any implementation of TextWatcher. I finally decided to build my own:
public class BrRealMoneyTextWatcher implements TextWatcher {
private static final Locale DEFAULT_LOCALE = new Locale("pt", "BR");
private static DecimalFormat NUMBER_FORMAT = (DecimalFormat) DecimalFormat.getCurrencyInstance(DEFAULT_LOCALE);
private static final int FRACTION_DIGITS = 2;
private static final String DECIMAL_SEPARATOR;
private static final String CURRENCY_SIMBOL;
static {
NUMBER_FORMAT.setMaximumFractionDigits(FRACTION_DIGITS);
NUMBER_FORMAT.setMaximumFractionDigits(FRACTION_DIGITS);
NUMBER_FORMAT.setParseBigDecimal(true);
DECIMAL_SEPARATOR = String.valueOf(NUMBER_FORMAT.getDecimalFormatSymbols().getDecimalSeparator());
CURRENCY_SIMBOL = NUMBER_FORMAT.getCurrency().getSymbol(DEFAULT_LOCALE);
}
final EditText target;
public BrRealMoneyTextWatcher(EditText target) {
this.target = target;
}
private boolean updating = false;
#Override
public void onTextChanged(CharSequence s, int start, int before, int after) {
if (updating) {
updating = false;
return;
}
updating = true;
try {
String valueStr = formatNumber(fixDecimal(s.toString()));
BigDecimal parsedValue = ((BigDecimal) NUMBER_FORMAT.parse(valueStr));
String value = NUMBER_FORMAT.format(parsedValue);
target.setText(value);
target.setSelection(value.length());
} catch (ParseException | NumberFormatException ex) {
throw new IllegalArgumentException("Erro ao aplicar a máscara", ex);
}
}
private String formatNumber(String originalNumber) {
String number = originalNumber.replaceAll("[^\\d]", "");
switch(number.length()) {
case 0 :
number = "0" + DECIMAL_SEPARATOR + "00";
break;
case 1 :
number = "0" + DECIMAL_SEPARATOR + "0" + number;
break;
case 2 :
number = "0" + DECIMAL_SEPARATOR + number;
break;
default:
number = number.substring(0, number.length() - 2) + DECIMAL_SEPARATOR + number.substring(number.length() - 2);
break;
}
return CURRENCY_SIMBOL + number;
}
private String fixDecimal(String number) {
int dotPos = number.indexOf('.') + 1;
int length = number.length();
return (length - dotPos < FRACTION_DIGITS) ? fixDecimal(number + "0") : number;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
}
I hope it helps somebody else.