androidspannablestringspannablespanned

Remove style on spans


I have used this StyleSpanRemover to remove style from selected text, but i have a problem. I want to have a bold button that toggle bold style of selected text. it means if the selected text (or part of that) is not bold, bold all of them; and if all of selected text is bold, remove bold style from it. But when i use this code, after one removing bold style on a text and again bolding it, getStyle() always returns Typeface.NORMAL

Here is the code i have used:

boolean isAllSpansBold = true;

        /**
         * Bold selected text
         */
        Spannable spannable = new SpannableString(mEditTextContent.getText());

        StyleSpan[] styleSpans = mEditTextContent.getText().getSpans(
                mEditTextContent.getSelectionStart(),
                mEditTextContent.getSelectionEnd(),
                StyleSpan.class
        );


        if(styleSpans.length == 0)
        {
            spannable.setSpan(
                    new StyleSpan(Typeface.BOLD),
                    mEditTextContent.getSelectionStart(),
                    mEditTextContent.getSelectionEnd(),
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            );

            Log.d("Ramansoft", "styleSpans.length = 0");
        }
        else
        {
            for(int i=0; i<styleSpans.length; i++) {
                Log.d("Ramansoft", "The style is: " + String.valueOf(styleSpans[i].getStyle()));

                if (styleSpans[i].getStyle() != Typeface.BOLD && styleSpans[i].getStyle() != Typeface.BOLD_ITALIC) {
                    isAllSpansBold = false;
                    break;
                }
            }

            if(isAllSpansBold)
            {
                StyleSpanRemover styleSpanRemover = new StyleSpanRemover();
                styleSpanRemover.RemoveStyle(
                        spannable,
                        mEditTextContent.getSelectionStart(),
                        mEditTextContent.getSelectionEnd(),
                        Typeface.BOLD
                );

                Log.d("Ramansoft", "isAllSpansBold = true");
            }
            else
            {
                spannable.setSpan(
                        new StyleSpan(Typeface.BOLD),
                        mEditTextContent.getSelectionStart(),
                        mEditTextContent.getSelectionEnd(),
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );

                Log.d("Ramansoft", "isAllSpansBold = false");
            }

Solution

  • After many tries, i can do that. Here is the solution.

    public class SpanStyleHelper
    {
    protected EditText mEditText;
    protected Spannable mSpannable;
    protected int mSelectedTextStart;
    protected int mSelectedTextEnd;
    
    public SpanStyleHelper(EditText editText)
    {
        mEditText = editText;
        mSpannable = mEditText.getText();
        mSelectedTextStart = mEditText.getSelectionStart();
        mSelectedTextEnd = mEditText.getSelectionEnd();
    }
    
    public Spannable boldSelectedText()
    {
        Log.d("Ramansoft", "Try to bold selected text..");
    
        StyleSpan[] styleSpans = mEditText.getText().getSpans(
                mSelectedTextStart,
                mSelectedTextEnd,
                StyleSpan.class
        );
    
        if(styleSpans.length > 0) {
    
            int lastSpanEnd = 0;
    
            for (StyleSpan styleSpan : styleSpans) {
                /**
                 * Save old style
                 */
                int oldStyle = styleSpan.getStyle();
    
    
                /**
                 * Get start and end of span
                 */
                int spanStart = mSpannable.getSpanStart(styleSpan);
                int spanEnd = mSpannable.getSpanEnd(styleSpan);
    
    
                /**
                 * Before bold this span, we check if any unspanned
                 * text between this span and last span remains. if any
                 * unspanned text exist, we should bold it
                 */
                if(spanStart > lastSpanEnd)
                {
                    mSpannable.setSpan(
                            new StyleSpan(Typeface.BOLD),
                            lastSpanEnd,
                            spanStart,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
    
                /**
                 * Update last span end
                 */
                lastSpanEnd = spanEnd;
    
    
                /**
                 * Remove the span
                 */
                mSpannable.removeSpan(styleSpan);
    
    
                /**
                 * Because we just need change selected text,
                 * if span start is lower than selected text start or
                 * if span end is higher than selected text end start
                 * we should restore span for unselected part of span
                 */
                if (spanStart < mEditText.getSelectionStart()) {
                    mSpannable.setSpan(
                            new StyleSpan(oldStyle),
                            spanStart,
                            mSelectedTextStart,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
    
                if (spanEnd > mEditText.getSelectionEnd()) {
                    mSpannable.setSpan(
                            new StyleSpan(oldStyle),
                            mSelectedTextEnd,
                            spanEnd,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
    
                /**
                 * We want to add bold style to current style
                 * so we most detect current style and change
                 * the style depend on current style
                 */
                if (oldStyle == Typeface.ITALIC) {
                    mSpannable.setSpan(
                            new StyleSpan(Typeface.BOLD_ITALIC),
                            spanStart,
                            spanEnd,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                } else {
                    mSpannable.setSpan(
                            new StyleSpan(Typeface.BOLD),
                            spanStart,
                            spanEnd,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
            }
    
            /**
             * Now we should check if any
             * unspanned selected text remains
             */
            if(mSelectedTextEnd != lastSpanEnd)
            {
                mSpannable.setSpan(
                        new StyleSpan(Typeface.BOLD),
                        lastSpanEnd,
                        mSelectedTextEnd,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
        }
        else
        {
            mSpannable.setSpan(
                    new StyleSpan(Typeface.BOLD),
                    mSelectedTextStart,
                    mSelectedTextEnd,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
    
        return mSpannable;
    }
    
    public Spannable unBoldSelectedText()
    {
        Log.d("Ramansoft", "Try to unbold selected text..");
    
        StyleSpan[] styleSpans = mEditText.getText().getSpans(
                mSelectedTextStart,
                mSelectedTextEnd,
                StyleSpan.class
        );
    
        for(StyleSpan styleSpan:styleSpans)
        {
            /**
             * Save old style
             */
            int oldStyle = styleSpan.getStyle();
    
    
            /**
             * Get start and end of span
             */
            int spanStart = mSpannable.getSpanStart(styleSpan);
            int spanEnd = mSpannable.getSpanEnd(styleSpan);
    
    
            /**
             * Remove the span
             */
            mSpannable.removeSpan(styleSpan);
    
    
            /**
             * Because we just need change selected text,
             * if span start is lower than selected text start or
             * if span end is higher than selected text end start
             * we should restore span for unselected part of span
             */
            if(spanStart < mEditText.getSelectionStart())
            {
                mSpannable.setSpan(
                        new StyleSpan(oldStyle),
                        spanStart,
                        mSelectedTextStart,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
    
            if(spanEnd > mEditText.getSelectionEnd())
            {
                mSpannable.setSpan(
                        new StyleSpan(oldStyle),
                        mSelectedTextEnd,
                        spanEnd,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
    
            /**
             * Because we just want to remove bold style,
             * if the span has another style, we should restore it
             */
            if(oldStyle == Typeface.BOLD_ITALIC)
            {
                mSpannable.setSpan(
                        new StyleSpan(Typeface.ITALIC),
                        spanStart,
                        spanEnd,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
        }
    
        return mSpannable;
    }
    
    public Spannable toggleBoldSelectedText()
    {
        Log.d("Ramansoft", "Try to toggle bold selected text..");
    
        boolean isAllSpansBold = true;
    
    
        StyleSpan[] styleSpans = mEditText.getText().getSpans(
                mSelectedTextStart,
                mSelectedTextEnd,
                StyleSpan.class
        );
    
    
        if(styleSpans.length == 0)
        {
            return boldSelectedText();
        }
        else
        {
            for(StyleSpan styleSpan:styleSpans)
            {
                Log.d("Ramansoft", "styleSpan.getStyle() = " + styleSpan.getStyle());
    
                if (styleSpan.getStyle() != Typeface.BOLD && styleSpan.getStyle() != Typeface.BOLD_ITALIC) {
                    isAllSpansBold = false;
                    break;
                }
            }
    
            Log.d("Ramansoft", "isAllSpansBold = " + isAllSpansBold);
    
    
            if(isAllSpansBold)
                return unBoldSelectedText();
            else
                return boldSelectedText();
        }
    
    }
    
    public Spannable italicSelectedText()
    {
        Log.d("Ramansoft", "Try to italic selected text..");
    
        StyleSpan[] styleSpans = mEditText.getText().getSpans(
                mSelectedTextStart,
                mSelectedTextEnd,
                StyleSpan.class
        );
    
        if(styleSpans.length > 0)
        {
            int lastSpanEnd = 0;
    
            for (StyleSpan styleSpan : styleSpans) {
                /**
                 * Save old style
                 */
                int oldStyle = styleSpan.getStyle();
    
    
                /**
                 * Get start and end of span
                 */
                int spanStart = mSpannable.getSpanStart(styleSpan);
                int spanEnd = mSpannable.getSpanEnd(styleSpan);
    
    
                /**
                 * Before italic this span, we check if any unspanned
                 * text between this span and last span remains. if any
                 * unspanned text exist, we should italic it
                 */
                if(spanStart > lastSpanEnd)
                {
                    mSpannable.setSpan(
                            new StyleSpan(Typeface.ITALIC),
                            lastSpanEnd,
                            spanStart,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
    
    
                /**
                 * Update last span end
                 */
                lastSpanEnd = spanEnd;
    
    
                /**
                 * Remove the span
                 */
                mSpannable.removeSpan(styleSpan);
    
    
                /**
                 * Because we just need change selected text,
                 * if span start is lower than selected text start or
                 * if span end is higher than selected text end start
                 * we should restore span for unselected part of span
                 */
                if (spanStart < mEditText.getSelectionStart()) {
                    mSpannable.setSpan(
                            new StyleSpan(oldStyle),
                            spanStart,
                            mSelectedTextStart,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
    
                if (spanEnd > mEditText.getSelectionEnd()) {
                    mSpannable.setSpan(
                            new StyleSpan(oldStyle),
                            mSelectedTextEnd,
                            spanEnd,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
    
                /**
                 * We want to add bold style to current style
                 * so we most detect current style and change
                 * the style depend on current style
                 */
                if (oldStyle == Typeface.BOLD) {
                    mSpannable.setSpan(
                            new StyleSpan(Typeface.BOLD_ITALIC),
                            spanStart,
                            spanEnd,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                } else {
                    mSpannable.setSpan(
                            new StyleSpan(Typeface.ITALIC),
                            spanStart,
                            spanEnd,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
            }
    
            /**
             * Now we should check if any
             * unspanned selected text remains
             */
            if(mSelectedTextEnd != lastSpanEnd)
            {
                mSpannable.setSpan(
                        new StyleSpan(Typeface.ITALIC),
                        lastSpanEnd,
                        mSelectedTextEnd,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
        }
        else
        {
            mSpannable.setSpan(
                    new StyleSpan(Typeface.ITALIC),
                    mSelectedTextStart,
                    mSelectedTextEnd,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            );
        }
    
        return mSpannable;
    }
    
    public Spannable unItalicSelectedText()
    {
        Log.d("Ramansoft", "Try to un-italic selected text..");
    
        StyleSpan[] styleSpans = mEditText.getText().getSpans(
                mSelectedTextStart,
                mSelectedTextEnd,
                StyleSpan.class
        );
    
        for(StyleSpan styleSpan:styleSpans)
        {
            /**
             * Save old style
             */
            int oldStyle = styleSpan.getStyle();
    
    
            /**
             * Get start and end of span
             */
            int spanStart = mSpannable.getSpanStart(styleSpan);
            int spanEnd = mSpannable.getSpanEnd(styleSpan);
    
    
            /**
             * Remove the span
             */
            mSpannable.removeSpan(styleSpan);
    
    
            /**
             * Because we just need change selected text,
             * if span start is lower than selected text start or
             * if span end is higher than selected text end start
             * we should restore span for unselected part of span
             */
            if(spanStart < mEditText.getSelectionStart())
            {
                mSpannable.setSpan(
                        new StyleSpan(oldStyle),
                        spanStart,
                        mSelectedTextStart,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
    
            if(spanEnd > mEditText.getSelectionEnd())
            {
                mSpannable.setSpan(
                        new StyleSpan(oldStyle),
                        mSelectedTextEnd,
                        spanEnd,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
    
            /**
             * Because we just want to remove bold style,
             * if the span has another style, we should restore it
             */
            if(oldStyle == Typeface.BOLD_ITALIC)
            {
                mSpannable.setSpan(
                        new StyleSpan(Typeface.BOLD),
                        spanStart,
                        spanEnd,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
        }
    
        return mSpannable;
    }
    
    public Spannable toggleItalicSelectedText()
    {
        Log.d("Ramansoft", "Try to toggle italic selected text..");
    
        boolean isAllSpansItalic = true;
    
    
        StyleSpan[] styleSpans = mEditText.getText().getSpans(
                mSelectedTextStart,
                mSelectedTextEnd,
                StyleSpan.class
        );
    
    
        if(styleSpans.length == 0)
        {
            return italicSelectedText();
        }
        else
        {
            for(StyleSpan styleSpan:styleSpans)
            {
                Log.d("Ramansoft", "styleSpan.getStyle() = " + styleSpan.getStyle());
    
                if (styleSpan.getStyle() != Typeface.ITALIC && styleSpan.getStyle() != Typeface.BOLD_ITALIC) {
                    isAllSpansItalic = false;
                    break;
                }
            }
    
            Log.d("Ramansoft", "isAllSpansItalic = " + isAllSpansItalic);
    
    
            if(isAllSpansItalic)
                return unItalicSelectedText();
            else
                return italicSelectedText();
        }
    }
    }