Android TextView,Linkify 每种语言的美国电话号码

Posted

技术标签:

【中文标题】Android TextView,Linkify 每种语言的美国电话号码【英文标题】:Andorid TextView, Linkify US phone number in every language 【发布时间】:2018-06-13 03:57:08 【问题描述】:

我有一条包含我们电话号码的短信。无论电话语言是什么,我都想让它们可点击。我调查了自动链接的工作原理,发现了我尝试在自定义 TextView 上使用的 Linkify.addLinks 方法。

public class PhoneNumberLinkTextView extends android.support.v7.widget.AppCompatTextView 

    public PhoneNumberLinkTextView(Context context) 
        super(context);
    

    public PhoneNumberLinkTextView(Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
    

    public PhoneNumberLinkTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
    

    public void setUSNumberText(CharSequence text) 
        SpannableStringBuilder spanText = new SpannableStringBuilder(text);
        if (addLinks(spanText)) 
            setText(spanText);
         else 
            setText(text);
        
    

    public boolean addLinks(@NonNull SpannableStringBuilder text) 
        ArrayList<LinkSpec> links = new ArrayList<>();

        gatherTelLinks(links, text);

        if (links.isEmpty()) 
            return false;
        

        Object[] spans = text.getSpans(0, text.length(), Object.class);
        final int count = spans.length;
        for (int i = 0; i < count; i++) 
            text.removeSpan(spans[i]);
        

        for (LinkSpec link: links) 
            applyLink(link.url, link.start, link.end, text);
        

        return true;
    

    private void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) 
        PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
        Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
                Locale.US.getCountry(), PhoneNumberUtil.Leniency.POSSIBLE, Long.MAX_VALUE);
        for (PhoneNumberMatch match : matches) 
            LinkSpec spec = new LinkSpec();
            spec.url = "tel:" + normalizeNumber(match.rawString());
            spec.start = match.start();
            spec.end = match.end();
            links.add(spec);
        
    

    private void applyLink(String url, int start, int end, Spannable text) 
        URLSpan span = new URLSpan (url);

        text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    

    /**
     * Normalize a phone number by removing the characters other than digits. If
     * the given number has keypad letters, the letters will be converted to
     * digits first.
     *
     * @param phoneNumber the number to be normalized.
     * @return the normalized number.
     */
    public String normalizeNumber(String phoneNumber) 
        if (TextUtils.isEmpty(phoneNumber)) 
            return "";
        

        StringBuilder sb = new StringBuilder();
        int len = phoneNumber.length();
        for (int i = 0; i < len; i++) 
            char c = phoneNumber.charAt(i);
            // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
            int digit = Character.digit(c, 10);
            if (digit != -1) 
                sb.append(digit);
             else if (sb.length() == 0 && c == '+') 
                sb.append(c);
             else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) 
                return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
            
        
        return sb.toString();
    

    class LinkSpec 
        String url;
        int start;
        int end;
    

此代码目前在视觉上工作。我的美国号码的格式符合我的预期,但我的电话号码不可点击。

然后我尝试在我的 setText() 之后添加一个 setMovementMethod(LinkMovementMethod.getInstance()),但这次我丢失了格式化为电话号码的美国号码。

有谁知道我怎样才能实现我想要做的事情?

【问题讨论】:

【参考方案1】:

我通过用扩展 ClickableSpan 的自定义类替换 URLSpan 来解决我的问题。

private class USNumberSpan extends ClickableSpan 

        private String url;

        USNumberSpan(String url) 
            this.url = url;
        

        @Override
        public void onClick(View widget) 
            Uri uri = Uri.parse(url);
            Context context = widget.getContext();
            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
            intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
            try 
                context.startActivity(intent);
             catch (ActivityNotFoundException e) 
                Log.w("URLSpan", "Activity was not found for intent, " + intent.toString(), e);
            
        
    

我不知道为什么这是有效的,而 URLSpan 不是,但我很高兴它有效。

【讨论】:

【参考方案2】:

我在此链接上找到了一个 Kotlin 答案(由用户 Iliya Mashin 提供),其中包含适用于任何类型数字的模式:android:autoLink for phone numbers doesn't always work

我针对 Java 进行了调整,最后指定了至少 4 个数字(因此它不会链接一些以 3 个数字“xxxxx-xxx”结尾的邮政编码),所以,如果您不想要这个特定的限制,只需删除表达式末尾的“4,”)。

LinkifyCompat.addLinks(textView, Linkify.ALL); // This will use the usual linkify for any other format
Pattern pattern = Pattern.compile("([\\d|\\(][\\h|\\(\\d3\\)|\\.|\\-|\\d]4,\\d4,)", Pattern.CASE_INSENSITIVE);
    LinkifyCompat.addLinks(textView, pattern, "tel://", null, null, null); // this adds the format for all kinds of phone number

如果您只想链接数字,请删除第一行(带有“Linkify.ALL”的行)。

【讨论】:

以上是关于Android TextView,Linkify 每种语言的美国电话号码的主要内容,如果未能解决你的问题,请参考以下文章

Android TextView 启用 LinkiFy 和 Href 标签

java 添加指向TextView的链接。 Linkify。字体:https://stackoverflow.com/questions/4746293/android-linkify-textvie

Android Linkify.addLinks() 适用于一个 textview 而不是另一个

如何从linkify textview获取url

Linkify 点击文本并转到网址

在 android TextView 中链接