Android 带html标签文本添加自定义超链接跳转
背景:
在项目开发过程中,需要在用户协议等文本中加入超链接跳转到APP内的其他界面,正常情况下我们都会知道用Html.fromHtml
来识别html标签中的超链接,比如<a href="http://www.baidu.com/">百度一下</a>
,点击时会自动打开系统浏览器去跳转,但是如果我们不想跳转浏览器打开网页,而是需要跳转到APP内的某个界面,则需要额外处理
第一步:在html文本中加入html超链接标签,并做个标识
<a href="privacy">查看用户隐私协议</a>
<a href="important">查看重要声明</a>
这里我们增加了两个超链接,href
的属性值主要用于标记要跳转到哪个APP界面,后面代码处理时会需要根据这个做不同处理
第二步:自定义一个可点击的文本块ClickableSpan
这里主要做了一个简单的封装,关键点在于重写ClickableSpan
的onCLick
方法,它的updateDrawState
方法中则可以修改超链接的一些属性,比如字体颜色,下划线等
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.view.View;import androidx.annotation.ColorRes;
import androidx.annotation.NonNull;public class ClickableSpanBuilder {private View.OnClickListener mListener;//点击事件private String mUrl;//要打开的网址private int mLinkColorRes;//超链接颜色Context mContext;public ClickableSpanBuilder(Context context) {this.mContext = context.getApplicationContext();mLinkColorRes = context.getColor(R.color.common_link_color);//超链接默认字体颜色}public ClickableSpanBuilder setClickListener(View.OnClickListener listener) {this.mListener = listener;return this;}public ClickableSpanBuilder setUrl(String url) {this.mUrl = url;return this;}public ClickableSpanBuilder setLinkColor(@ColorRes int colorRes) {this.mLinkColorRes = mContext.getColor(colorRes);;return this;}public ClickableSpan build() {return new CustomClickableSpan(mContext, mListener, mUrl, mLinkColorRes);}private static class CustomClickableSpan extends ClickableSpan {private View.OnClickListener mListener;//点击事件private String mUrl;//要打开的网址private Context mContext;private int mLinkColorRes;//超链接颜色public CustomClickableSpan(Context context, View.OnClickListener mListener, String mUrl, int linkColorRes) {this.mContext = context;this.mListener = mListener;this.mUrl = mUrl;this.mLinkColorRes = linkColorRes;}@Overridepublic void onClick(@NonNull View widget) {if (mListener != null) {//处理点击事件mListener.onClick(null);} else if (!TextUtils.isEmpty(mUrl)) {//如果没有设置,则看是否有设置urlif (mContext != null) {//有的话则使用系统浏览器跳转mContext.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl)));}}}@Overridepublic void updateDrawState(@NonNull TextPaint ds) {super.updateDrawState(ds);ds.setUnderlineText(true);//设置需要下划线ds.setColor(mLinkColorRes);//设置超链接字体颜色}}
}
第三步:封装一个工具类,实现处理带html标签的纯文本
工具类中封装了两个方法:
handleContentLink
处理带html标签的文本中所有超链接Spanned sourceContent
这个就是通过Html.fromHtml
解析html后返回的文本内容,其实解析之后html中的<a>
会被解析成URLSpan
对象进行封装- 通过
Spanned
的getSpans
方法可以获取到所有的URLSpan
对象,getSpanStart(URLSpan)
和getSpanEnd(URLSpan)
可以获取指定Span
标签的起始位置和结束位置,也就是<a>xxxxx</a>
包括住的这部分内容前后位置 - 通过
URLSpan
对象的getURL()
方法就可以获取到我们在html中<a>
标签的href
属性值,用于区分该超链接要跳转到哪里 - 还需要创建一个新的
Spanned
对象来保存我们修改后的文本内容,这里我们使用继承于Spanned
的SpannableStringBuilder
,因为它有个clearSpans()
方法可以将之前文本中所有旧的的URLSpan
对象清除;如果不清楚旧的,直接添加新的会导致无法点击跳转 - 通过上一步骤封装的
ClickableSpanBuilder
类创建可以自定义跳转逻辑的ClickableSpan
对象 - 通过
SpannableStringBuilder.setSpan
方法给指定范围的文本设置超链接点击事件
getClickableLink
返回一个可以点击的超链接文本- 用于普通String文本后面需要拼接一个超链接文本Spanned的场景
- String文本和Spanned超链接文本不能直接拼接到一起
- 需要调用
TextView
的append(Spanned)
方法将后面超链接文本拼接进去
import android.content.Context;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.util.Log;
import android.view.View;public class LinkUtils {private static final String TAG = "LinkUtils";/*** 处理带html标签的文本中所有超链接* @param context* @param sourceContent* @return*/public static Spanned handleContentLink(Context context, Spanned sourceContent) {SpannableStringBuilder tmp = new SpannableStringBuilder(sourceContent);tmp.clearSpans();//这里必须要清除旧的SpanURLSpan[] urls = sourceContent.getSpans(0, sourceContent.length(), URLSpan.class);//获取所有的超链接标签<a>for (URLSpan urlSpan : urls) {Log.i(TAG, "url:" + urlSpan.getURL());if ("privacy".equals(urlSpan.getURL())) {ClickableSpan clickableSpan = new ClickableSpanBuilder(context).setLinkColor(R.color.common_content_text_color).setClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//点击跳转用户隐私协议}}).build();tmp.setSpan(clickableSpan, sourceContent.getSpanStart(urlSpan), sourceContent.getSpanEnd(urlSpan), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);} else if ("important".equals(urlSpan.getURL())) {ClickableSpan clickableSpan = new ClickableSpanBuilder(context).setLinkColor(R.color.common_content_text_color).setClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//点击跳转重要声明}}).build();tmp.setSpan(clickableSpan, sourceContent.getSpanStart(urlSpan), sourceContent.getSpanEnd(urlSpan), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}}return tmp;}/*** 返回一个可以点击的超链接文本* @param context* @param linkText 超链接文本内容* @param listener 点击事件* @return*/public static Spanned getClickableLink(Context context, String linkText, View.OnClickListener listener) {SpannableString linkSpan = new SpannableString(linkText);ClickableSpan clickableSpan = new ClickableSpanBuilder(context).setClickListener(listener).build();linkSpan.setSpan(clickableSpan, 0, linkSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);return linkSpan;}
}
第四步:项目中的使用
- 批量处理html文本中超链接用法:
Spanned htmlContent = Html.fromHtml("<a href=\"privacy\">查看用户隐私协议</a><a href=\"important\">查看重要声明</a>");contentTextView.setText(LinkUtils.handleContentLink(this, htmlContent));
- 纯文本拼接超链接文本用法:
Spanned linkSpan = LinkUtils.getClickableLink(this, "隐私和条款", new View.OnClickListener() {@Overridepublic void onClick(View v) {//自定义点击跳转}});
String content = "点击查看 ";
textView.setText(content);
textView.append((linkSpan));