Remove underline from links in TextView - Android
Asked Answered
C

13

109

I am using two textview to display links from database, I managed to change link colors but I want to remove the underline

email.setText(c.getString(5));
    website.setText(c.getString(6));
    Linkify.addLinks(email, Linkify.ALL);
    Linkify.addLinks(website, Linkify.ALL);

Can I do that from XML or Code ?

Chandra answered 4/11, 2010 at 12:55 Comment(0)
T
244

You can do it in code by finding and replacing the URLSpan instances with versions that don't underline. After you call Linkify.addLinks(), call the function stripUnderlines() pasted below on each of your TextViews:

    private void stripUnderlines(TextView textView) {
        Spannable s = new SpannableString(textView.getText());
        URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
        for (URLSpan span: spans) {
            int start = s.getSpanStart(span);
            int end = s.getSpanEnd(span);
            s.removeSpan(span);
            span = new URLSpanNoUnderline(span.getURL());
            s.setSpan(span, start, end, 0);
        }
        textView.setText(s);
    }

This requires a customized version of URLSpan which doesn't enable the TextPaint's "underline" property:

    private class URLSpanNoUnderline extends URLSpan {
        public URLSpanNoUnderline(String url) {
            super(url);
        }
        @Override public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            ds.setUnderlineText(false);
        }
    }
Timmerman answered 16/12, 2010 at 17:18 Comment(17)
This solution assumes the text of the span is the same as the URL, which is the case for a basic http:// link. However, Linkify is smart and converts a phone number like (212) 555-1212 into the URL tel:2125551212. The new URLSpanNoUnderline call should be passed span.getURL() to retain this info, otherwise you generate bad links that cause exceptions when clicked. I've placed this proposed solution in the edit queue for your answer, since I don't have edit permissions myself.Gaddy
Cheers Mike. I approved your edit, first time I'd ever seen that edit dialog, hope it worked.Timmerman
What does "text of the span is the same as the URL" mean?Unjust
It means that a word that you read on the screen, which is a link, can be the same or different than the actual link: <a href="myweb.com">go there</a>, here the url is "myweb.com", while "go there" is the spanXylene
But even non telephone numbers are getting displayed as link for example 20.00 can be clickable ..Any workaround for this ?Hulbert
Not Working for me. It gives Class Cast Exception at Line Spannable s = (Spannable)textView.getText();Welker
Just replace that line with Spannable s = new SpannableString(textView.getText());Minicam
I've have tried this one, but after calling the function the links stop working. I'm on android 4.3, and i had to replace Spannable s = (Spannable)textView.getText(); with Spannable s = new SpannableString(textView.getText());Ideality
replacing with Spannable s = (Spannable) textView.getText() with Spannable s = new SpannableString(textView.getText()); doesn't work for me. If I switch back to casting then it does work, only if the TextView has android:textIsSelectable=true. Any idea why?Elagabalus
Note that this doesn't work when you use textView.setAutoLinkMask(..).Petromilli
It works after i removed android:autoLink="web" from TextView. Thanks.Retreat
@clu textView.getText() only returns a Spannable if the TextView is editable or if it setText() is called with BufferType.SPANNABLE.Renick
@Petromilli Same goes for removing android:autoLink="email"Deathday
Is there any way to make this work with the android:autoLink attribute? Otherwise it seems you have to create your own onclick methods for built in functionality.Douglass
This solution doesn't work with android:autoLink attribute. Without this attribute it doesn't has such a value.Neville
Kotlin: private fun stripUnderlines(textView: TextView) = SpannableString(textView.text).apply { for (span in getSpans(0, length, URLSpan::class.java)) { removeSpan(span) setSpan(URLSpanNoUnderline(span.url), getSpanStart(span), getSpanEnd(span), 0) } } private class URLSpanNoUnderline(url: String) : URLSpan(url) { override fun updateDrawState(ds: TextPaint) { super.updateDrawState(ds) ds.isUnderlineText = false } }Gagne
To make this work with the android;autoLink attribute, you have to call textView.setAutoLinkMask(0) before calling textView.setText(s), otherwise the autolinking process will run on the new text, which gets rid of our modified spans.Townswoman
V
40

