My code:
fractionNumEt.addTextChangedListener(new TextWatcher() {
boolean ignoreChange = false;
#Override
public void afterTextChanged(Editable s) {
if(!ignoreChange) {
String string = String.valueOf(s);
if (string.length() > 2) {
string = string.substring(0, string.length() - 1);
ignoreChange = true;
fractionNumEt.setText(string);
ignoreChange = false;
}
}
}
....
I am trying to limit the characters to length 2, but want to keep listening. When I type '1','2', it displays "12" which is fine. Now when I type '3', it writes "31" instead of "23".
What on earth is going on!
I also tried:
string = string.substring(1, string.length());
in this case it works for the first time only n then nothing is changed.
This is happening because after setText(), EditText's cursor returns to first position. Then, any new number will be added at the begging of the String.
Try to update your code as follows:
#Override
public void afterTextChanged(Editable s) {
if(!ignoreChange) {
String string = String.valueOf(s);
if (string.length() > 2) {
string = string.substring(0, string.length() - 1);
ignoreChange = true;
fractionNumEt.setText(string);
fractionNumEt.setSelection(fractionNumEt.getText().length());
ignoreChange = false;
}
}
}
Just a suggestion
Check Editable Docs HERE
I think you don't need to convert it to String. afterTextChanged(Editable s) receives a Editable as argument. You can change it directly. Don't need to covert it to a String.
Moreover: You don't even need to make EditText.setText() because any change in the editable, will be passed automatically to EditText (since afterTextChanged is called by android and give you a chance to update the text few moments before to effectively display that text in the EditText).
Maybe, something like that:
#Override
public void afterTextChanged(Editable s) {
if(!ignoreChange) {
if(s.length() > 2) {
s.delete(s.length() - 1,s.length());
}
}
}
Play a little bit with Editable and you will see that it is easier.
Related
I am going to save the value in one textview after the length of input string is fulfilled. but saved value is empty if the value of textview is used. If Editable s value in afterTextChanged is used, it causes crash.
Some codes as following:
number = (EditText) findViewById(R.id.number);
final String numberStr = number.getText().toString();
if following afterTextChanged is used, empty value is saved even I already input sth.
#Override
public void afterTextChanged(Editable s) {
if (s.length() == 11) {
Toast.makeText(MainActivity.this, numberStr , Toast.LENGTH_SHORT).show();
saveSettingNote(MainActivity.this, "number_save", "number", numberStr);
number.setText(getSettingNote(MainActivity.this,"number_save", "number"));
}
}
if following code is used, it will cause crash:
#Override
public void afterTextChanged(Editable s) {
if (s.length() == 11) {
Toast.makeText(MainActivity.this, s.toString(), Toast.LENGTH_SHORT).show();
saveSettingNote(MainActivity.this, "number_save", "number", s.toString());
number.setText(getSettingNote(MainActivity.this,"number_save", "number"));
}
}
Saving and getting are based on SharedPreferences, which works well in other situation.
Actually, what I want to implement is saving String after the criteria is fulfilled for input string.
Please help to identify what is wrong in above code or suggest a new to get that function. Thanks a lot in advance.
Your app is crashing because your listener is looping indefinitely with the same value passed in over and over.
The ugly truth is that you've to unbind your listener, set the value and then bind it again:
#Override
public void afterTextChanged(Editable s) {
if (s.length() == 11) {
// unbind your listener
editText.addTextChangedListener(null);
// do your stuff
Toast.makeText(MainActivity.this, s.toString(), Toast.LENGTH_SHORT).show();
saveSettingNote(MainActivity.this, "number_save", "number", s.toString());
number.setText(getSettingNote(MainActivity.this,"number_save", "number"));
// bind your listener again
editText.addTextChangedListener(MainActivity.this);
}
}
Using RxBinding you can achieve this in a more elegant way:
RxTextView.textChanges(editText)
.map { it.toString() }
.filter({ it.length == 11 })
.distinctUntilChanged() // <-- the important part
.subscribe(
{ s -> /* do your stuff here */}
)
you set text in editext afterTextChanged(Editable s) which calls text change listener again and again and cause the application to crash. call number.setText(getSettingNote(MainActivity.this,"number_save", "number")); outside the text change listener.
My guess it that by declaring numberStr as you did, you expect to change its value each time the edit text change its content. However, this is not the case; it will be initialised with an empty string "" (unless you don't have any other string in the edit text at that moment) and then it will NEVER change its value, thus resulting in the empty value that you save. As solution I would suggest to make numberStr non final and update its value (like you already did) each time before showing the Toast.
I have an EditText in the following form:
<EditText
android:id="#+id/editTextNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_column="1"
android:layout_margin="10dp"
android:ems="10"
android:inputType="numberDecimal" />
Later I make BigDecimal-Calculations with the input, and realized that its possible to enter nothing or even a simple dot in that EditText. So I wrote following:
EditText editTextNumber = (EditText) rootView.findViewById(R.id.editTextNumber);
String number = String.valueOf(editTextNumber.getText());
if (number.equals("") || number.equals(".")) {
number = "1";
editTextNumber.setText(number);
}
Since I have overseen these two possible exceptions beforee I wonder if I have overseen another bug possibility in this code, and if there is a "cleaner" way.
I use this function. This does not account for international keypads that may use ',', rather than '.'.
public static boolean isNumeric(#Nullable String s)
{
if (TextUtils.isEmpty(s)) return false;
return s.matches("[-+]?\\d*\\.?\\d+");
}
If you don't handle any floats, TextUtils.isDigitsOnly() (javadoc) might be helpful.
while processing data from editText or any other String which is populated from any other source is critical as you may never know what exactly is being feed to the String
you can write following code or customize it according to your requirement
private boolean isValidString(String text){
if (text == null) {
return false;
}
if (text.trim().contentEquals("")) {
return false;
}
if (text.trim().length() < 1) {
return false;
}
if (text.trim().toLowerCase().contentEquals("null")) {
return false;
}
// you won't require following check for normal String , but as you would be dealing
// with numbers you may want to check that otherwise, Some surprises are
// waiting for you, while doing some math with the numbers
if (!text.matches("[0-9]+(\\.[0-9][0-9]?)?")) {
System.out.println("invalid: "+ text);
return false;
}else{
System.out.println("Valid: "+ text);
}
//
return true;
}
now assume this is the edittext you have
EditText editTextNumber = (EditText) rootView.findViewById(R.id.editTextNumber);
if(isValidString(editTextNumber.getText().toString)){
your String validation is done so you can perform your relevant action here
}
I have a TextWatcher that adds a dollar sign in front of an EditText value (it's a price field). Everything works fine except that if you type two first digits fast enough, the second digit won't appear. Once you past the first two digits it's all fine. If you type them slowly (almost a second in between) it also works fine.
Here is the TextWatcher code I am using:
#AfterTextChange(R.id.add_itemPrice) // android annotations way
void addDollar(Editable e) {
if (priceFieldBeingModified) {
return;
}
if (!e.toString().startsWith(CURRENCY_SYMB)) {
priceFieldBeingModified = true;
String newValue = CURRENCY_SYMB + e;
priceField.setText(newValue);
if (priceField.getSelectionStart() == 0) {
// move the cursor to the end
priceField.setSelection(priceField.getText().length());
}
priceFieldBeingModified = false;
}
}
priceField EditText has fixed layout_width/layout_height (in dp). From what I can gather, setText()/getText() are just too expensive but I don't know how to avoid them in this case.
priceField.getText().insert(0, CURRENCY_SYMB) doesn't do anything for some reason.
EDIT:
Looks like the problem only happens on Android 4.3 (or an Xperia Z phone). Tried a 4.1 phone - works like a charm.
Any suggestions would be appreciated, I am out of ideas with this one!
This is working fine for me:
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
if(s.toString().equalsIgnoreCase(""))
{
}
else if (!s.toString().startsWith("$")) {
priceFieldBeingModified = true;
String newValue = "$" + s;
e.setText(newValue);
if (e.getSelectionStart() == 0) {
// move the cursor to the end
e.setSelection(e.getText().length());
}
priceFieldBeingModified = false;
}
It checks if string is not null and it doesn't start with "$", then it adds "$" at start and moves cursor to end.
This is my text watcher to control user's birth year enter. If user enters less than 1900 I want to replace it with 1900. App works properly. I can enter a value to TextView. But when start typing starts with "1" then immediately replacing to "1900" after I can't edit TextView again. Delete button on keypad does not delete entered value.
#Override
public void afterTextChanged(Editable s) {
try {
int birth = Integer.parseInt(s.toString());
if (birth < 1900) {
s.replace(0, s.length(), "1900");
}
} catch (NumberFormatException e) {
}
}
<EditText
android:id="#+id/editText1"
android:hint="Birth Year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberSigned">
</EditText>
Alright my Friend i've been thinking about it for a while here is the closest solution i could get. try this
#Override
public void afterTextChanged(Editable s) {
try {
int birth = Integer.parseInt(s.toString());
if (birth < 1900 && s.length()>=4) {
s.replace(0, s.length(), "1900");
}
} catch (NumberFormatException e) {
}
}
This is only a small modification on your if statement.
replacing to "1900" after I can't edit TextView again. Delete button on keypad does not delete entered value.
This is expected behavior, It doesn't mean that delete button on keyboard does not delete the reentered value, Since after you replace the 1 to 1900 and then try to delete 0 (the last digit) will be deleted and immediately afterTextChanged will be called with input 190, so your condition again satisfies to <1900 and the text is replace with 1900 again (this continues when you try to delete) .
I would suggest to add a check to see the minimum length of the input , I believe you expect the user to enter at least 4 digit.
public void afterTextChanged(Editable s) {
if(s.toString().length < 4) {
return;
}
// do your other logic here
}
I am trying to learn java while building an android app. I have a points calculator without a button it uses a textchange listener to calculate the total. When backspace key is pressed and the box has null it crashes. I tried validating using the code below (only validated on field to begin with). But it does not work. Any help would be greatly appreciated.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wwcalc);
etFat = (EditText)findViewById(R.id.editTextFat);
etFiber = (EditText)findViewById(R.id.editTextFiber);
etProtein = (EditText)findViewById(R.id.editTextProtein);
etCarbs = (EditText)findViewById(R.id.editTextCarbs);
tvTotal = (TextView)findViewById(R.id.textViewPoints);
TextWatcher watcher = new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
if(isEmpty(etFat) == false){
intFat = Integer.parseInt(etFat.getText().toString());
}
else{
etFat.setText("0");
etFat.hasFocus();
return;
}
intProtein = Integer.parseInt(etProtein.getText().toString());
intFiber = Integer.parseInt(etFiber.getText().toString());
intCarbs = Integer.parseInt(etCarbs.getText().toString());
calculate();
}
#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
}
};
etFat.addTextChangedListener(watcher);
etProtein.addTextChangedListener(watcher);
etFiber.addTextChangedListener(watcher);
etCarbs.addTextChangedListener(watcher);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_wwcalc, menu);
return true;
}
public void calculate(){
//intTot = intFat + intCarbs + intFiber + intProtein;
intTot = (int) Math.ceil((intFat * (4/35)) + (intCarbs * (4/36.84)) - (intFiber* (4/50))+ (intProtein * (4/43.75)) ) ;
tvTotal.setText(Integer.toString(intTot));
}
private boolean isEmpty(EditText etText)
{
if(etText.getText().toString().trim().length() > 0 || etText.getText().toString().trim() != null)
return false;
else
return true;
}
}
Thanks for the help all. I got it working not sure if it is the best solution if anyone thinks there is a better way let me know. The try catch as suggested by conor catches the exception, then just insert a 0 set focus and select the 0
try {
intFat = Integer.parseInt(etFat.getText().toString());
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
etFat.setText("0");
etFat.hasFocus();
etFat.selectAll();
}
Without seeing the stacktrace it is hard to know but i'm going to make a stab it anyway.
The following piece of your isEmpty() function is allowing false to be returned when the box could still be emtpy.
etText.getText().toString().trim() != null
This means that when you press backspace and clear the field it is empty but your function says it's not. Then the kicker, when your app thinks the field is not empty it tried to parse the contents for an integer value (which is not present). This attempt at parsing throws an exception and crashes your app.
I expect the stacktrace to show the app crashing at this line
intFat = Integer.parseInt(etFat.getText().toString());
It's worth noting that you should always surround calls like Integer.parseInt() with a try, catch block.
Hope that helps.
The exception should be caused by Integer.parseInt,
intFat = Integer.parseInt(etFat.getText().toString());
ntProtein = Integer.parseInt(etProtein.getText().toString());
intFiber = Integer.parseInt(etFiber.getText().toString());
intCarbs = Integer.parseInt(etCarbs.getText().toString());
If you pass string with spaces to parseInt, NumberFormatException will be thrown, you need to trim the text, and then set them, such as,
intFat = Integer.parseInt(etFat.getText().toString().trim());
try this
When backspace is pressed and a field is changed from say "0" to--> "" the operation that is supposed to convert the value to a double fails and crashed my application. "holder.mChoiceRNK"is the " EditView" reference " . If we look at what happens onAfterTextChanged we can set in a default value and then do a select all. The default value of "0" will not cause an error.
Cheers
And happy programming.
holder.mChoiceRNK.doAfterTextChanged {
Log.d(mTAG70, "130xxx 131 Pre Error")
if (holder.mChoiceRNK.text.toString() == "") {
holder.mChoiceRNK.setText("0") // new value if the backspace is pressed to create ""
holder.mChoiceRNK.selectAll()
} else{
Log.d(mTAG70, "130xxx 133 Past Error--> ${holder.mChoiceRNK.text}")
myRNKvotingArray[position].mRNK = holder.mChoiceRNK.text.toString().toDouble()
}}
Alternate solution:
holder.mChoiceRNK.doAfterTextChanged {
try {
myRNKvotingArray[position].mRNK =holder.mChoiceRNK.text.toString().toDouble()
} catch (err:Exception) {
holder.mChoiceRNK.setText("0")
holder.mChoiceRNK.selectAll()
}}