在我的应用程序的登录面板中,我在两个可编辑的文本视图中划分了国家/地区呼叫代码和剩余号码,如下所示:
我想在右边的文本视图中使用国际格式标准。如果电话号码为90544444的用户在这些框中键入号码,我想在左侧框中看到“90”,在右侧框中看到“54444444”。
因此,我尝试使用以下使用libphonenumber的实现:
/**
* Watches a {@link android.widget.TextView} and if a phone number is entered
* will format it.
* <p>
* Stop formatting when the user
* <ul>
* <li>Inputs non-dialable characters</li>
* <li>Removes the separator in the middle of string.</li>
* </ul>
* <p>
* The formatting will be restarted once the text is cleared.
*/
public class PhoneNumberFormattingTextWatcher implements TextWatcher {
/**
* Indicates the change was caused by ourselves.
*/
private boolean mSelfChange = false;
/**
* Indicates the formatting has been stopped.
*/
private boolean mStopFormatting;
private AsYouTypeFormatter mFormatter;
private String code;
/**
* The formatting is based on the current system locale and future locale changes
* may not take effect on this instance.
*/
public PhoneNumberFormattingTextWatcher() {
this(Locale.getDefault().getCountry());
}
/**
* The formatting is based on the given <code>countryCode</code>.
*
* @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
* where the phone number is being entered.
*/
public PhoneNumberFormattingTextWatcher(String countryCode) {
if (countryCode == null) throw new IllegalArgumentException();
mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
if (mSelfChange || mStopFormatting) {
return;
}
// If the user manually deleted any non-dialable characters, stop formatting
if (count > 0 && hasSeparator(s, start, count)) {
stopFormatting();
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mSelfChange || mStopFormatting) {
return;
}
// If the user inserted any non-dialable characters, stop formatting
if (count > 0 && hasSeparator(s, start, count)) {
stopFormatting();
}
}
@Override
public synchronized void afterTextChanged(Editable s) {
if (mStopFormatting) {
// Restart the formatting when all texts were clear.
mStopFormatting = !(s.length() == 0);
return;
}
if (mSelfChange) {
// Ignore the change caused by s.replace().
return;
}
String formatted = reformat(s, Selection.getSelectionEnd(s));
if (formatted != null) {
int rememberedPos = mFormatter.getRememberedPosition();
mSelfChange = true;
s.replace(0, s.length(), formatted, 0, formatted.length());
// The text could be changed by other TextWatcher after we changed it. If we found the
// text is not the one we were expecting, just give up calling setSelection().
if (formatted.equals(s.toString())) {
Selection.setSelection(s, rememberedPos);
}
mSelfChange = false;
}
// PhoneNumberUtils.ttsSpanAsPhoneNumber(s, 0, s.length());
}
/**
* Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the
* nearest dialable char to the left. For instance, if the number is (650) 123-45678 and '4' is
* removed then the cursor should be behind '3' instead of '-'.
*/
private String reformat(CharSequence s, int cursor) {
// The index of char to the leftward of the cursor.
int curIndex = cursor - 1;
String formatted = null;
mFormatter.clear();
char lastNonSeparator = 0;
boolean hasCursor = false;
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (PhoneNumberUtils.isNonSeparator(c)) {
if (lastNonSeparator != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor);
hasCursor = false;
}
lastNonSeparator = c;
}
if (i == curIndex) {
hasCursor = true;
}
}
if (lastNonSeparator != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor);
}
return formatted;
}
private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator)
: mFormatter.inputDigit(lastNonSeparator);
}
private void stopFormatting() {
mStopFormatting = true;
mFormatter.clear();
}
private boolean hasSeparator(final CharSequence s, final int start, final int count) {
for (int i = start; i < start + count; i++) {
char c = s.charAt(i);
if (!PhoneNumberUtils.isNonSeparator(c)) {
return true;
}
}
return false;
}
}
然而,这个TextWatcher格式化了包括呼叫代码在内的号码。换句话说,它成功格式化了“90544444”,但无法格式化“54444”。当输入的电话号码包含右侧文本视图中的国家/地区代码时,如何获得相同的结果?不用说,我希望得到以下输出:
CCP图书馆完整示例:
布局:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<com.hbb20.CountryCodePicker
android:id="@+id/ccp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:ccp_textSize="20sp"
android:layout_gravity="center"
app:ccp_flagBorderColor="@color/colorPrimary"
/>
<EditText
android:id="@+id/phone"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textSize="20sp"
android:autofillHints="Enter phone number"
android:inputType="phone|numberDecimal"
android:hint="@string/your_phone"
tools:text="9000000000"
/>
</LinearLayout>
活动/片段(在我的案例中为片段):
package app.my.fragments;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import com.hbb20.CountryCodePicker;
import com.hbb20.InternationalPhoneTextWatcher;
import java.util.Locale;
import app.my.R;
import app.my.util.Logger;
import app.my.util.TextHelper;
public class LoginEnterPhoneFragment extends Fragment {
private final static String TAG = LoginEnterPhoneFragment.class.getSimpleName();
private EditText phoneNumberView;
private CountryCodePicker ccp;
private InternationalPhoneTextWatcher internationalPhoneTextWatcher;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_login_phone, container, false);
phoneNumberView = view.findViewById(R.id.phone);
ccp = view.findViewById(R.id.ccp);
// Setting up ccp
ccp.setDefaultCountryUsingNameCode(Locale.getDefault().getCountry());
ccp.showNameCode(false);
ccp.setOnCountryChangeListener(new CountryCodePicker.OnCountryChangeListener() {
@Override
public void onCountrySelected() {
if (internationalPhoneTextWatcher != null) {
phoneNumberView.removeTextChangedListener(internationalPhoneTextWatcher);
}
internationalPhoneTextWatcher = new InternationalPhoneTextWatcher(getContext(), ccp.getSelectedCountryNameCode(), ccp.getSelectedCountryCodeAsInt());
phoneNumberView.addTextChangedListener(internationalPhoneTextWatcher);
// Triggering phoneNumberView.TextChanged to reformat phone number
if (TextHelper.isNotEmpty(phoneNumberView.getText().toString())) {
phoneNumberView.setText(String.format("+%s", phoneNumberView.getText()));
}
}
});
// Triggering ccp.CountryChanged to add InternationalPhoneTextWatcher to phoneNumberView
ccp.setCountryForNameCode(Locale.getDefault().getCountry());
// Setting up phoneNumberView
phoneNumberView.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void afterTextChanged(Editable s) {
String original = s.toString().replaceAll("[^\\d+]", "");
String result = original;
if (result.startsWith(ccp.getDefaultCountryCodeWithPlus())) {
result = result.substring(ccp.getDefaultCountryCodeWithPlus().length());
}
if (result.startsWith("+")) {
result = result.substring(1);
}
if (!original.equals(result)) {
phoneNumberView.setText(result);
}
}
});
return view;
}
}
感谢@Dorukhan Arslan和@NixSam的回答。被接受的答案运行良好,但当用户更改中间某个数字时,问题就会出现。另一个答案在这方面很有帮助,但对于某些边缘情况,它的行为并不是我想要的。所以我想用另一种方法来解决它。此解决方案使用“digitsBeforeCursor”每次[希望:-])都保持正确的光标位置。
对于所有面临这个问题的人来说,有两种方法可以解决这个问题。
如果你想从零开始实现,那就来吧。
dependencies {
compile 'io.michaelrocks:libphonenumber-android:8.9.0'
}
InternationalPhoneTextWatcher
向该类添加以下代码。CCP在这里使用这个类。然后使用这个类的对象到editText。这将在构造器中使用国家名称代码和电话代码。当调用updateCountry()来更改国家时,将自动更新格式。
public class InternationalPhoneTextWatcher implements TextWatcher {
// Reference https://stackoverflow.com/questions/32661363/using-phonenumberformattingtextwatcher-without-typing-country-calling-code to solve formatting issue
// Check parent project of this class at https://github.com/hbb20/CountryCodePickerProject
private static final String TAG = "Int'l Phone TextWatcher";
PhoneNumberUtil phoneNumberUtil;
/**
* Indicates the change was caused by ourselves.
*/
private boolean mSelfChange = false;
/**
* Indicates the formatting has been stopped.
*/
private boolean mStopFormatting;
private AsYouTypeFormatter mFormatter;
private String countryNameCode;
Editable lastFormatted = null;
private int countryPhoneCode;
//when country is changed, we update the number.
//at this point this will avoid "stopFormatting"
private boolean needUpdateForCountryChange = false;
/**
* @param context
* @param countryNameCode ISO 3166-1 two-letter country code that indicates the country/region
* where the phone number is being entered.
* @param countryPhoneCode Phone code of country. https://countrycode.org/
*/
public InternationalPhoneTextWatcher(Context context, String countryNameCode, int countryPhoneCode) {
if (countryNameCode == null || countryNameCode.length() == 0)
throw new IllegalArgumentException();
phoneNumberUtil = PhoneNumberUtil.createInstance(context);
updateCountry(countryNameCode, countryPhoneCode);
}
public void updateCountry(String countryNameCode, int countryPhoneCode) {
this.countryNameCode = countryNameCode;
this.countryPhoneCode = countryPhoneCode;
mFormatter = phoneNumberUtil.getAsYouTypeFormatter(countryNameCode);
mFormatter.clear();
if (lastFormatted != null) {
needUpdateForCountryChange = true;
String onlyDigits = phoneNumberUtil.normalizeDigitsOnly(lastFormatted);
lastFormatted.replace(0, lastFormatted.length(), onlyDigits, 0, onlyDigits.length());
needUpdateForCountryChange = false;
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
if (mSelfChange || mStopFormatting) {
return;
}
// If the user manually deleted any non-dialable characters, stop formatting
if (count > 0 && hasSeparator(s, start, count) && !needUpdateForCountryChange) {
stopFormatting();
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mSelfChange || mStopFormatting) {
return;
}
// If the user inserted any non-dialable characters, stop formatting
if (count > 0 && hasSeparator(s, start, count)) {
stopFormatting();
}
}
@Override
public synchronized void afterTextChanged(Editable s) {
if (mStopFormatting) {
// Restart the formatting when all texts were clear.
mStopFormatting = !(s.length() == 0);
return;
}
if (mSelfChange) {
// Ignore the change caused by s.replace().
return;
}
//calculate few things that will be helpful later
int selectionEnd = Selection.getSelectionEnd(s);
boolean isCursorAtEnd = (selectionEnd == s.length());
//get formatted text for this number
String formatted = reformat(s);
//now calculate cursor position in formatted text
int finalCursorPosition = 0;
if (formatted.equals(s.toString())) {
//means there is no change while formatting don't move cursor
finalCursorPosition = selectionEnd;
} else if (isCursorAtEnd) {
//if cursor was already at the end, put it at the end.
finalCursorPosition = formatted.length();
} else {
// if no earlier case matched, we will use "digitBeforeCursor" way to figure out the cursor position
int digitsBeforeCursor = 0;
for (int i = 0; i < s.length(); i++) {
if (i >= selectionEnd) {
break;
}
if (PhoneNumberUtils.isNonSeparator(s.charAt(i))) {
digitsBeforeCursor++;
}
}
//at this point we will have digitsBeforeCursor calculated.
// now find this position in formatted text
for (int i = 0, digitPassed = 0; i < formatted.length(); i++) {
if (digitPassed == digitsBeforeCursor) {
finalCursorPosition = i;
break;
}
if (PhoneNumberUtils.isNonSeparator(formatted.charAt(i))) {
digitPassed++;
}
}
}
//if this ends right before separator, we might wish to move it further so user do not delete separator by mistake.
// because deletion of separator will cause stop formatting that should not happen by mistake
if (!isCursorAtEnd) {
while (0 < finalCursorPosition - 1 && !PhoneNumberUtils.isNonSeparator(formatted.charAt(finalCursorPosition - 1))) {
finalCursorPosition--;
}
}
//Now we have everything calculated, set this values in
if (formatted != null) {
mSelfChange = true;
s.replace(0, s.length(), formatted, 0, formatted.length());
mSelfChange = false;
lastFormatted = s;
Selection.setSelection(s, finalCursorPosition);
}
}
/**
* this will format the number in international format (only).
*/
private String reformat(CharSequence s) {
String internationalFormatted = "";
mFormatter.clear();
char lastNonSeparator = 0;
String countryCallingCode = "+" + countryPhoneCode;
//to have number formatted as international format, add country code before that
s = countryCallingCode + s;
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (PhoneNumberUtils.isNonSeparator(c)) {
if (lastNonSeparator != 0) {
internationalFormatted = mFormatter.inputDigit(lastNonSeparator);
}
lastNonSeparator = c;
}
}
if (lastNonSeparator != 0) {
internationalFormatted = mFormatter.inputDigit(lastNonSeparator);
}
internationalFormatted = internationalFormatted.trim();
if (internationalFormatted.length() > countryCallingCode.length()) {
if (internationalFormatted.charAt(countryCallingCode.length()) == ' ')
internationalFormatted = internationalFormatted.substring(countryCallingCode.length() + 1);
else
internationalFormatted = internationalFormatted.substring(countryCallingCode.length());
} else {
internationalFormatted = "";
}
return TextUtils.isEmpty(internationalFormatted) ? "" : internationalFormatted;
}
private void stopFormatting() {
mStopFormatting = true;
mFormatter.clear();
}
private boolean hasSeparator(final CharSequence s, final int start, final int count) {
for (int i = start; i < start + count; i++) {
char c = s.charAt(i);
if (!PhoneNumberUtils.isNonSeparator(c)) {
return true;
}
}
return false;
}
}
我编辑了reformat(charSequence,cursor)
方法,最终实现了无国家电话号码的国际格式化电话号码。如果要获得相同的结果,可以看到下面编辑的代码:
/**
* Watches a {@link android.widget.TextView} and if a phone number is entered
* will format it.
* <p>
* Stop formatting when the user
* <ul>
* <li>Inputs non-dialable characters</li>
* <li>Removes the separator in the middle of string.</li>
* </ul>
* <p>
* The formatting will be restarted once the text is cleared.
*/
public class PhoneNumberFormattingTextWatcher implements TextWatcher {
/**
* Indicates the change was caused by ourselves.
*/
private boolean mSelfChange = false;
/**
* Indicates the formatting has been stopped.
*/
private boolean mStopFormatting;
private AsYouTypeFormatter mFormatter;
private String countryCode;
/**
* The formatting is based on the current system locale and future locale changes
* may not take effect on this instance.
*/
public PhoneNumberFormattingTextWatcher() {
this(Locale.getDefault().getCountry());
}
/**
* The formatting is based on the given <code>countryCode</code>.
*
* @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
* where the phone number is being entered.
*
* @hide
*/
public PhoneNumberFormattingTextWatcher(String countryCode) {
if (countryCode == null) throw new IllegalArgumentException();
mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
this.countryCode = countryCode;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
if (mSelfChange || mStopFormatting) {
return;
}
// If the user manually deleted any non-dialable characters, stop formatting
if (count > 0 && hasSeparator(s, start, count)) {
stopFormatting();
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mSelfChange || mStopFormatting) {
return;
}
// If the user inserted any non-dialable characters, stop formatting
if (count > 0 && hasSeparator(s, start, count)) {
stopFormatting();
}
}
@Override
public synchronized void afterTextChanged(Editable s) {
if (mStopFormatting) {
// Restart the formatting when all texts were clear.
mStopFormatting = !(s.length() == 0);
return;
}
if (mSelfChange) {
// Ignore the change caused by s.replace().
return;
}
String formatted = reformat(s, Selection.getSelectionEnd(s));
if (formatted != null) {
int rememberedPos = formatted.length();
Log.v("rememberedPos", "" + rememberedPos);
mSelfChange = true;
s.replace(0, s.length(), formatted, 0, formatted.length());
// The text could be changed by other TextWatcher after we changed it. If we found the
// text is not the one we were expecting, just give up calling setSelection().
if (formatted.equals(s.toString())) {
Selection.setSelection(s, rememberedPos);
}
mSelfChange = false;
}
}
/**
* Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the
* nearest dialable char to the left. For instance, if the number is (650) 123-45678 and '4' is
* removed then the cursor should be behind '3' instead of '-'.
*/
private String reformat(CharSequence s, int cursor) {
// The index of char to the leftward of the cursor.
int curIndex = cursor - 1;
String formatted = null;
mFormatter.clear();
char lastNonSeparator = 0;
boolean hasCursor = false;
String countryCallingCode = "+" + CountryCodesAdapter.getCode(countryCode);
s = countryCallingCode + s;
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (PhoneNumberUtils.isNonSeparator(c)) {
if (lastNonSeparator != 0) {
formatted = getFormattedNumber(lastNonSeparator, hasCursor);
hasCursor = false;
}
lastNonSeparator = c;
}
if (i == curIndex) {
hasCursor = true;
}
}
if (lastNonSeparator != 0) {
Log.v("lastNonSeparator", "" + lastNonSeparator);
formatted = getFormattedNumber(lastNonSeparator, hasCursor);
}
if (formatted.length() > countryCallingCode.length()) {
if (formatted.charAt(countryCallingCode.length()) == ' ')
return formatted.substring(countryCallingCode.length() + 1);
return formatted.substring(countryCallingCode.length());
}
return formatted.substring(formatted.length());
}
private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator)
: mFormatter.inputDigit(lastNonSeparator);
}
private void stopFormatting() {
mStopFormatting = true;
mFormatter.clear();
}
private boolean hasSeparator(final CharSequence s, final int start, final int count) {
for (int i = start; i < start + count; i++) {
char c = s.charAt(i);
if (!PhoneNumberUtils.isNonSeparator(c)) {
return true;
}
}
return false;
}
}
这里是Twilio的新手,并寻求twilio专家的建议。我正在尝试拨打IVR系统的出站电话,等待几秒钟并发送键盘号码输入以在另一端启动工作流程 到目前为止,我可以呼叫IVR系统,但我如何发送号码输入?我认为TwiML是实现这一点的方法!非常感谢您的帮助
问题内容: 我已经找到了针对Objective-C的答案,但是林先生很难迅速做到这一点。 我用它来获取当前位置的国家代码: 但是,如何将该国家/地区代码转换为国家/地区名称,例如在此示例中,将“ US”转换为“ United States”? 问题答案: 迅捷3
问题内容: 如果用户查看我的JavaScript文件,复制函数的内容并使用AJAX向我的服务器发送请求,会发生什么情况?有没有办法适当地防止这种情况的发生? 问题答案: 防止这种情况发生的方法与针对 任何 Web请求采取的保护方法没有什么不同。您这样做是为了使您的站点需要某种形式的身份验证(即用户必须登录),并且如果请求未正确身份验证,则不要执行任何操作。 通常,当您发出AJAX请求时,cooki
问题内容: 我继承了一个非常古老的JSP应用程序(JDK 1.3.1_15),并试图插入会话固定漏洞。 在使用身份验证后,我成功使当前会话无效,但是在创建新会话时,将重新使用旧会话ID。 我可以在HTTP监视器中看到新的会话分配,只是再次使用了相同的数字。 在我之前,根本没有第二个响应头。 有人对如何生成新的会话ID有任何建议吗?我对JRUN4不太熟悉,但是在配置文档中拖拉并没有发现任何东西。 问
当我使用jsonObject请求进行网络调用时。我没有收到任何答复。我总是收到请求的HTTP响应= 当请求缓慢时,Android截击双发: 来源1,来源2,来源3
我在调用我的onLeScan时遇到问题。我在开始扫描中放置了一个标签,每次都会被调用。出于某种原因,我的onLeScan永远不会被调用。有人看到我所做的有问题吗?onLeScan应该在开始扫描后立即调用,对吗? 编辑更改了我的onLeScan函数。仍然不起作用,但我认为我正在走向正确的道路。DeviceBeacon是一个只包含方法的类:getName()、getSignal()和getAddres