Search code examples
androidandroid-edittextspannablestring

Override getText's EditText


I want to override getText() of EditText.

I receive this kind of String: "12,345,678"

My purpose is to just remove the commas and return the Editable but when with my code I get an error.

public class AmountEditText extends EditText {
    @Override
    public Editable getText() {
        Editable s = super.getText();
        if(s!=null && s.length()>0) {
            if (s.toString().contains(",")) {
                return new SpannableStringBuilder(s.toString().replace(",", ""));
            }
        }
        return s;
    }
    private TextWatcher watcher = 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) {
            int position = getSelectionStart();
            int nbCommaBefore;
            int nbCommaAfter;
            String str = s.toString();
            String finalStr;
            String formattedStr;
            nbCommaBefore  = str.length() - str.replace(",", "").length();
            boolean containsDot = false;
            if (str.contains(".")) {
                containsDot = true;
                formattedStr = str.split("\\.")[0];

            } else {
                formattedStr = str;
            }
            if (!s.toString().isEmpty()) {
                removeTextChangedListener(watcher);
                formattedStr = formattedStr.replace(",", "");
                formattedStr = formattedStr.replaceAll("(\\d)(?=(\\d{3})+$)", "$1,");
                if (containsDot) {
                    if (str.split("\\.").length != 1) {
                        finalStr = formattedStr + "." + str.split("\\.")[1].replace(",", "");
                    } else {
                        finalStr = formattedStr + ".";
                    }
                } else {
                    finalStr = formattedStr;
                }
                nbCommaAfter  = finalStr.length() - finalStr.replace(",", "").length();
                setText(finalStr);
                if (position == str.length()){
                    setSelection(finalStr.length());
                }
                else if (position == 0)
                {
                    setSelection(0);
                }
                else if (nbCommaBefore < nbCommaAfter){
                    setSelection(position + 1);
                }

                else if (nbCommaAfter < nbCommaBefore){
                    setSelection(position - 1);
                }
                else{
                    setSelection(position);
                }
                addTextChangedListener(watcher);
            }
        }

        @Override
        public void afterTextChanged(Editable s) {
        }

    };

    public AmountEditText(Context context) {
        this(context, null);
    }

    public AmountEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        addTextChangedListener(watcher);
    }

    public AmountEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        addTextChangedListener(watcher);
    }
}

E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback E/MessageQueue-JNI: java.lang.IndexOutOfBoundsException: setSpan (0 ... 5) ends beyond length 4 at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1265) at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:684) at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:677) at android.widget.SpellChecker$SpellParser.setRangeSpan(SpellChecker.java:532) at android.widget.SpellChecker$SpellParser.parse(SpellChecker.java:515) at android.widget.SpellChecker.spellCheck(SpellChecker.java:242) at android.widget.Editor.updateSpellCheckSpans(Editor.java:679) at android.widget.Editor.sendOnTextChanged(Editor.java:1249) at android.widget.TextView.sendOnTextChanged(TextView.java:8191) at android.widget.TextView.setText(TextView.java:4483) at android.widget.TextView.setText(TextView.java:4337) at android.widget.EditText.setText(EditText.java:89) at android.widget.TextView.setText(TextView.java:4312) at org.newtonproject.newpay.widgetlib.AmountEditText$1.onTextChanged(AmountEditText.java:74)

I would like to precise that the error doesn't come from my onTextChanged because everything works well without the getText() override

EDIT : The user can enter number, I will append some commas in order to format the number. But when I override getText() I want to delete these commas in that way I don't have to filter the return of getText() everytime


Solution

  • In your case, you can not override getText() and resize and using TextWatcher at same time.

    Check the android source code below and you will why

    SpannableStringBuilder.java

    public void setSpan(Object what, int start, int end, int flags) {
        setSpan(true, what, start, end, flags, true/*enforceParagraph*/);
    }
    
    private void setSpan(boolean send, Object what, int start, int end, int flags,
                boolean enforceParagraph) {
        checkRange("setSpan", start, end);
    }
    
    private void checkRange(final String operation, int start, int end) {
        ...
        int len = length();
        if (start > len || end > len) {
            throw new IndexOutOfBoundsException(operation + " " +
                        region(start, end) + " ends beyond length " + len); // here is you exception
        }
    }
    
    public int length() {
        return mText.length - mGapLength;
    }
    

    SpellChecker.java

    private void setRangeSpan(Editable editable, int start, int end) {
        ...
        editable.setSpan(mRange, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    
    public void parse(int start, int end) {
        ...
        if (parseEnd > start) {
            setRangeSpan((Editable) mTextView.getText(), start, parseEnd); // I think the error happened from here, they use your getText() function here and receive shorter string, but the start, parseEnd still stick with original string
            parse();
        }
    }
    

    Solution .
    You can simple defind a new function like getBeautifulText().