Linkify使用方法——借鉴系统

Linkify系统中的添加步骤:

先使用addLinks(Spannale text,int mask){};添加连接

首先会判断mask==0如果是,则直接跳出,否则继续进行判断:

if (mask == 0) {

            return false;

        }

接下来先将文本中的所有Span移除:

URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);

        for (int i = old.length - 1; i >= 0; i--) {

            text.removeSpan(old[i]);

        }

然后在新建一个List来保存链接数据

ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();

拿电话号码为例,如果mask与电话号码相同的话,就

if ((mask & PHONE_NUMBERS) != 0) {

            gatherLinks(links, text, Patterns.PHONE,

                new String[] { "tel:" },

                sPhoneNumberMatchFilter, sPhoneNumberTransformFilter);

        }

将保存所以链接的links放入,要查看是否有该链接的文本,然后是Patterns,然后新建一个String加上了"tel:"字段方便后面对Intent进行处理.然后是MatchFilter和TransformFileter

下面我们就看看gatherLinks方法的实现:

private static final void gatherLinks(ArrayList<LinkSpec> links,
            Spannable s, Pattern pattern, String[] schemes,
            MatchFilter matchFilter, TransformFilter transformFilter) {
        Matcher m = pattern.matcher(s);
        while (m.find()) {
            int start = m.start();
            int end = m.end();
            if (matchFilter == null || matchFilter.acceptMatch(s, start, end)) {
                LinkSpec spec = new LinkSpec();
                String url = makeUrl(m.group(0), schemes, m, transformFilter);
                spec.url = url;
                spec.start = start;
                spec.end = end;
               links.add(spec);
            }
        }
    }

我们会发现他先使用pattern.matcher(Spannable s);s也就是我们要检测的文本,来创建一个matcher,然后使用matcher.find来依次发现符合正则表达式的内容,在每发现一个之后,如果matchFilter不为null(用户自定义了)或者通过了matchFilter.acceptMatch(Spannable s,m.start(),m.end())函数的验证,其确实为一个null,然后将其制作为一个utl然后将url添加到LinkSpec中。

LinkSpec的构造:

class LinkSpec {
    String url;
    int start;
    int end;
}

看Patterns.PHONE的正则表达式如下:

