android TextView中识别url

时间:2022-11-26 22:57:42

一、需求

     1、在TextView中识别url;

     2、能识别出非http://|https://|ftp://等协议开头的非标准url

     3、对url设置颜色、下划线、点击等等

二、方案

 LinkedList<String> mStringList = new LinkedList<String>();
    LinkedList<UrlInfo> mUrlInfos = new LinkedList<UrlInfo>();
    private String pattern =
            "((http|ftp|https)://)(([a-zA-Z0-9\\._-]+\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\\&%_\\./-~-]*)?|(([a-zA-Z0-9\\._-]+\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\\&%_\\./-~-]*)?";

    Pattern r = Pattern.compile(pattern);
    Matcher m;
    int flag= Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;

    class UrlInfo {
        public int start;
        public int end;
    }


 public SpannableStringBuilderForAllvers identifyUrl(CharSequence text) {
        mStringList.clear();
        mUrlInfos.clear();

        CharSequence contextText;
        CharSequence clickText;
        text = text == null ? "" : text;
        //以下用于拼接本来存在的spanText
        SpannableStringBuilderForAllvers span = new SpannableStringBuilderForAllvers(text);
        ClickableSpan[] clickableSpans = span.getSpans(0, text.length(), ClickableSpan.class);
        if (clickableSpans.length > 0) {
            int start=0;
            int end=0;
            for (int i=0;i<clickableSpans.length;i++){
                start=span.getSpanStart(clickableSpans[0]);
                end=span.getSpanEnd(clickableSpans[i]);
            }
            //可点击文本后面的内容页
            contextText = text.subSequence(end, text.length());
            //可点击文本
            clickText = text.subSequence(start,
                    end);
        } else {
            contextText = text;
            clickText = null;
        }
        m = r.matcher(contextText);
        //匹配成功
        while (m.find()) {
            //得到网址数
            UrlInfo info = new UrlInfo();
            info.start = m.start();
            info.end = m.end();
            mStringList.add(m.group());
            mUrlInfos.add(info);
        }
        return joinText(clickText, contextText);
    }

    /** 拼接文本 */
    private SpannableStringBuilderForAllvers joinText(CharSequence clickSpanText,
                                                      CharSequence contentText) {
        SpannableStringBuilderForAllvers spanBuilder;
        if (clickSpanText != null) {
            spanBuilder = new SpannableStringBuilderForAllvers(clickSpanText);
        } else {
            spanBuilder = new SpannableStringBuilderForAllvers();
        }
        if (mStringList.size() > 0) {
            //只有一个网址
            if (mStringList.size() == 1) {
                String preStr = contentText.toString().substring(0, mUrlInfos.get(0).start);
                spanBuilder.append(preStr);
                String url = mStringList.get(0);
                int start = spanBuilder.length();
                spanBuilder.append(url, new UnderlineSpan(), flag);
                int end = spanBuilder.length();
                if (start >= 0 && end > 0 && end > start) {
                    spanBuilder.setSpan(new ForegroundColorSpan(Color.BLUE), start, end, flag);
                }
                String nextStr = contentText.toString().substring(mUrlInfos.get(0).end);
                spanBuilder.append(nextStr);
            } else {
                //有多个网址
                for (int i = 0; i < mStringList.size(); i++) {
                    if (i == 0) {
                        //拼接第1个span的前面文本
                        String headStr =
                                contentText.toString().substring(0, mUrlInfos.get(0).start);
                        spanBuilder.append(headStr);
                    }
                    if (i == mStringList.size() - 1) {
                        //拼接最后一个span的后面的文本
                        int start = spanBuilder.length();
                        spanBuilder.append(mStringList.get(i), new UnderlineSpan(),
                                flag);
                        int end = spanBuilder.length();
                        if (start >= 0 && end > 0 && end > start) {
                            spanBuilder.setSpan(new ForegroundColorSpan(Color.BLUE), start, end, flag);
                        }
                        String footStr = contentText.toString().substring(mUrlInfos.get(i).end);
                        spanBuilder.append(footStr);
                    }
                    if (i != mStringList.size() - 1) {
                        //拼接两两span之间的文本
                        int start = spanBuilder.length();
                        spanBuilder.append(mStringList.get(i), new UnderlineSpan(), flag);
                        int end = spanBuilder.length();
                        if (start >= 0 && end > 0 && end > start) {
                            spanBuilder.setSpan(new ForegroundColorSpan(Color.BLUE), start, end, flag);
                        }
                        String betweenStr = contentText.toString()
                                .substring(mUrlInfos.get(i).end,
                                        mUrlInfos.get(i + 1).start);
                        spanBuilder.append(betweenStr);
                    }
                }
            }
        } else {
            spanBuilder.append(contentText);
        }
        return spanBuilder;
    }
public class SpannableStringBuilderForAllvers extends SpannableStringBuilder{

    public SpannableStringBuilderForAllvers() {
        super("");
    }
    public SpannableStringBuilderForAllvers(CharSequence text) {
        super(text, 0, text.length());
    }
    public SpannableStringBuilderForAllvers(CharSequence text, int start, int end){
        super(text,start,end);
    }

    @Override
    public SpannableStringBuilder append(CharSequence text) {
       if (text == null) {
           return this;
       }
        int length = length();
        return (SpannableStringBuilderForAllvers)replace(length, length, text, 0, text.length());
    }

    /**该方法在原API里面只支持API21或者以上,这里适应低版本*/
    public SpannableStringBuilderForAllvers append(CharSequence text, Object what, int flags) {
        if (text == null) {
            return this;
        }
        int start = length();
        append(text);
        setSpan(what, start, length(), flags);
        return this;
    }
}