android:autoLink 对于 phone 数字并不总是有效
android:autoLink for phone numbers doesn't always work
我有一个简单的 TextView
,本地 phone 号码为 852112222 或 (8 5) 211 2222。
我需要它是可点击的,所以我自然而然地使用了android:autoLink="all"
。
但出于某种原因,我不明白相同的 phone 号码在所有设备上都不是 "linkified"。
在普通的 Genymotion 设备上它不起作用。在我个人的 OnePlus2 设备上,它工作正常。
在不同设备上进行了测试 - 运气不好。
可能是什么问题?
用户帐户偏好? Android版本? ORM?还有别的吗?
对于 phone 特定的自动链接,您应该使用
android:autoLink="phone"
更多参考:textview autolink
你能试试下面的代码吗?以编程方式设置属性。
Activity
package custom.com.android_lab;
import android.app.Activity;
import android.os.Bundle;
import android.text.util.Linkify;
import android.widget.TextView;
/**
* You can use Activity or AppCompatActivity
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// local phone number 852112222 or (8 5) 211 2222.
// Tested OK!
TextView textView1 = (TextView) findViewById(R.id.textv1);
textView1.setAutoLinkMask(Linkify.PHONE_NUMBERS);
textView1.setText("852112222");
// Tested OK!
TextView textView2 = (TextView) findViewById(R.id.textv2);
textView2.setAutoLinkMask(Linkify.PHONE_NUMBERS);
textView2.setText("(85) 211-2222");
// Tested Failed!
// Reason : Need to apply setAutoLinkMask prior to apply setText
TextView textView3 = (TextView) findViewById(R.id.textv3);
textView3.setText("852112222");
textView2.setAutoLinkMask(Linkify.PHONE_NUMBERS);
}
}
查看
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textv1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textv2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textv3"
android:layout_width="match_parent"
android:layout_height="55dp" />
</LinearLayout>
测试设备
- 一加一与Android7.1
- Genymotion。 4.1.1 - API 16
我在使用 auto link 时看到了很多不一致的地方。如果您正在为产品版本工作。始终选择带有 LinkMovementMethod 的 SpannableStringBuilder。您可以很好地控制文本的显示方式。
下面的代码片段可能会有所帮助。
String phone = "your phone number";
String message = "Phone number is: ";
Spannable span = new SpannableString(String.format("%s\n%s",message, phone));
ForegroundColorSpan color = new ForegroundColorSpan(Res.color(R.color.blue));
ClickableSpan click = new ClickableSpan() {
@Override
public void onClick(View widget) {
Navigator.dialer("your phone number");
}
public void updateDrawState(TextPaint ds) {// override updateDrawState
ds.setUnderlineText(false); // set to false to remove underline
}
};
span.setSpan(color, message.length(), message.length() + phone.length() + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
span.setSpan(click, message.length(), message.length() + phone.length() + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
changesMessageLabel.setText(span);
changesMessageLabel.setMovementMethod(LinkMovementMethod.getInstance());
这是我的调查。
我创建了一个新项目,并将 android:autoLink="all"
添加到 activity_main.xml
中的文本视图。感谢 Android Studio 的开发者,我可以看到预览,并且发现了一些有趣的东西:
12345
不是 linked
123456
不是 linked
1234567
linked
12345678
linked
123456789
不是 linked
1234567890
未链接
12345678901
linked
123456789012
不是 linked
我的 phone 结果是一样的。所以我查看了源代码,搜索关键字autolink,然后我找到了这个:
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
...
// unconcerned code above
if (mAutoLinkMask != 0) {
Spannable s2;
if (type == BufferType.EDITABLE || text instanceof Spannable) {
s2 = (Spannable) text;
} else {
s2 = mSpannableFactory.newSpannable(text);
}
if (Linkify.addLinks(s2, mAutoLinkMask)) {
text = s2;
type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
/*
* We must go ahead and set the text before changing the
* movement method, because setMovementMethod() may call
* setText() again to try to upgrade the buffer type.
*/
mText = text;
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
if (mLinksClickable && !textCanBeSelected()) {
setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
...
// unconcerned code above
}
所以关键字现在是 Linkify
。对于 addLinks
:
public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
...
if ((mask & PHONE_NUMBERS) != 0) {
gatherTelLinks(links, text);
}
...
}
private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE);
for (PhoneNumberMatch match : matches) {
LinkSpec spec = new LinkSpec();
spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
spec.start = match.start();
spec.end = match.end();
links.add(spec);
}
}
然后,不好的事情发生了,SDK没有PhoneNumberUtil
,具体就是下面这3个类:
import com.android.i18n.phonenumbers.PhoneNumberMatch;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
目前,第一个原因浮出水面:Locale.getDefault().getCountry()
。
所以我去设置,找到语言,选择中文。结果如下:
12345
linked
123456
linked
1234567
linked
12345678
linked
123456789
linked
1234567890
linked
12345678901
linked
123456789012
linked
其次,对于com.android.i18n.phonenumbers
的包,我发现了这个:
https://android.googlesource.com/platform/external/libphonenumber/+/ics-factoryrom-2-release/java/src/com/android/i18n/phonenumbers
如果您有兴趣,请查看上面的link。 URL 中的注意事项:ics-factoryrom-2-release
。所以我非常怀疑这是平台依赖。
对于解决方案,是对的,完全控制LinkMovementMethod
是一个不错的选择。
我建议你只需添加国家代码,你所有的问题都会得到解决,
android:autoLink="phone"
android:text="+91-8000000000"
如果我们在号码前加上国家代码,就不需要其他临时解决方案了。
只需执行以下操作
TextView userInput= (TextView) view.findViewById(R.id.textView);
if(userInput != null){
Linkify.addLinks(userInput, Patterns.PHONE,"tel:",Linkify.sPhoneNumberMatchFilter,Linkify.sPhoneNumberTransformFilter);
userInput.setMovementMethod(LinkMovementMethod.getInstance());
}
并删除
android:autoLink
来自您的 xml 文件
在 phone 数字为 11 位或更多且有 3 个块的情况下,答案对我有用。 例如123 456 78910
TextView textView = findViewById(R.id.text_view);
textView.setText("123 456 78910");
Linkify.addLinks(textView, Patterns.PHONE, "tel:", Linkify.sPhoneNumberMatchFilter,
Linkify.sPhoneNumberTransformFilter);
我必须在设置文本后调用 Linkify.addLinks
才能正常工作。
请注意,Linkify.addLinks
已经在文本视图上调用了 setMovementMethod
。
自动链接不接受某些号码="phone"
因此您可以直接调用 Phone 向 TextView 添加 clickListener 的 Intent。
将 phone 声明为 class 属性:
private String phone = "1234567890";
在activity的onCreate()方法中:
//...
TextView tvPhoneTelefone = (TextView) findViewById(R.id.tv_telefone);
tvPhone.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent callIntent = new Intent(Intent.ACTION_DIAL);
callIntent.setData(Uri.parse("tel:" + phone ));
startActivity(callIntent);
}
});
这应该适用于任何数字。
我为 phone 个数字制作了一个通用模式并添加了一个 Linkify 掩码。 Kotlin,扩展函数:
fun TextView.makeLinkedable(){
val pattern = Pattern.compile("""([\d|\(][\h|\(\d{3}\)|\.|\-|\d]{4,}\d)""",
Pattern.CASE_INSENSITIVE)
LinkifyCompat.addLinks(this, Linkify.ALL)
LinkifyCompat.addLinks(this, pattern, "tel://", null, null, null)
setLinkTextColor(ContextCompat.getColor(context, R.color.blue))
}
应该适用于所有设备
重点在于:
val pattern = Pattern.compile("""([\d|\(][\h|\(\d{3}\)|\.|\-|\d]{4,}\d)""",
Pattern.CASE_INSENSITIVE)
LinkifyCompat.addLinks(this, pattern, "tel://", null, null, null)
在Java中:
Pattern pattern = Pattern.compile("([\d|(][\h|(\d{3})|.|\-|\d]{4,}\d{4,})", Pattern.CASE_INSENSITIVE);
LinkifyCompat.addLinks(textView, pattern, "tel://", null, null, null);
对我来说是最好的解决方案。
我将它改编为 Java 并在最后指定了至少 4 个数字(因此它不会 link 验证一些以 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|\(\d{3}\)|\.|\-|\d]{4,}\d{4,})", Pattern.CASE_INSENSITIVE);
LinkifyCompat.addLinks(textView, pattern, "tel://", null, null, null); // this adds the format for all kinds of phone number
如果您只想 link 数字,请删除第一行(带有 "Linkify.ALL" 的行)。
我使用 linkify
使 phone 号码可点击,它对我有用
private val PHONE_NUMBER_VALIDATE = "^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s./0-9]*$"
val spannableString = SpannableString(mMarker.snippet)
val pattern = Pattern.compile(PHONE_NUMBER_VALIDATE, Pattern.MULTILINE)
LinkifyCompat.addLinks(spannableString, pattern, "tel://")
textview.text = spannableString
textview.movementMethod = LinkMovementMethod.getInstance()
确保您没有在 XML
中添加 android:autoLink="phone"
在这里,我尝试Pattern.MULTILINE
编译模式
Pattern.MULTILINE
tells Java to accept the anchors ^ and $ to match
at the start and end of each line (otherwise they only match at the
start/end of the entire string).
我为文本视图创建了一个扩展函数,使电话号码在所有设备上都能正常工作:
import android.text.method.LinkMovementMethod
import android.text.util.Linkify
import android.util.Patterns
import android.widget.TextView
import androidx.core.text.util.LinkifyCompat
fun TextView.compatMakeLinksClickable() {
LinkifyCompat.addLinks(this, Linkify.ALL)
// For some phone manufacturers (e.g. Moto G5 plus and some samsung devices)
// Linkify.ALL does not work for phonenumbers
// therefore we added the next line, to fix that:
Linkify.addLinks(
this, Patterns.PHONE, "tel:",
Linkify.sPhoneNumberMatchFilter,
Linkify.sPhoneNumberTransformFilter
);
this.movementMethod = LinkMovementMethod.getInstance()
}
我有一个简单的 TextView
,本地 phone 号码为 852112222 或 (8 5) 211 2222。
我需要它是可点击的,所以我自然而然地使用了android:autoLink="all"
。
但出于某种原因,我不明白相同的 phone 号码在所有设备上都不是 "linkified"。
在普通的 Genymotion 设备上它不起作用。在我个人的 OnePlus2 设备上,它工作正常。 在不同设备上进行了测试 - 运气不好。
可能是什么问题?
用户帐户偏好? Android版本? ORM?还有别的吗?
对于 phone 特定的自动链接,您应该使用
android:autoLink="phone"
更多参考:textview autolink
你能试试下面的代码吗?以编程方式设置属性。
Activity
package custom.com.android_lab;
import android.app.Activity;
import android.os.Bundle;
import android.text.util.Linkify;
import android.widget.TextView;
/**
* You can use Activity or AppCompatActivity
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// local phone number 852112222 or (8 5) 211 2222.
// Tested OK!
TextView textView1 = (TextView) findViewById(R.id.textv1);
textView1.setAutoLinkMask(Linkify.PHONE_NUMBERS);
textView1.setText("852112222");
// Tested OK!
TextView textView2 = (TextView) findViewById(R.id.textv2);
textView2.setAutoLinkMask(Linkify.PHONE_NUMBERS);
textView2.setText("(85) 211-2222");
// Tested Failed!
// Reason : Need to apply setAutoLinkMask prior to apply setText
TextView textView3 = (TextView) findViewById(R.id.textv3);
textView3.setText("852112222");
textView2.setAutoLinkMask(Linkify.PHONE_NUMBERS);
}
}
查看
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textv1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textv2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textv3"
android:layout_width="match_parent"
android:layout_height="55dp" />
</LinearLayout>
测试设备
- 一加一与Android7.1
- Genymotion。 4.1.1 - API 16
我在使用 auto link 时看到了很多不一致的地方。如果您正在为产品版本工作。始终选择带有 LinkMovementMethod 的 SpannableStringBuilder。您可以很好地控制文本的显示方式。
下面的代码片段可能会有所帮助。
String phone = "your phone number";
String message = "Phone number is: ";
Spannable span = new SpannableString(String.format("%s\n%s",message, phone));
ForegroundColorSpan color = new ForegroundColorSpan(Res.color(R.color.blue));
ClickableSpan click = new ClickableSpan() {
@Override
public void onClick(View widget) {
Navigator.dialer("your phone number");
}
public void updateDrawState(TextPaint ds) {// override updateDrawState
ds.setUnderlineText(false); // set to false to remove underline
}
};
span.setSpan(color, message.length(), message.length() + phone.length() + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
span.setSpan(click, message.length(), message.length() + phone.length() + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
changesMessageLabel.setText(span);
changesMessageLabel.setMovementMethod(LinkMovementMethod.getInstance());
这是我的调查。
我创建了一个新项目,并将 android:autoLink="all"
添加到 activity_main.xml
中的文本视图。感谢 Android Studio 的开发者,我可以看到预览,并且发现了一些有趣的东西:
12345
不是 linked123456
不是 linked1234567
linked12345678
linked123456789
不是 linked1234567890
未链接12345678901
linked123456789012
不是 linked
我的 phone 结果是一样的。所以我查看了源代码,搜索关键字autolink,然后我找到了这个:
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
...
// unconcerned code above
if (mAutoLinkMask != 0) {
Spannable s2;
if (type == BufferType.EDITABLE || text instanceof Spannable) {
s2 = (Spannable) text;
} else {
s2 = mSpannableFactory.newSpannable(text);
}
if (Linkify.addLinks(s2, mAutoLinkMask)) {
text = s2;
type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
/*
* We must go ahead and set the text before changing the
* movement method, because setMovementMethod() may call
* setText() again to try to upgrade the buffer type.
*/
mText = text;
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
if (mLinksClickable && !textCanBeSelected()) {
setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
...
// unconcerned code above
}
所以关键字现在是 Linkify
。对于 addLinks
:
public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
...
if ((mask & PHONE_NUMBERS) != 0) {
gatherTelLinks(links, text);
}
...
}
private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE);
for (PhoneNumberMatch match : matches) {
LinkSpec spec = new LinkSpec();
spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
spec.start = match.start();
spec.end = match.end();
links.add(spec);
}
}
然后,不好的事情发生了,SDK没有PhoneNumberUtil
,具体就是下面这3个类:
import com.android.i18n.phonenumbers.PhoneNumberMatch;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
目前,第一个原因浮出水面:Locale.getDefault().getCountry()
。
所以我去设置,找到语言,选择中文。结果如下:
12345
linked123456
linked1234567
linked12345678
linked123456789
linked1234567890
linked12345678901
linked123456789012
linked
其次,对于com.android.i18n.phonenumbers
的包,我发现了这个:
https://android.googlesource.com/platform/external/libphonenumber/+/ics-factoryrom-2-release/java/src/com/android/i18n/phonenumbers
如果您有兴趣,请查看上面的link。 URL 中的注意事项:ics-factoryrom-2-release
。所以我非常怀疑这是平台依赖。
对于解决方案,LinkMovementMethod
是一个不错的选择。
我建议你只需添加国家代码,你所有的问题都会得到解决,
android:autoLink="phone"
android:text="+91-8000000000"
如果我们在号码前加上国家代码,就不需要其他临时解决方案了。
只需执行以下操作
TextView userInput= (TextView) view.findViewById(R.id.textView);
if(userInput != null){
Linkify.addLinks(userInput, Patterns.PHONE,"tel:",Linkify.sPhoneNumberMatchFilter,Linkify.sPhoneNumberTransformFilter);
userInput.setMovementMethod(LinkMovementMethod.getInstance());
}
并删除
android:autoLink
来自您的 xml 文件
TextView textView = findViewById(R.id.text_view);
textView.setText("123 456 78910");
Linkify.addLinks(textView, Patterns.PHONE, "tel:", Linkify.sPhoneNumberMatchFilter,
Linkify.sPhoneNumberTransformFilter);
我必须在设置文本后调用 Linkify.addLinks
才能正常工作。
请注意,Linkify.addLinks
已经在文本视图上调用了 setMovementMethod
。
自动链接不接受某些号码="phone"
因此您可以直接调用 Phone 向 TextView 添加 clickListener 的 Intent。
将 phone 声明为 class 属性:
private String phone = "1234567890";
在activity的onCreate()方法中:
//...
TextView tvPhoneTelefone = (TextView) findViewById(R.id.tv_telefone);
tvPhone.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent callIntent = new Intent(Intent.ACTION_DIAL);
callIntent.setData(Uri.parse("tel:" + phone ));
startActivity(callIntent);
}
});
这应该适用于任何数字。
我为 phone 个数字制作了一个通用模式并添加了一个 Linkify 掩码。 Kotlin,扩展函数:
fun TextView.makeLinkedable(){
val pattern = Pattern.compile("""([\d|\(][\h|\(\d{3}\)|\.|\-|\d]{4,}\d)""",
Pattern.CASE_INSENSITIVE)
LinkifyCompat.addLinks(this, Linkify.ALL)
LinkifyCompat.addLinks(this, pattern, "tel://", null, null, null)
setLinkTextColor(ContextCompat.getColor(context, R.color.blue))
}
应该适用于所有设备
重点在于:
val pattern = Pattern.compile("""([\d|\(][\h|\(\d{3}\)|\.|\-|\d]{4,}\d)""",
Pattern.CASE_INSENSITIVE)
LinkifyCompat.addLinks(this, pattern, "tel://", null, null, null)
在Java中:
Pattern pattern = Pattern.compile("([\d|(][\h|(\d{3})|.|\-|\d]{4,}\d{4,})", Pattern.CASE_INSENSITIVE);
LinkifyCompat.addLinks(textView, pattern, "tel://", null, null, null);
我将它改编为 Java 并在最后指定了至少 4 个数字(因此它不会 link 验证一些以 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|\(\d{3}\)|\.|\-|\d]{4,}\d{4,})", Pattern.CASE_INSENSITIVE);
LinkifyCompat.addLinks(textView, pattern, "tel://", null, null, null); // this adds the format for all kinds of phone number
如果您只想 link 数字,请删除第一行(带有 "Linkify.ALL" 的行)。
我使用 linkify
使 phone 号码可点击,它对我有用
private val PHONE_NUMBER_VALIDATE = "^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s./0-9]*$"
val spannableString = SpannableString(mMarker.snippet)
val pattern = Pattern.compile(PHONE_NUMBER_VALIDATE, Pattern.MULTILINE)
LinkifyCompat.addLinks(spannableString, pattern, "tel://")
textview.text = spannableString
textview.movementMethod = LinkMovementMethod.getInstance()
确保您没有在 XML
中添加android:autoLink="phone"
在这里,我尝试Pattern.MULTILINE
编译模式
Pattern.MULTILINE
tells Java to accept the anchors ^ and $ to match at the start and end of each line (otherwise they only match at the start/end of the entire string).
我为文本视图创建了一个扩展函数,使电话号码在所有设备上都能正常工作:
import android.text.method.LinkMovementMethod
import android.text.util.Linkify
import android.util.Patterns
import android.widget.TextView
import androidx.core.text.util.LinkifyCompat
fun TextView.compatMakeLinksClickable() {
LinkifyCompat.addLinks(this, Linkify.ALL)
// For some phone manufacturers (e.g. Moto G5 plus and some samsung devices)
// Linkify.ALL does not work for phonenumbers
// therefore we added the next line, to fix that:
Linkify.addLinks(
this, Patterns.PHONE, "tel:",
Linkify.sPhoneNumberMatchFilter,
Linkify.sPhoneNumberTransformFilter
);
this.movementMethod = LinkMovementMethod.getInstance()
}