I am using a textwatcher to check the validity of an user input. Some of my user had a crash caused by this textwacher. Here is the stacktrace given by google :
java.lang.IndexOutOfBoundsException: Invalid index 1, size is 1
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
at java.util.ArrayList.get(ArrayList.java:308)
at android.widget.TextView.sendAfterTextChanged(TextView.java:7997)
at android.widget.TextView$ChangeWatcher.afterTextChanged(TextView.java:10043)
at android.text.SpannableStringBuilder.sendAfterTextChanged(SpannableStringBuilder.java:970)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:497)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:435)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:30)
at android.view.inputmethod.BaseInputConnection.replaceText(BaseInputConnection.java:679)
at android.view.inputmethod.BaseInputConnection.setComposingText(BaseInputConnection.java:437)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:333)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:77)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5584)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
at dalvik.system.NativeStart.main(Native Method)
And my code :
Zipcode.addTextChangedListener(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) {
if (editable.toString().equals(zc)) return;
zc = editable.toString();
isZipcodeChecked = false;
Log.d(TAG, "Text changed : " + editable.toString());
if (editable.toString().length() != 5) {
Zipcode.setTextColor(Color.RED);
return;
}
checkZipcode(editable.toString());
}
});
I do not have much more info about how this happened. It works fine most of the time and I could not reproduce this bug. Any idea about what happened?
The answer has already been given in a comment, but hard to see if you're just skimming by like me. So the real answer is:
Avoid adding the TextWatcher twice or more times, as the first one will edit the content and the subsequent ones will then cause an exception.
Since there's no way to check if an EditText already has a TextWatcher set, I've built my own solution with a boolean, that checks if the TextWatcher has already been added or not.
private EditText mEditText;
private TextWatcher mTextWatcher;
private boolean mTextWatcherIsEnabled = false;
public void setTextWatcher(TextWatcher textWatcher) {
mTextWatcher = textWatcher;
}
public void enableTextWatcher() {
if (!mTextWatcherIsEnabled) {
mEditText.addTextChangedListener(mTextWatcher);
}
mTextWatcherIsEnabled = true;
}
public void disableTextWatcher() {
if (mTextWatcherIsEnabled) {
mEditText.removeTextChangedListener(mTextWatcher);
}
mTextWatcherIsEnabled = false;
}
For me this was happening because I had two different TextWatcher
s added to the same view, and the one I added first was removing itself:
view.addTextChangedListener(new ListenerA());
view.addTextChangedListener(new ListenerB());
class ListenerA extends TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int i, int i1, int i2) {
view.removeTextChangedListener(this);
// ...
}
}
Because the first one removes itself, it causes an index out of bounds exception here:
private void sendBeforeTextChanged(CharSequence text, int start, int before, int after) {
if (mListeners != null) {
final ArrayList<TextWatcher> list = mListeners;
final int count = list.size();
for (int i = 0; i < count; i++) {
list.get(i).beforeTextChanged(text, start, before, after);
}
}
// ...
}
count
is queried and the list has size of 2
. So it calls get(0)
which then removes itself. Then it calls get(1)
, but the list size is only 1
now, so this throws the exception.
I solved this by not actually removing my TextWatcher in the first place, and instead just disabling the behavior. You could alternatively solve this by guaranteeing that only the last watcher in the list ever removes itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With