/**
     * This pattern is intended for searching for things that look like they
     * might be phone numbers in arbitrary text, not for validating whether
     * something is in fact a phone number.  It will miss many things that
     * are legitimate phone numbers.
     *
     * <p> The pattern matches the following:
     * <ul>
     * <li>Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes
     * may follow.
     * <li>Optionally, sets of digits in parentheses, separated by spaces, dots, or dashes.
     * <li>A string starting and ending with a digit, containing digits, spaces, dots, and/or dashes.
     * </ul>
     */

    public static final Pattern PHONE

        = Pattern.compile(                                  // sdd = space, dot, or dash

                "(\\+[0-9]+[\\- \\.]*)?"                    // +<digits><sdd>*

                + "(\\([0-9]+\\)[\\- \\.]*)?"               //(<digits>)<sdd>*

                + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit>

然后看MatchFilter通过其来过滤电话号码数字个数不到达一个电话号码的数字组合。

/**
     *  Filters out URL matches that don't have enough digits to be a
     *  phone number.
     */

    public static final MatchFilter sPhoneNumberMatchFilter = new MatchFilter() {

        public final boolean acceptMatch(CharSequence s, int start, int end) {
            int digitCount = 0;
            for (int i = start; i < end; i++) {
                if (Character.isDigit(s.charAt(i))) {
                    digitCount++;
                    if (digitCount >= PHONE_NUMBER_MINIMUM_DIGITS) {
                       return true;
                    }
                }
            }
            return false;
        }
    };

其中PHONE_NUMBER_MINIMUM_DIGITS是这样解释的:也就是说最小判断电话号码长度为五位数字

/**

     * Don't treat anything with fewer than this many digits as a

     * phone number.

     */

    private static final int PHONE_NUMBER_MINIMUM_DIGITS = 5;

然后看makeUrl()方法如下:

private static final String makeUrl(String url, String[] prefixes,

            Matcher m, TransformFilter filter) {

        if (filter != null) {
            url = filter.transformUrl(m, url);
        }

        boolean hasPrefix = false;

        for (int i = 0; i < prefixes.length; i++) {

            if (url.regionMatches(true, 0, prefixes[i], 0,
                                  prefixes[i].length())) {
                hasPrefix = true;
                // Fix capitalization if necessary
                if (!url.regionMatches(false, 0, prefixes[i], 0,
                                      prefixes[i].length())) {
                    url = prefixes[i] + url.substring(prefixes[i].length());
                }
                break;
            }
        }

        if (!hasPrefix) {
            url = prefixes[0] + url;
        }
        return url;
    }

接收的参数是:makeUrl(m.group(0), schemes, m, transformFilter);

group是针对()来说的,group(0)就是指的整个串,group(1) 指的是第一个括号里的东西,group(2)指的第二个括号里的东西。第一个参数也就是检测到的第一个符合要求的字符串,第二个就是new String[] { "tel:" }字符串,第三个是Matcher,第四个是TransformFilter具体里面是怎么撮合的utl自己看看就明白了

然后看TransformFilter格式转换可以将+1 (919) 555-1212的格式转换为再打电话时的+19195551212格式

/**

     *  Transforms matched phone number text into something suitable

     *  to be used in a tel: URL.  It does this by removing everything

     *  but the digits and plus signs.  For instance:

     *  &apos;+1 (919) 555-1212&apos;

     *  becomes &apos;+19195551212&apos;

     */

    public static final TransformFilter sPhoneNumberTransformFilter = new TransformFilter() {

        public final String transformUrl(final Matcher match, String url) {
            return Patterns.digitsAndPlusOnly(match);
        }
    };

其中digitsAndPlusOnly的方法如下:

/**
     * Convenience method to return only the digits and plus signs
     * in the matching string.
     *
     * @param matcher      The Matcher object from which digits and plus will
     *                     be extracted
     *
     * @return              A String comprising all of the digits and plus in
     *                     the match
     */

    public static final String digitsAndPlusOnly(Matcher matcher) {

        StringBuilder buffer = new StringBuilder();

        String matchingRegion = matcher.group();

        for (int i = 0, size = matchingRegion.length(); i < size; i++) {

            char character = matchingRegion.charAt(i);

            if (character == '+' || Character.isDigit(character)) {

                buffer.append(character);
            }
        }
        return buffer.toString();
    }

最后就是要撮合好的utl添加到LinkSpec中。

      

spec.url = url;
spec.start = start;
spec.end = end;
links.add(spec);

最后会通过applyLink方法来对文本添加Span

 

private static final void applyLink(String url, int start, int end, Spannable text) {
        URLSpan span = new URLSpan(url);
        text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

至于文本的setSpan方法,

 

/**
     * Attach the specified markup object to the range <code>start&hellip;end</code>
     * of the text, or move the object to that range if it was already
     * attached elsewhere.  See {@link  Spanned} for an explanation of
     * what the flags mean.  The object can be one that has meaning only
     * within your application, or it can be one that the text system will
     * use to affect text display or behavior.  Some noteworthy ones are
     * the subclasses of {@link  android.text.style.CharacterStyle} and
     * {@link  android.text.style.ParagraphStyle}, and
     * {@link  android.text.TextWatcher} and
     * {@link  android.text.SpanWatcher}.
     */
    public void setSpan(Object what, int start, int end, int flags);

//具体实现如下: 在android.text.SpannableStringBuilder下

 private void setSpan(boolean send,

                         Object what, int start, int end, int flags) {
        int nstart = start;
        int nend = end;
        checkRange("setSpan", start, end);
        if ((flags & START_MASK) == (PARAGRAPH << START_SHIFT)) {
            if (start != 0 && start != length()) {
                char c = charAt(start - 1);
                if (c != '\n')
                    throw new RuntimeException(
                            "PARAGRAPH span must start at paragraph boundary");
            }
        }
        if ((flags & END_MASK) == PARAGRAPH) {
            if (end != 0 && end != length()) {
                char c = charAt(end - 1);
                if (c != '\n')
                    throw new RuntimeException(
                            "PARAGRAPH span must end at paragraph boundary");
            }
        }
        if (start > mGapStart)
            start += mGapLength;
        else if (start == mGapStart) {
            int flag = (flags & START_MASK) >> START_SHIFT;
            if (flag == POINT || (flag == PARAGRAPH && start == length()))
                start += mGapLength;
        }

        if (end > mGapStart)
            end += mGapLength;
        else if (end == mGapStart) {
            int flag = (flags & END_MASK);
            if (flag == POINT || (flag == PARAGRAPH && end == length()))
                end += mGapLength;
        }
        int count = mSpanCount;
        Object[] spans = mSpans;
        for (int i = 0; i < count; i++) {

            if (spans[i] == what) {
                int ostart = mSpanStarts[i];
                int oend = mSpanEnds[i];
                if (ostart > mGapStart)
                    ostart -= mGapLength;
                if (oend > mGapStart)
                    oend -= mGapLength;
                mSpanStarts[i] = start;
                mSpanEnds[i] = end;
                mSpanFlags[i] = flags;
                if (send)
                    sendSpanChanged(what, ostart, oend, nstart, nend);
                return;
            }
        }
        if (mSpanCount + 1 >= mSpans.length) {

            int newsize = ArrayUtils.idealIntArraySize(mSpanCount + 1);
            Object[] newspans = new Object[newsize];
            int[] newspanstarts = new int[newsize];
            int[] newspanends = new int[newsize];
            int[] newspanflags = new int[newsize];
            System.arraycopy(mSpans, 0, newspans, 0, mSpanCount);
            System.arraycopy(mSpanStarts, 0, newspanstarts, 0, mSpanCount);
            System.arraycopy(mSpanEnds, 0, newspanends, 0, mSpanCount);
            System.arraycopy(mSpanFlags, 0, newspanflags, 0, mSpanCount);
            mSpans = newspans;
            mSpanStarts = newspanstarts;
            mSpanEnds = newspanends;
            mSpanFlags = newspanflags;
        }
        mSpans[mSpanCount] = what;
        mSpanStarts[mSpanCount] = start;
        mSpanEnds[mSpanCount] = end;
        mSpanFlags[mSpanCount] = flags;
        mSpanCount++;
        if (send)
            sendSpanAdded(what, nstart, nend);
    }
看了上面的源码,我想,你就可以根据自己的需要做添加自己的 Linkify了

转载于:https://my.oschina.net/zhibuji/blog/80059

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值