在 TextView-Android 中删除链接中的下划线

我使用两个 textview来显示从数据库的链接,我设法改变链接颜色,但我想删除下划线

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

我可以从 XML 或代码做到这一点吗?

74694 次浏览

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

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

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

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...!!!

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

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

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

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

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

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

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

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