Removing style from selected text in edittext
Asked Answered
T

3

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.

Tidal answered 29/7, 2013 at 10:11 Comment(1)
sorry. updated the question with the error.Tidal
A
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)
Ancestress answered 29/7, 2013 at 10:11 Comment(9)
Spannable still has those methods.. But they don't do the trick when I use it with removeSpan.Tidal
Yes is true since both implements Spanned. I need more information in order to help because removeSpan should workAncestress
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.Tidal
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.Ancestress
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) thanksTidal
Have you tried the latest changes? Any comments would be appreciatedAncestress
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 ?Cacophonous
@Rahul Gupta - I update my answer to add support for StyleSpan and resolve some issues.Ancestress
I have used your code, but i have a problem. I have posted my problem here: https://mcmap.net/q/1923375/-remove-style-on-spansHangbird
D
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
Disrate answered 29/7, 2013 at 10:56 Comment(1)
android.graphics.Typeface.DEFAULT.getStyle() didn't do the trick. It revert the text to its original style.Tidal
N
0

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

Please try this code:

myEditText.setTypeface(Typeface.DEFAULT);
Nero answered 25/12, 2015 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.