5

My app should allow users to style inputted or selected text in an Edittext. Some of these styles are Underline, Strike through, Bold and Italic. They are easily added but I don't know how they could be removed and how I could determine if that style has already been added to the selected text. Code for adding style:

Spannable str = myEditText.getText();
    if (!SpannableUtils.isEmpty(str)) {
        str.setSpan(new StrikethroughSpan(), startSelection, endSelection,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

I tried using removeSpan but it didn't work. Also tried using

        str.setSpan(new StyleSpan(android.graphics.Typeface.DEFAULT),
                startSelection, endSelection,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

but it poses the error : The constructor StyleSpan(Typeface) is undefined.

meborda
  • 391
  • 3
  • 9

3 Answers3

10

I think this class solve your problem.

public class StyleSpanRemover {

    public void RemoveOne(Spannable spannable,
                          int startSelection, int endSelection, Class<?> style){

        ArrayList<SpanParts> spansParts = getSpanParts(spannable, startSelection, endSelection);
        removeOneSpan(spannable, startSelection, endSelection, style);
        restoreSpans(spannable, spansParts);
    }

    public void RemoveStyle(Spannable spannable,
              int startSelection, int endSelection, int styleToRemove){

        ArrayList<SpanParts> spansParts = getSpanParts(spannable, startSelection, endSelection);
        removeStyleSpan(spannable, startSelection, endSelection, styleToRemove);
        restoreSpans(spannable, spansParts);
    }

    public void RemoveAll(Spannable spannable, int startSelection, int endSelection){

        ArrayList<SpanParts> spansParts = getSpanParts(spannable, startSelection, endSelection);
        removeAllSpans(spannable, startSelection, endSelection);
        restoreSpans(spannable, spansParts);
    }

    protected void restoreSpans(Spannable spannable, ArrayList<SpanParts> spansParts){

        for (SpanParts spanParts : spansParts) {
            if(spanParts.part1.canAplly())
                spannable.setSpan(spanParts.part1.span, spanParts.part1.start,
                                  spanParts.part1.end,spanParts.span_flag);
            if(spanParts.part2.canAplly())
                spannable.setSpan(spanParts.part2.span, spanParts.part2.start,
                                  spanParts.part2.end, spanParts.span_flag);
        }
    }

    protected void removeAllSpans(Spannable spannable,int startSelection, int endSelection) {

        Object spansToRemove[] = spannable.getSpans(startSelection, endSelection, Object.class);
        for(Object span: spansToRemove){
            if(span instanceof CharacterStyle)
                spannable.removeSpan(span);
        }
    }

    protected void removeOneSpan(Spannable spannable,int startSelection, int endSelection,
                           Class<?> style) {

        CharacterStyle spansToRemove[] = spannable.getSpans(startSelection, endSelection, CharacterStyle.class);
        for(CharacterStyle span: spansToRemove){
            if(span.getUnderlying().getClass() == style )
                spannable.removeSpan(span);
        }
    }

    protected void removeStyleSpan(Spannable spannable, int startSelection,
        int endSelection, int styleToRemove) {

        MetricAffectingSpan spans[] = spannable.getSpans(startSelection, endSelection, MetricAffectingSpan.class);
        for(MetricAffectingSpan span: spans){
            int stylesApplied = 0;  
            int stylesToApply;
            int spanStart;
            int spanEnd;
            int spanFlag;
            Object spanUnd = span.getUnderlying();
            if(spanUnd instanceof StyleSpan){
                spanFlag = spannable.getSpanFlags(spanUnd);
                stylesApplied = ((StyleSpan) spanUnd).getStyle();
                stylesToApply = stylesApplied & ~styleToRemove;

                spanStart = spannable.getSpanStart(span);
                spanEnd = spannable.getSpanEnd(span);
                if(spanEnd >= 0 && spanStart >= 0){
                    spannable.removeSpan(span);
                    spannable.setSpan(new StyleSpan(stylesToApply), spanStart, spanEnd,spanFlag);
                }
            }
        }
    }

    protected ArrayList<SpanParts> getSpanParts(Spannable spannable,
                                              int startSelection,int endSelection){

        ArrayList<SpanParts> spansParts = new ArrayList<SpanParts>();
        Object spans[] = spannable.getSpans(startSelection, endSelection, Object.class);
        for(Object span: spans){
            if(span instanceof CharacterStyle){
                SpanParts spanParts = new SpanParts();
                int spanStart = spannable.getSpanStart(span);
                int spanEnd = spannable.getSpanEnd(span);
                if(spanStart == startSelection && spanEnd == endSelection) continue;
                spanParts.span_flag = spannable.getSpanFlags(span);
                spanParts.part1.span = CharacterStyle.wrap((CharacterStyle) span);
                spanParts.part1.start = spanStart;
                spanParts.part1.end = startSelection;
                spanParts.part2.span = CharacterStyle.wrap((CharacterStyle) span);
                spanParts.part2.start = endSelection;
                spanParts.part2.end = spanEnd;
                spansParts.add(spanParts);
            }
        }
        return spansParts;
    }

    private class SpanParts{
        int span_flag;
        Part part1;
        Part part2;
        SpanParts() {
            part1 = new Part();
            part2 = new Part();
        }
    }
    private class Part{
        CharacterStyle span;
        int start;
        int end;
        boolean canAplly() {
            return start < end;
        }
    }
}

How to use:

int startSelection=editText.getSelectionStart();
int endSelection=editText.getSelectionEnd();
Spannable spannable = editText.getText();

StyleSpanRemover spanRemover = new StyleSpanRemover();
// to remove all styles
spanRemover.RemoveAll(spannable,startSelection,endSelection);
//to remove only StrikethroughSpan style use:
spanRemover.RemoveOne(spannable,startSelection,endSelection,StrikethroughSpan.class);
//to remove one StyleSpan use:
spanRemover.RemoveStyle(spannable,startSelection,endSelection,Typeface.BOLD)
ramaral
  • 6,149
  • 4
  • 34
  • 57
  • Spannable still has those methods.. But they don't do the trick when I use it with removeSpan. – meborda Jul 29 '13 at 11:06
  • Yes is true since both implements Spanned. I need more information in order to help because `removeSpan` should work – ramaral Jul 29 '13 at 13:58
  • remove span does work, but now the way i want it to. My goal is to remove the style pan only from the selected text. not the entire text in the edittext. – meborda Jul 30 '13 at 01:18
  • The problem is when, for example, a style is applied to four characters and you select two inner to remove the style, the initial style must be splitted in two. To handle that I will update my answer. – ramaral Jul 30 '13 at 18:52
  • It works only at the first time a substring is selected.. :| But modified it and got it to work for another problem (removing only of a specific style eg. bold then italic are added then either should be removed) thanks – meborda Jul 31 '13 at 02:37
  • Have you tried the latest changes? Any comments would be appreciated – ramaral Aug 01 '13 at 18:29
  • If my string is both bold and italics and i just want that bold style should be removed, what should i do in that case ? – Rahul Gupta Nov 06 '13 at 12:12
  • @Rahul Gupta - I update my answer to add support for StyleSpan and resolve some issues. – ramaral Nov 07 '13 at 21:03
  • I have used your code, but i have a problem. I have posted my problem here: http://stackoverflow.com/questions/36307409/remove-style-on-spans – Mostafa Lavaei Mar 30 '16 at 11:28
3

android.graphics.TYPEFACE.Default returns a Typeface, and the StyleSpan constructor takes an int as paramater.

Try using :

android.graphics.Typeface.DEFAULT.getStyle() 

instead of:

android.graphics.Typeface.DEFAULT
njzk2
  • 38,969
  • 7
  • 69
  • 107
Vedang Jadhav
  • 510
  • 2
  • 11
  • android.graphics.Typeface.DEFAULT.getStyle() didn't do the trick. It revert the text to its original style. – meborda Jul 29 '13 at 11:02
0

I think this way below is the simplest but useful. It works for me.

Please try this code:

myEditText.setTypeface(Typeface.DEFAULT);
Thang Pham
  • 1,006
  • 2
  • 9
  • 18