当前位置:   article > 正文

Android 带html标签文本添加自定义超链接跳转

Android 带html标签文本添加自定义超链接跳转

Android 带html标签文本添加自定义超链接跳转

背景:
在项目开发过程中,需要在用户协议等文本中加入超链接跳转到APP内的其他界面,正常情况下我们都会知道用Html.fromHtml来识别html标签中的超链接,比如<a href="http://www.baidu.com/">百度一下</a>,点击时会自动打开系统浏览器去跳转,但是如果我们不想跳转浏览器打开网页,而是需要跳转到APP内的某个界面,则需要额外处理

第一步:在html文本中加入html超链接标签,并做个标识

<a href="privacy">查看用户隐私协议</a>
<a href="important">查看重要声明</a>
  • 1
  • 2

这里我们增加了两个超链接,href的属性值主要用于标记要跳转到哪个APP界面,后面代码处理时会需要根据这个做不同处理

第二步:自定义一个可点击的文本块ClickableSpan

这里主要做了一个简单的封装,关键点在于重写ClickableSpanonCLick方法,它的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;
        }

        @Override
        public void onClick(@NonNull View widget) {
            if (mListener != null) {//处理点击事件
                mListener.onClick(null);
            } else if (!TextUtils.isEmpty(mUrl)) {//如果没有设置,则看是否有设置url
                if (mContext != null) {//有的话则使用系统浏览器跳转
                    mContext.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl)));
                }
            }
        }

        @Override
        public void updateDrawState(@NonNull TextPaint ds) {
            super.updateDrawState(ds);
            ds.setUnderlineText(true);//设置需要下划线
            ds.setColor(mLinkColorRes);//设置超链接字体颜色
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

第三步:封装一个工具类,实现处理带html标签的纯文本

工具类中封装了两个方法:

  • handleContentLink 处理带html标签的文本中所有超链接
    • Spanned sourceContent 这个就是通过Html.fromHtml解析html后返回的文本内容,其实解析之后html中的<a>会被解析成URLSpan对象进行封装
    • 通过SpannedgetSpans方法可以获取到所有的URLSpan对象,getSpanStart(URLSpan)getSpanEnd(URLSpan)可以获取指定Span标签的起始位置和结束位置,也就是<a>xxxxx</a>包括住的这部分内容前后位置
    • 通过URLSpan对象的getURL()方法就可以获取到我们在html中<a>标签的href属性值,用于区分该超链接要跳转到哪里
    • 还需要创建一个新的Spanned对象来保存我们修改后的文本内容,这里我们使用继承于SpannedSpannableStringBuilder,因为它有个clearSpans()方法可以将之前文本中所有旧的的URLSpan对象清除;如果不清楚旧的,直接添加新的会导致无法点击跳转
    • 通过上一步骤封装的ClickableSpanBuilder类创建可以自定义跳转逻辑的ClickableSpan对象
    • 通过SpannableStringBuilder.setSpan方法给指定范围的文本设置超链接点击事件
  • getClickableLink 返回一个可以点击的超链接文本
    • 用于普通String文本后面需要拼接一个超链接文本Spanned的场景
    • String文本和Spanned超链接文本不能直接拼接到一起
    • 需要调用TextViewappend(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();//这里必须要清除旧的Span
        URLSpan[] 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() {
                            @Override
                            public 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() {
                            @Override
                            public 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;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

第四步:项目中的使用

  • 批量处理html文本中超链接用法:
Spanned htmlContent = Html.fromHtml("<a href=\"privacy\">查看用户隐私协议</a><a href=\"important\">查看重要声明</a>");

contentTextView.setText(LinkUtils.handleContentLink(this, htmlContent));
  • 1
  • 2
  • 3
  • 纯文本拼接超链接文本用法:
Spanned linkSpan = LinkUtils.getClickableLink(this, "隐私和条款"
                    , new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            //自定义点击跳转
                        }
                    });
String content = "点击查看 ";
textView.setText(content);
textView.append((linkSpan));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/300541
推荐阅读
相关标签
  

闽ICP备14008679号