Given a textView and content:

TextView textView = (TextView) findViewById(R.id.your_text_view_id);
String content = "your <a href='http://some.url'>html</a> content";

Here is a concise way to remove underlines from hyperlinks:

Spannable s = (Spannable) Html.fromHtml(content);
for (URLSpan u: s.getSpans(0, s.length(), URLSpan.class)) {
    s.setSpan(new UnderlineSpan() {
        public void updateDrawState(TextPaint tp) {
            tp.setUnderlineText(false);
        }
    }, s.getSpanStart(u), s.getSpanEnd(u), 0);
}
textView.setText(s);

This is based on the approach suggested by robUx4.

In order to make the links clickable you also need to call:

textView.setMovementMethod(LinkMovementMethod.getInstance());
Vienne answered 2/10, 2013 at 9:26 Comment(6)
this does not work if you try to get a string from resources, so String content = getResources().getString(R.string.content); which includes a link no longer works.Hus
How can that be? A String does not know where it originated from. There must be a difference in the String contents.Vienne
not sure why but it doesn't show the url when I call the string from res/values/strings.xml which contains exactly the same thing as the example. Can you test in your end?Hus
I'm definitely not going to test at my end (-:. If you want to be sure about the string contents, log both strings together with their hashCode. It would surprise me if you find different results when using the code above with the exact same strings.Vienne
If you want to make it work with getting string from resources, you need to encode it. > = &gt;, < = &lt; etc. For example: <string name="link_to_google" >&lt;a href="https://www.google.com/"&gt;Google&lt;/a&gt;</string> See developer.android.com/guide/topics/resources/string-resourceAvron
If you want to make it work with getting string from resources you can also embed the whole HTML string in <![CDATA[html_string_goes_here]]>, which looks much cleaner than encoding < and >Erving
C
26

Here is Kotlin extension function:

fun TextView.removeLinksUnderline() {
    val spannable = SpannableString(text)
    for (u in spannable.getSpans(0, spannable.length, URLSpan::class.java)) {
        spannable.setSpan(object : URLSpan(u.url) {
            override fun updateDrawState(ds: TextPaint) {
                super.updateDrawState(ds)
                ds.isUnderlineText = false
            }
        }, spannable.getSpanStart(u), spannable.getSpanEnd(u), 0)
    }
    text = spannable
}

Usage:

txtView.removeLinksUnderline()    
Corelation answered 7/4, 2020 at 14:46 Comment(3)
Use: ds.color = ContextCompat.getColor(this@MainActivity, R.color.myColor) To change link text colorVanhorn
@Fortran show us your code. which android version you have tested?Corelation
Here's a slightly more Kotlin-y way to do this: ~~~ spannable.getSpans<URLSpan>(0, spannable.length).forEach { span -> val startIdx = spannable.getSpanStart(span) val endIdx = spannable.getSpanEnd(span) spannable.removeSpan(span) spannable.setSpan( URLSpanNoUnderline(span.url), startIdx, endIdx, 0 ) } ~~~Demisec
B
11

UnderlineSpan already exists, but can only set the underline.

Another solution is to add a no underline span on each existing URLSpan. Thus the underline state is disabled just before painting. This way you keep your URLSpan (possibly custom) classes and all other styles set elsewhere.

public class NoUnderlineSpan extends UnderlineSpan {
    public NoUnderlineSpan() {}

    public NoUnderlineSpan(Parcel src) {}

    @Override
    public void updateDrawState(TextPaint ds) {
        ds.setUnderlineText(false);
    }
}

Here is how you set it without removing the existing URLSpan object:

URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
for (URLSpan span: spans) {
    int start = s.getSpanStart(span);
    int end = s.getSpanEnd(span);
    NoUnderlineSpan noUnderline = new NoUnderlineSpan();
    s.setSpan(noUnderline, start, end, 0);
}
Bisutun answered 11/6, 2013 at 11:36 Comment(0)
V
5

I've implemented a solution which, in my opinion, is more elegant. I've made a custom TextView. This way you don't need to execute extra code for every TextView with hyperlinks.

package com.example.view;

import android.content.Context;
import android.support.v7.widget.AppCompatTextView;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.URLSpan;
import android.util.AttributeSet;

