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 links = new ArrayList();

拿电话号码为例,如果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 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.
     *
     * 

The pattern matches the following: *

    *
  • Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes * may follow. *
  • Optionally, sets of digits in parentheses, separated by spaces, dots, or dashes. *
  • A string starting and ending with a digit, containing digits, spaces, dots, and/or dashes. *
*/ public static final Pattern PHONE = Pattern.compile( // sdd = space, dot, or dash "(\\+[0-9]+[\\- \\.]*)?" // +* + "(\\([0-9]+\\)[\\- \\.]*)?" //()* + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // + 然后看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:

     *  '+1 (919) 555-1212'

     *  becomes '+19195551212'

     */

    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 start…end
     * 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

你可能感兴趣的:(移动开发)