问题
I have a simple TextView
with local phone number 852112222 or (8 5) 211 2222.
I need it to be clickable, so naturally I used android:autoLink="all"
.
But for some reason I don't understand same phone number is not "linkified" on all devices.
On plain Genymotion device it didn't work. On my personal OnePlus2 device it worked. Tested on bunch on different devices - no luck.
What could be the issue?
User account preferences? Android version? ORM? Something else?
回答1:
Here is my investigation.
I created a new project, and added android:autoLink="all"
to a text view in activity_main.xml
. Thanks to the developers of Android Studio, I could see the preview, and I found something interesting:
12345
not linked123456
not linked1234567
linked12345678
linked123456789
not linked1234567890
not likned12345678901
linked123456789012
not linked
The result is the same on my phone. So I looked into the source code, searched for the keyword autolink, then I found this:
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
}
So the keyword is Linkify
now. For 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);
}
}
Then, something bad happened, the SDK doesn't have PhoneNumberUtil
, specifically these 3 classes below:
import com.android.i18n.phonenumbers.PhoneNumberMatch;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
For now, the first reason surfaced: Locale.getDefault().getCountry()
.
So I went to setting, found language, selected Chinese. The result is below:
12345
linked123456
linked1234567
linked12345678
linked123456789
linked1234567890
linked12345678901
linked123456789012
linked
Secondly, for the package of com.android.i18n.phonenumbers
, I found this:
https://android.googlesource.com/platform/external/libphonenumber/+/ics-factoryrom-2-release/java/src/com/android/i18n/phonenumbers
If you are interested, check the link above. Notice in the URL: ics-factoryrom-2-release
. So I highly doubt that this is platform-dependent.
For the solution, CleverAndroid is right, taking full control of LinkMovementMethod
is a good option.
回答2:
Just do the following
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());
}
and also remove the
android:autoLink
from your xml file
回答3:
Can you please try below code. Set attribute programmatically.
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);
}
}
View
<?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>
Testing Devices
- One plus one with Android 7.1
- Genymotion. 4.1.1 - API 16
回答4:
I would suggest you just to add country code and all your issue will be resolved,
android:autoLink="phone"
android:text="+91-8000000000"
If we add country code before the number, then no need of other temporary solutions.
回答5:
I have seen a lot of inconsistencies when using auto link. If at all you are working for a prod version. Always go for SpannableStringBuilder with LinkMovementMethod. You have good control of how the text needs to be displayed.
The snippet below may help.
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());
回答6:
Harsh Agrawal answer worked for me in cases where the phone number is 11 digits or more with 3 blocks. e.g. 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);
I had to call Linkify.addLinks
after setting text for it to work.
Note that Linkify.addLinks
already calls setMovementMethod
on the text view.
回答7:
Create universal pattern for phone numbers and add Linkify mask next(Kotlin, extension function):
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))
}
Should work for all devices
回答8:
For phone autolink specific you should use
android:autoLink="phone"
For more refer: textview autolink
回答9:
Some numbers are not accepted by the autoLink="phone"
So you can call directly the Phone Intent adding a clickListener to the TextView.
Declare phone as a class attribute:
private String phone = "1234567890";
At the onCreate() method of the activity:
//...
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);
}
});
This should work with any number.
回答10:
Iliya Mashin's answer was the best solution for me.
I adapted it for Java and specified at least 4 numbers in the end (so it won't linkify some zipcodes ending with 3 numbers "xxxxx-xxx"), so, if you don't want this specific limitation, just remove "{4,}" in the ending).
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
If you want to link just the numbers, remove the first line (the one with "Linkify.ALL").
来源:https://stackoverflow.com/questions/40788608/androidautolink-for-phone-numbers-doesnt-always-work