import com.example.utils.UrlSpanNoUnderline;

public class TextViewNoUnderline extends AppCompatTextView {
    public TextViewNoUnderline(Context context) {
        this(context, null);
    }

    public TextViewNoUnderline(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.textViewStyle);
    }

    public TextViewNoUnderline(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setSpannableFactory(Factory.getInstance());
    }

    private static class Factory extends Spannable.Factory {
        private final static Factory sInstance = new Factory();

        public static Factory getInstance() {
            return sInstance;
        }

        @Override
        public Spannable newSpannable(CharSequence source) {
            return new SpannableNoUnderline(source);
        }
    }

    private static class SpannableNoUnderline extends SpannableString {
        public SpannableNoUnderline(CharSequence source) {
            super(source);
        }

        @Override
        public void setSpan(Object what, int start, int end, int flags) {
            if (what instanceof URLSpan) {
                what = new UrlSpanNoUnderline((URLSpan) what);
            }
            super.setSpan(what, start, end, flags);
        }
    }
}

And code for UrlSpanNoUnderline:

package com.jankstudios.smmagazine.utils;

import android.text.TextPaint;
import android.text.style.URLSpan;

public class UrlSpanNoUnderline extends URLSpan {
    public UrlSpanNoUnderline(URLSpan src) {
        super(src.getURL());
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        super.updateDrawState(ds);
        ds.setUnderlineText(false);
    }
}
Venezuela answered 6/11, 2015 at 10:32 Comment(0)
O
3

If you are using Textview autolink property and you want to remove underlines you can use it:

First, extend UnderlineSpan and remove underline:

public class NoUnderlineSpan extends UnderlineSpan {

    @Override
    public void updateDrawState(TextPaint ds) {
        ds.setUnderlineText(false);
    }
}

Second, create and instance of NoUnderlineSpan, create a Spannable from the String text and set the span to the spannable:

NoUnderlineSpan mNoUnderlineSpan = new NoUnderline();
if (yourTextView.getText() instanceof Spannable) {
    Spannable s = (Spannable) yourTextView.getText();
    s.setSpan(mNoUnderlineSpan, 0, s.length(), Spanned.SPAN_MARK_MARK);
}

Reference: http://prog3.com/sbdm/blog/maosidiaoxian/article/details/39156563

Oceanid answered 17/5, 2017 at 17:7 Comment(2)
Nice solution, but after return from Telephone app (after click on phone number) it again underlines TextView.Neville
Yes, if your activity goes to the background you should use this code in your onResume activity function to maintain the textview no underline state.Oceanid
G
2

Whenever, try to remove URL underline with spannable am i suggest only these things :

1> Creating Custom class :

private class URLSpanNoUnderline extends URLSpan {
            public URLSpanNoUnderline(String url) {
                super(url);
            }
            @Override public void updateDrawState(TextPaint ds) {
                super.updateDrawState(ds);
                ds.setUnderlineText(false);
            }
        }

This requires a customized version of URLSpan which doesn't enable the TextPaint's "underline" property

2> setSpan with spannable text :

spannableText.setSpan(new URLSpanNoUnderline(UrlText), 0, UrlText.length() , 0);

Here, spannableText is Object of SpannableString...!!!

Garygarza answered 13/1, 2015 at 9:5 Comment(2)
This is the best solution and the only that worked for meAdit
This solution is for a text with one URL.Neville
S
2

Here's a Kotlin extension function as a solution to remove all UrlSpan underlines:

private fun Spannable.removeAllUrlSpanUnderline() {
    for (urlSpan in getSpans(0, length, URLSpan::class.java)) {
        setSpan(object : UnderlineSpan() {
            override fun updateDrawState(tp: TextPaint) {
                tp.isUnderlineText = false
            }
        }, getSpanStart(urlSpan), getSpanEnd(urlSpan), 0)
    }
}

In my case, I began with a string with href tags. fromHtml returns a Spanned so cast it to a Spannable so it's mutable. See the sample use below:

val sampleHtmlString = "<a href=\"www.google.com\">first Link</a> and <a href=\"www.google.com\">second link</a>"

