Remove underline from links in TextView - Android

70,725

Solution 1

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);
        }
    }

Solution 2

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());

Solution 3

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()    

Solution 4

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);
}

Solution 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);
    }
}
Share:
70,725

Related videos on Youtube

Mohamed Ben Dhaou
Author by

Mohamed Ben Dhaou

Updated on August 11, 2021

Comments

  • Mohamed Ben Dhaou
    Mohamed Ben Dhaou almost 3 years

    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 ?

  • Mike Mueller
    Mike Mueller over 13 years
    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.
  • Reuben Scratton
    Reuben Scratton over 13 years
    Cheers Mike. I approved your edit, first time I'd ever seen that edit dialog, hope it worked.
  • Shane Oliver
    Shane Oliver about 13 years
    What does "text of the span is the same as the URL" mean?
  • Lumis
    Lumis about 13 years
    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 span
  • Nemo
    Nemo about 11 years
    But even non telephone numbers are getting displayed as link for example 20.00 can be clickable ..Any workaround for this ?
  • Brijesh Thakur
    Brijesh Thakur almost 11 years
    Not Working for me. It gives Class Cast Exception at Line Spannable s = (Spannable)textView.getText();
  • FOliveira
    FOliveira almost 10 years
    Just replace that line with Spannable s = new SpannableString(textView.getText());
  • GuilhE
    GuilhE almost 9 years
    This is the best solution and the only that worked for me
  • shoke
    shoke almost 9 years
    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?
  • Rain Man
    Rain Man almost 9 years
    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.
  • Adriaan Koster
    Adriaan Koster almost 9 years
    How can that be? A String does not know where it originated from. There must be a difference in the String contents.
  • Rain Man
    Rain Man almost 9 years
    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?
  • Adriaan Koster
    Adriaan Koster almost 9 years
    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.
  • Kishore
    Kishore over 7 years
    It works after i removed android:autoLink="web" from TextView. Thanks.
  • Neil Miller
    Neil Miller almost 7 years
    @clu textView.getText() only returns a Spannable if the TextView is editable or if it setText() is called with BufferType.SPANNABLE.
  • finstas
    finstas over 6 years
    @Krishnaraj Same goes for removing android:autoLink="email"
  • Alkarin
    Alkarin about 6 years
    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.
  • CoolMind
    CoolMind about 6 years
    Nice solution, but after return from Telephone app (after click on phone number) it again underlines TextView.
  • CoolMind
    CoolMind about 6 years
    This solution is for a text with one URL.
  • Francisco Moya
    Francisco Moya about 6 years
    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.
  • Micer
    Micer almost 6 years
    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-resource
  • Makks129
    Makks129 about 4 years
    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 >