0

For a long time i been confused by the setSpan as it looks so easy but not always working as expected

In my last attempt i am trying to setSpan to recognized patterns. When user starts typing "5 kilo toma" the "5 kilo" will be highlighted.

I used the next code to do that as part of the TextWatcher listener

@Override
    public void afterTextChanged(Editable s) {

        String quantity = mAutoCompleteAdapter.getQuantity();

        if (!quantity.isEmpty()) {
            int index = mAutoCompleteAdapter.getQuantityIndex();
            final StyleSpan styleSpan = new StyleSpan( Typeface.BOLD_ITALIC );
            s.setSpan( styleSpan, index, index + quantity.length(), Spanned.SPAN_MARK_MARK );
        }
    }

This is working correctly (basic testing)

enter image description here

But if i start deleting the text to read "5 kil", the setSpan does not refresh to normal when pattern no longer detected

enter image description here

So i was thinking, perhaps the setSpan is kept in memory or something and i need to set entire string span every time. So i tried the next code:

@Override
    public void afterTextChanged(Editable s) {

        if (s.length() > 0) {

            String quantity = mAutoCompleteAdapter.getQuantity();
            int index = 0;

            if (!quantity.isEmpty()) {
                index = mAutoCompleteAdapter.getQuantityIndex();
                StyleSpan styleSpan = new StyleSpan( Typeface.BOLD_ITALIC );
                s.setSpan( styleSpan, index, index + quantity.length(), Spanned.SPAN_MARK_MARK );
            }

            StyleSpan styleSpanNormal = new StyleSpan( Typeface.NORMAL );
            s.setSpan( styleSpanNormal, index + quantity.length(), s.length(), Spanned.SPAN_MARK_MARK );
        }
    }

But still not working correctly

epic
  • 1,333
  • 1
  • 13
  • 27

2 Answers2

2

I think i understand it more now.

Please correct me if i am wrong and don't hurry to downgrade.

I was confused to think that the EditText.getText() was returning a String and even when i see that the afterTextChanged(Editable s) using the Editable, i still thought it was a similar object.

Only now that i noticed i could use getText(), then setSpan without using the setText, that i realized i am actually getting the actual object and thus manipulate it directly.

The meaning of this is that with every getText() or using TextWatcher listener, the old setSpan still exists and i need to clear it.

At first i thought clearSpans() will do the trick but it seems to also clear everything else. So instead, i first set the default span and only then set the correct one

For example:

//Clear old span
s.setSpan( new BackgroundColorSpan( Color.TRANSPARENT),
                0,
                s.length(),
                Spanned.SPAN_MARK_MARK );

//set new span
s.setSpan( new BackgroundColorSpan( Color.RED),
                    indexStart,
                    indexStart + mQuantity.length(),
                    Spanned.SPAN_MARK_MARK );

In addition, i noticed that the Autocomplete adapter and the TextWatcher order was not always the same. When using log i noticed that mostly the TextWatcher firing before the autocomplete but not always. Either that or perhaps that one can fire while the other?! i am not sure.

Anyway, since the order is significant as the adapter examines the text, i used MutableLiveData<String> to keep track of the mQuantity value and modify span accordigly

//In AutoComplete adapter
public MutableLiveData<String> getQuantity() {
    if (mQuantity == null) {
        mQuantity = new MutableLiveData<>();
    }
    return mQuantity;
}

//In handler constructor
mAutoCompleteAdapter.getQuantity().observe( (LifecycleOwner) activity, new 
Observer<String>() {
        @Override
        public void onChanged(String s) {
            mQuantity = s;
            refreshSpans();
        }
    } );

private void refreshSpans() {
    Editable s = mTextView.getText();
    clearEditTextSpan(s);

    if (!mQuantity.isEmpty()) {
        //Set span
        int indexStart = mAutoCompleteAdapter.getQuantityIndex();

        s.setSpan( new BackgroundColorSpan( Color.RED),
                indexStart,
                indexStart + mQuantity.length(),
                Spanned.SPAN_MARK_MARK );
    } 
}

private void clearEditTextSpan(Editable s) {
    s.setSpan( new BackgroundColorSpan( Color.TRANSPARENT),
            0,
            s.length(),
            Spanned.SPAN_MARK_MARK );
}

Hope it helps anyone.

epic
  • 1,333
  • 1
  • 13
  • 27
-1

i think you should try the textChangedListener as its method onTextChanged will help you to check the text while user is typing new text,deleting some text in textview

textview.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) {
               //place your code here
               //the code here will work when user is changing the text

            }
Prathamesh
  • 1,064
  • 1
  • 6
  • 16