val sampleSpannable = HtmlCompat.fromHtml(caslString, HtmlCompat.FROM_HTML_MODE_LEGACY) as Spannable

val sampleSpannableWithoutUnderlines = sampleSpannable.removeAllUrlSpanUnderline()

Now you're free to set sampleSpannableWithoutUnderlines to your TextView

Speak answered 11/8, 2021 at 5:31 Comment(0)
P
0
public void setView()
{
TextView t=(TextView) findViewById(R.id.textView3);
        t.setText(Html.fromHtml("<a href=http://www.urdusms.net > UrduSMS "));
        t.setTextColor(Color.BLACK);
        t.setGravity(Gravity.CENTER);
        t.setMovementMethod(LinkMovementMethod.getInstance());

         Spannable s = (Spannable) t.getText();
            URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
            for (URLSpan span: spans) {
                int start = s.getSpanStart(span);
                int end = s.getSpanEnd(span);
                s.removeSpan(span);
                span = new URLSpanline_none(span.getURL());
                s.setSpan(span, start, end, 0);
            }
        t.setText(s);
 }
//inner class is    
private class URLSpanline_none extends URLSpan {
            public URLSpanline_none(String url) {
                super(url);
            }
            @Override public void updateDrawState(TextPaint ds) {
                super.updateDrawState(ds);
                ds.setUnderlineText(false);
            }
        }
Pentarchy answered 5/6, 2015 at 7:52 Comment(0)
S
0

If you just want the text and don't bother about the URL link

This will STRIP the link but KEEP the text

private Spannable stripLinks(String content) {
    Spannable s = new SpannableString(content);
    URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
    for (URLSpan span : spans) {
        s.removeSpan(span);
    }

    return s;
}

no additional classes required

String content = "<a href='http://stackoverflow.com'>Stack Overflow</a> Rocks!";
textView.setText(stripLinks(content));
Stripling answered 4/11, 2016 at 7:58 Comment(0)
M
0

Here is My method

 public static void removeUnderlines(Spannable p_Text) {
            if (p_Text != null && p_Text.toString().length() > 0) {
                URLSpan[] spans = p_Text.getSpans(0, p_Text.length(), URLSpan.class);

                for (URLSpan span : spans) {
                    int start = p_Text.getSpanStart(span);
                    int end = p_Text.getSpanEnd(span);
                    p_Text.removeSpan(span);
                    span = new URLSpanNoUnderline(span.getURL());
                    p_Text.setSpan(span, start, end, 0);
                }
            }
        }

Call It like this

AppController.removeUnderlines((Spannable) eventEmail.getText());

Appcontroller is my application class where i put this method so that i can access it from anywhere

Mazuma answered 20/7, 2017 at 7:53 Comment(0)
S
0

For Xamarin users finding this post, here is how I got it to work:

  1. Create a custom span class to handle stripping out the underlines.
    class URLSpanNoUnderline : URLSpan {
            public URLSpanNoUnderline (string url) : base (url) {
            }

            public override void UpdateDrawState (TextPaint ds) {
                base.UpdateDrawState (ds);
                ds.UnderlineText = false;
            }
        }
  1. Create an extension method to find all url spans and replace them with our custom spans.
public static void StripUnderlinesFromLinks (this TextView textView) {
                var spannable = new SpannableStringBuilder (textView.TextFormatted);
                var spans = spannable.GetSpans (0, spannable.Length (), Java.Lang.Class.FromType (typeof (URLSpan)));
                foreach (URLSpan span in spans) {
                    var start = spannable.GetSpanStart (span);
                    var end = spannable.GetSpanEnd (span);
                    spannable.RemoveSpan(span);
                    var newSpan = new URLSpanNoUnderline (span.URL);
                    spannable.SetSpan(newSpan, start, end, 0);
                }
                textView.TextFormatted = spannable;
            }
Stevenson answered 9/1, 2019 at 19:32 Comment(0)
B
0
abstract class NoUnderlineClickableSpan : ClickableSpan() {        
    override fun updateDrawState(ds: TextPaint) {
        super.updateDrawState(ds)
        ds.isUnderlineText = false
    }    
}
Bulge answered 5/5, 2023 at 9:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.