TextWatcher slow when using setText - android

I'm using TextWatcher to modify the EditText to always look like #tag1 #tag2...
Here is my code:
editHashtags.addTextChangedListener(new TextWatcher() {
private boolean lock = false;
private String toGo;
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(before == 1) return;
toGo = s.toString();
if(toGo.charAt(0) != '#') {
toGo = '#' + toGo;
}
if(toGo.charAt(start) == ' ' && toGo.charAt(start - 1) == '#' && start > 0
|| toGo.charAt(start) == ' ' && toGo.charAt(start - 1) == ' ' && start > 0) {
toGo = toGo.substring(0, toGo.length() - 1);
} else if(toGo.charAt(start) == ' ') {
toGo += '#';
} else if(toGo.charAt(start) != '#' && toGo.charAt(start - 1) == ' ') {
toGo = toGo.substring(0, toGo.length() - 1) + '#' + toGo.charAt(start);
}
}
#Override
public void afterTextChanged(Editable s) {
if(lock) return;
toGo = toGo.replaceAll("#+", "#");
lock = true;
editHashtags.setText(toGo);
editHashtags.setSelection(toGo.length());
lock = false;
}
});
The problem is when I use lock to change the text. It appears that setText is really slow, so if I type fast, some characters are ignored.
What is the best approach to solve this issue?
Thank you.

The right way to modify a EditText using TextWatcher is modifying the mutable object Editable received on the afterTextChanged method.

You could also look at implementing a custom TransformationMethod instead, which might give you better performance. An implementation similar to the framework's ReplacementTransformationMethod, for instance.

Related

Get Emoji Count In String

I would like to find how many emojis the user has input into an EditText. If the user only enters emojis, and uses 3 or less, I want to be able to display that string within the app with a larger font.
Right now I did come across this post which does help detect if emojis are present in the string, but I have not been able to figure out how to count the number of emojis.
Detecting if a character in a String is an emoticon (using Android)
Does anyone know how I can get the emoji count from a String?
Another approach would be to take advantage of EmojiCompat. This code presumes you initialized EmojiCompat when your app was starting up. The basic idea here is to have EmojiCompat process your CharSequence, inserting instances of EmojiSpan wherever any emoji appear, and then examine the results, returning a count of the EmojiSpan instances in the processed Spannable.
public static int getEmojiCount(CharSequence charSequence) {
int count = 0;
CharSequence processed = EmojiCompat.get().process(charSequence, 0, charSequence.length() -1, Integer.MAX_VALUE, EmojiCompat.REPLACE_STRATEGY_ALL);
if (processed instanceof Spannable) {
Spannable spannable = (Spannable) processed;
count = spannable.getSpans(0, spannable.length() - 1, EmojiSpan.class).length;
}
return count;
}
Do not forget to add dependency in app gradle:
implementation 'androidx.emoji:emoji:1.1.0'
int emojiCount = 0;
for (int i = 0; i < yourString.length(); i++) {
int type = Character.getType(yourString.charAt(i));
if (type == Character.SURROGATE || type == Character.OTHER_SYMBOL) {
emojiCount++;
}
}
return emojiCount/2;
My approach to this was to import this library:
implementation 'com.vdurmont:emoji-java:4.0.0'
Then I created a utility method to get the length of a string counting emojis as 1:
fun getLengthWithEmoji(s: String): Int{
var emojiCount = EmojiParser.extractEmojis(s).size;
var noEmojiString = EmojiParser.removeAllEmojis(s);
var emojiAndStringCount = emojiCount + noEmojiString.length;
return emojiAndStringCount;
}
Generally to 'Get emoji count in string' I would use this line:
var emojiCount = EmojiParser.extractEmojis(s).size;
This accounts for all the latest emojis (depending on how up to date your library it). Check for some of the forks that others have made on the library as they in some cases have added missing emoji patterns.
try this
private TextWatcher textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
editText.post(new Runnable() {
#Override
public void run() {
if (length < 100) {
if (count > 0 && after <= 0)/*remove emoij*/ {
Log.i("MainActivity", "emoij -> down length");
length--;
} else if (count > after)/*remove text*/ {
Log.i("MainActivity", "text -> down length");
length--;
} else if (count == 0 && after > 1)/*emoij*/ {
Log.i("MainActivity", "emoij -> increase");
++length;
} else if (count == 0 && after == 1)/*Text*/ {
Log.i("MainActivity", "text -> increase");
++length;
} else if (count > 0 && after > 1) {
Log.i("MainActivity", "text -> increase");
++length;
}
if (s.length() <= 0)
length = 0;
Log.w("MainActivity", " Length: " + length);
} else {
if (count > 0 && after <= 0)/*remove emoij*/ {
Log.i("MainActivity", "emoij -> down length");
length--;
} else if (count > after)/*remove text*/ {
Log.i("MainActivity", "text -> down length");
length--;
}
Log.w("MainActivity", " Length: " + length);
}
if (length == 100) {
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(s.length())});
} else {
editText.setFilters(new InputFilter[]{});
}
}
});
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
}
};
`
The best way for me was codePointCount method
For example this method returns 1 if text value is "🐓":
fun getLengthWithEmoji(name: String): Int {
return name.codePointCount(0, name.length)
}

After entering 2 numbers insert a colon(:) in android

edtTxt.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
if(s.length() != 0 && s.length() == 2){
String str = s.toString();
str.replaceAll("..(?!$)", "$0:");
edtTxt.setText(str);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
}
});
I need to display ":" after 2nd digit that is for example 10:25, maximum length is 5 digits it is edittext.
If i started typing in the edittext 10 after this ":" should be inserted then 10:25 should be displayed in the edittext.
I tried with the above logic not working. can anyone help me. Thanks in advance
After replaceAll you should assign the value to same variable. Its working fine..
public void afterTextChanged(Editable s) {
if(s.length() != 0 && s.length() == 3){
String str = s.toString();
str = str.replaceAll("..(?!$)", "$0:");
edtTxt.setText(str);
edtTxt.setSelection(edtTxt.getText().length()); //cursor at last position
}
}
First of all you ignore the result of str.replaceAll(). The method returns a String.
The if condition can be simplified to s.length() == 2.
And the regex you are using doesn't work.
This will add colon in the EditText after you have entered 2 characters
if (s.length() == 2) {
edtTxt.setText(s.toString() + ":");
}
Kotlin and improved version of the #sasikumar's solution:
private fun formatInput(clock: Editable?) {
if (clock.toString().isNotEmpty()
&& clock.toString().contains(":").not()
&& clock.toString().length == 3
) {
var str: String = clock.toString()
str = str.replace("..(?!$)".toRegex(), "$0:")
etClock.setText(str)
etClock.setSelection(etClock.text.length)
}
}
etClock.addTextChangedListener(
afterTextChanged = {
formatInput(it)
}
)
etClock.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
etClock.setSelection(etClock.text.length)
}
}
etClock.setOnClickListener {
etClock.setSelection(etClock.text.length)
}
And a great explanation of the used Regex: https://stackoverflow.com/a/23404646/421467
Just do it like this
editTextTime.addTextChangedListener {
if(it?.length == 3 && !it.contains(":")){
it.insert(2,":")
}
}
I think the code is clear
if you put 3 number it will add ":" before the 3rd number
and it will check if your 3rd Char it not already :
then it will insert ":" for you

How to show dropdown only when inserting # character on MultiAutoCompleteTextView in android

I have a MultiAutoCompleteTextView. It works fine. But I want to show suggestion dropdown only when user type # on it (like tagging user in facebook app). I have no idea how to do it. Here is my code :
mChatbox = (MultiAutoCompleteTextView) findViewById(R.id.chatbox);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line, userList);
mChatBox.setAdapter(adapter);
mChatBox.setTokenizer(new SpaceTokenizer());
public class SpaceTokenizer implements MultiAutoCompleteTextView.Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}
Create a custom Text view extending MultiAutoCompleteTextView -> override enoughToFilter() -> set the threshold to 0 (the bold variable in the below given code) :
public boolean enoughToFilter() {
Editable text = getText();
int end = getSelectionEnd();
if (end < 0 || mTokenizer == null) {
return false;
}
int start = mTokenizer.findTokenStart(text, end);
if (end - start >= mThreshold && start != -1) {
return true;
} else {
return false;
}
}
Using this code you'll see the auto suggested list on press of #
If you want to detect your string starts with '#' for mention (tag) someone or '#' for hashTag, then do query or filter with it, you could follow this code belows:
#Override
public void onTextChanged(CharSequence s, int start, int before, final int count) {
if (s.length() > 0) {
// Todo: query mentions
Matcher mentionMatcher = Pattern.compile("#([A-Za-z0-9_-]+)").matcher(s.toString());
// while matching
while (mentionMatcher.find()) {
yourSearchText = s.toString().substring(mentionMatcher.start() + 1, mentionMatcher.end());
// do query with yourSearchText below
}
}
}
It references from the link Multiautocompletetextview, Show autocomplete drop down only when user presses a key after '#' key (like mention in FB app) please scroll down to find #Phuong Sala answer.
I got a solution by myself. I create custom view which extends MultiAutoCompleteTextView and override performFiltering in it. Check if first char is "#", then filter the next chars after it. Otherwise, replace chars with "*" to avoid filtering. Here is my code.
#Override
protected void performFiltering(CharSequence text, int start, int end, int keyCode) {
if (text.charAt(start) == '#') {
start = start + 1;
} else {
text = text.subSequence(0, start);
for (int i = start; i < end; i++) {
text = text + "*";
}
}
super.performFiltering(text, start, end, keyCode);
}

Textwatcher - Different Last Char Situations

Long story short I am facing this problem: I am attaching a textwatcher to an edittext. As soon as "1" is the last char written on it, that should be replaced with the letter "a". But here is the problem: I'd like as soon as "a" is also the last char written on the edittext (user pressed "a"), EXCEPT THROUGH THE PREVIOUS METHOD, some things to be done. But as I test it and type "1", that is converted to "a" normally and the things I mention are also done. I can't seem to find a way to overpass this, can any suggestions be given? Thanks a lot. I use:
public void afterTextChanged(Editable s) {
//TODO Auto-generated method stub
if (s.length() > 0 && s.toString().charAt(s.length() - 1) == '1') {
current_string = s.toString().substring(0, (s.length() - 1));
et.setText(current_string + "a");
length = s.length();
et.setSelection(length);
}
else if (s.length() > 0 && s.toString().charAt(s.length() - 1) == 'a') {
//do some things
}
try this
public void afterTextChanged(Editable s) {
if (s.length() > 0 && s.toString().charAt(s.length() - 1) == 'a') {
//do some things
}
if (s.length() > 0 && s.toString().charAt(s.length() - 1) == '1') {
current_string = s.toString().substring(0, (s.length() - 1));
et.setText(current_string + "a");
length = s.length();
et.setSelection(length);
}
Do you mean.
1)when user enters the 1 in end it should replace by 'a'.
2)when user enters 'a' separately (not converted by the step 1) it should do something else
amount1.addTextChangedListener(new TextWatcher() {
int flagg;
public void onTextChanged(CharSequence s, int start, int before,
int count) {
System.out.println("flagg"+s+"#"+flagg );
if (s.length() > 0 && s.toString().charAt(s.length() - 1) == '1' )
{ System.out.println("Converting and doing NOthing");
flagg=1;
String current_string = s.toString().substring(0,(s.length() - 1));
amount1.setText(current_string + "a");
int length = s.length();
amount1.setSelection(length);
}
else if (s.length() > 0 && ( s.toString().charAt(s.length() - 1) == 'a') && flagg == 0 ) {
System.out.println("Staying same and doing Something");
amount2.setText(amount2.getText().toString() + "Z");
// do some things
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
if (start != 0) {
if ((s.toString()).substring(s.length() - 1).equals("a")) {
flagg = 1;
}
if ((s.toString()).substring(s.length() - 1).equals("1")) {
flagg = 0;
} else {
flagg = 0;
}
}
}
#Override
public void afterTextChanged(Editable arg0) {
}
});
//new flagg is set in if condition

Phone number formatting an EditText in Android

I am making a simple Address Book app (targeting 4.2) that takes name, address, city, state, zip and phone.
I want to format the phone number input as a phone number (XXX) XXX-XXXX, but I need to pull the value out as a string so I can store it in my database when I save. How can i do this??
I have the EditText set for "phone number" input but that obviously doesn't do too much.
Simply use the PhoneNumberFormattingTextWatcher, just call:
editText.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
Addition
To be clear, PhoneNumberFormattingTextWatcher's backbone is the PhoneNumberUtils class. The difference is the TextWatcher maintains the EditText while you must call PhoneNumberUtils.formatNumber() every time you change its contents.
There is a library called PhoneNumberUtils that can help you to cope with phone number conversions and comparisons. For instance, use ...
EditText text = (EditText) findViewById(R.id.editTextId);
PhoneNumberUtils.formatNumber(text.getText().toString())
... to format your number in a standard format.
PhoneNumberUtils.compare(String a, String b);
... helps with fuzzy comparisons. There are lots more. Check out http://developer.android.com/reference/android/telephony/PhoneNumberUtils.html for more.
p.s. setting the the EditText to phone is already a good choice; eventually it might be helpful to add digits e.g. in your layout it looks as ...
<EditText
android:id="#+id/editTextId"
android:inputType="phone"
android:digits="0123456789+"
/>
Simply Use This :
In Java Code :
editText.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
In XML Code :
<EditText
android:id="#+id/etPhoneNumber"
android:inputType="phone"/>
This code work for me. It'll auto format when text changed in edit text.
I've recently done a similar formatting like 1 (XXX) XXX-XXXX for Android EditText. Please find the code below. Just use the TextWatcher sub-class as the text changed listener :
....
UsPhoneNumberFormatter addLineNumberFormatter = new UsPhoneNumberFormatter(
new WeakReference<EditText>(mYourEditText));
mYourEditText.addTextChangedListener(addLineNumberFormatter);
...
private class UsPhoneNumberFormatter implements TextWatcher {
//This TextWatcher sub-class formats entered numbers as 1 (123) 456-7890
private boolean mFormatting; // this is a flag which prevents the
// stack(onTextChanged)
private boolean clearFlag;
private int mLastStartLocation;
private String mLastBeforeText;
private WeakReference<EditText> mWeakEditText;
public UsPhoneNumberFormatter(WeakReference<EditText> weakEditText) {
this.mWeakEditText = weakEditText;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
if (after == 0 && s.toString().equals("1 ")) {
clearFlag = true;
}
mLastStartLocation = start;
mLastBeforeText = s.toString();
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// TODO: Do nothing
}
#Override
public void afterTextChanged(Editable s) {
// Make sure to ignore calls to afterTextChanged caused by the work
// done below
if (!mFormatting) {
mFormatting = true;
int curPos = mLastStartLocation;
String beforeValue = mLastBeforeText;
String currentValue = s.toString();
String formattedValue = formatUsNumber(s);
if (currentValue.length() > beforeValue.length()) {
int setCusorPos = formattedValue.length()
- (beforeValue.length() - curPos);
mWeakEditText.get().setSelection(setCusorPos < 0 ? 0 : setCusorPos);
} else {
int setCusorPos = formattedValue.length()
- (currentValue.length() - curPos);
if(setCusorPos > 0 && !Character.isDigit(formattedValue.charAt(setCusorPos -1))){
setCusorPos--;
}
mWeakEditText.get().setSelection(setCusorPos < 0 ? 0 : setCusorPos);
}
mFormatting = false;
}
}
private String formatUsNumber(Editable text) {
StringBuilder formattedString = new StringBuilder();
// Remove everything except digits
int p = 0;
while (p < text.length()) {
char ch = text.charAt(p);
if (!Character.isDigit(ch)) {
text.delete(p, p + 1);
} else {
p++;
}
}
// Now only digits are remaining
String allDigitString = text.toString();
int totalDigitCount = allDigitString.length();
if (totalDigitCount == 0
|| (totalDigitCount > 10 && !allDigitString.startsWith("1"))
|| totalDigitCount > 11) {
// May be the total length of input length is greater than the
// expected value so we'll remove all formatting
text.clear();
text.append(allDigitString);
return allDigitString;
}
int alreadyPlacedDigitCount = 0;
// Only '1' is remaining and user pressed backspace and so we clear
// the edit text.
if (allDigitString.equals("1") && clearFlag) {
text.clear();
clearFlag = false;
return "";
}
if (allDigitString.startsWith("1")) {
formattedString.append("1 ");
alreadyPlacedDigitCount++;
}
// The first 3 numbers beyond '1' must be enclosed in brackets "()"
if (totalDigitCount - alreadyPlacedDigitCount > 3) {
formattedString.append("("
+ allDigitString.substring(alreadyPlacedDigitCount,
alreadyPlacedDigitCount + 3) + ") ");
alreadyPlacedDigitCount += 3;
}
// There must be a '-' inserted after the next 3 numbers
if (totalDigitCount - alreadyPlacedDigitCount > 3) {
formattedString.append(allDigitString.substring(
alreadyPlacedDigitCount, alreadyPlacedDigitCount + 3)
+ "-");
alreadyPlacedDigitCount += 3;
}
// All the required formatting is done so we'll just copy the
// remaining digits.
if (totalDigitCount > alreadyPlacedDigitCount) {
formattedString.append(allDigitString
.substring(alreadyPlacedDigitCount));
}
text.clear();
text.append(formattedString.toString());
return formattedString.toString();
}
}
Maybe below sample project helps you;
https://github.com/reinaldoarrosi/MaskedEditText
That project contains a view class call MaskedEditText. As first, you should add it in your project.
Then you add below xml part in res/values/attrs.xml file of project;
<resources>
<declare-styleable name="MaskedEditText">
<attr name="mask" format="string" />
<attr name="placeholder" format="string" />
</declare-styleable>
</resources>
Then you will be ready to use MaskedEditText view.
As last, you should add MaskedEditText in your xml file what you want like below;
<packagename.currentfolder.MaskedEditText
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/maskedEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:text="5"
app:mask="(999) 999-9999"
app:placeholder="_" >
Of course that, you can use it programmatically.
After those steps, adding MaskedEditText will appear like below;
As programmatically, if you want to take it's text value as unmasked, you may use below row;
maskedEditText.getText(true);
To take masked value, you may send false value instead of true value in the getText method.
You need to create a class:
public class PhoneTextFormatter implements TextWatcher {
private final String TAG = this.getClass().getSimpleName();
private EditText mEditText;
private String mPattern;
public PhoneTextFormatter(EditText editText, String pattern) {
mEditText = editText;
mPattern = pattern;
//set max length of string
int maxLength = pattern.length();
mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
StringBuilder phone = new StringBuilder(s);
Log.d(TAG, "join");
if (count > 0 && !isValid(phone.toString())) {
for (int i = 0; i < phone.length(); i++) {
Log.d(TAG, String.format("%s", phone));
char c = mPattern.charAt(i);
if ((c != '#') && (c != phone.charAt(i))) {
phone.insert(i, c);
}
}
mEditText.setText(phone);
mEditText.setSelection(mEditText.getText().length());
}
}
#Override
public void afterTextChanged(Editable s) {
}
private boolean isValid(String phone)
{
for (int i = 0; i < phone.length(); i++) {
char c = mPattern.charAt(i);
if (c == '#') continue;
if (c != phone.charAt(i)) {
return false;
}
}
return true;
}
}
Use this as follows:
phone = view.findViewById(R.id.phone);
phone.addTextChangedListener(new PhoneTextFormatter(phone, "+7 (###) ###-####"));
If you're only interested in international numbers and you'd like to be able to show the flag of the country that matches the country code in the input, I wrote a small library for that:
https://github.com/tfcporciuncula/phonemoji
Here's how it looks:
Follow the instructions in this Answer to format the EditText mask.
https://stackoverflow.com/a/34907607/1013929
And after that, you can catch the original numbers from the masked string with:
String phoneNumbers = maskedString.replaceAll("[^\\d]", "");
//(123) 456 7890 formate set
private int textlength = 0;
public class MyPhoneTextWatcher implements TextWatcher {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
String text = etMobile.getText().toString();
textlength = etMobile.getText().length();
if (text.endsWith(" "))
return;
if (textlength == 1) {
if (!text.contains("(")) {
etMobile.setText(new StringBuilder(text).insert(text.length() - 1, "(").toString());
etMobile.setSelection(etMobile.getText().length());
}
} else if (textlength == 5) {
if (!text.contains(")")) {
etMobile.setText(new StringBuilder(text).insert(text.length() - 1, ")").toString());
etMobile.setSelection(etMobile.getText().length());
}
} else if (textlength == 6 || textlength == 10) {
etMobile.setText(new StringBuilder(text).insert(text.length() - 1, " ").toString());
etMobile.setSelection(etMobile.getText().length());
}
}
#Override
public void afterTextChanged(Editable editable) {
}
}
More like clean:
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String text = etyEditText.getText();
int textlength = etyEditText.getText().length();
if (text.endsWith("(") ||text.endsWith(")")|| text.endsWith(" ") || text.endsWith("-") )
return;
switch (textlength){
case 1:
etyEditText.setEditText(new StringBuilder(text).insert(text.length() - 1, "(").toString());
etyEditText.setSelection(etyEditText.getText().length());
break;
case 5:
etyEditText.setEditText(new StringBuilder(text).insert(text.length() - 1, ")").toString());
etyEditText.setSelection(etyEditText.getText().length());
break;
case 6:
etyEditText.setEditText(new StringBuilder(text).insert(text.length() - 1, " ").toString());
etyEditText.setSelection(etyEditText.getText().length());
break;
case 10:
etyEditText.setEditText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
etyEditText.setSelection(etyEditText.getText().length());
break;
}
}
You can use spawns to format phone numbers in Android. This solution is better than the others because it does not change input text. Formatting remains purely visual.
implementation 'com.googlecode.libphonenumber:libphonenumber:7.0.4'
Formatter class:
open class PhoneNumberFormatter : TransformationMethod {
private val mFormatter: AsYouTypeFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(Locale.getDefault().country)
override fun getTransformation(source: CharSequence, view: View): CharSequence {
val formatted = format(source)
if (source is Spannable) {
setSpans(source, formatted)
return source
}
return formatted
}
override fun onFocusChanged(view: View?, sourceText: CharSequence?, focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) = Unit
private fun setSpans(spannable: Spannable, formatted: CharSequence): CharSequence {
spannable.clearSpawns()
var charterIndex = 0
var formattedIndex = 0
var spawn = ""
val spawns: List<String> = spannable
.map {
spawn = ""
charterIndex = formatted.indexOf(it, formattedIndex)
if (charterIndex != -1){
spawn = formatted.substring(formattedIndex, charterIndex-1)
formattedIndex = charterIndex+1
}
spawn
}
spawns.forEachIndexed { index, sequence ->
spannable.setSpan(CharterSpan(sequence), index, index + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
return formatted
}
private fun Spannable.clearSpawns() =
this
.getSpans(0, this.length, CharterSpan::class.java)
.forEach { this.removeSpan(it) }
private fun format(spannable: CharSequence): String {
mFormatter.clear()
var formated = ""
for (i in 0 until spannable.length) {
formated = mFormatter.inputDigit(spannable[i])
}
return formated
}
private inner class CharterSpan(private val charters: String) : ReplacementSpan() {
var space = 0
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
space = Math.round(paint.measureText(charters, 0, charters.length))
return Math.round(paint.measureText(text, start, end)) + space
}
override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
space = Math.round(paint.measureText(charters, 0, charters.length))
canvas.drawText(text, start, end, x + space, y.toFloat(), paint)
canvas.drawText(charters, x, y.toFloat(), paint)
}
}
}
Uasge:
editText.transformationMethod = formatter
You can use a Regular Expression with pattern matching to extract number from a string.
String s="";
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("(1111)123-456-789"); //editText.getText().toString()
while (m.find()) {
s=s+m.group(0);
}
System.out.println("............"+s);
Output : ............1111123456789
Don't worry. I have make a most of better solution for you. You can see this simple app link below.
private EditText mPasswordField;
public int textLength = 0;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPasswordField = (EditText) findViewById(R.id.password_field);
mPasswordField.addTextChangedListener(this);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String text = mPasswordField.getText().toString();
textLength = mPasswordField.getText().length();
if (text.endsWith("-") || text.endsWith(" ") || text.endsWith(" "))
return;
if (textLength == 1) {
if (!text.contains("(")) {
mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, "(").toString());
mPasswordField.setSelection(mPasswordField.getText().length());
}
} else if (textLength == 5) {
if (!text.contains(")")) {
mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, ")").toString());
mPasswordField.setSelection(mPasswordField.getText().length());
}
} else if (textLength == 6) {
mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, " ").toString());
mPasswordField.setSelection(mPasswordField.getText().length());
} else if (textLength == 10) {
if (!text.contains("-")) {
mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
mPasswordField.setSelection(mPasswordField.getText().length());
}
} else if (textLength == 15) {
if (text.contains("-")) {
mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
mPasswordField.setSelection(mPasswordField.getText().length());
}
}else if (textLength == 18) {
if (text.contains("-")) {
mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
mPasswordField.setSelection(mPasswordField.getText().length());
}
} else if (textLength == 20) {
Intent i = new Intent(MainActivity.this, Activity2.class);
startActivity(i);
}
}
#Override
public void afterTextChanged(Editable s) {
}
Not: Don't forget "implement TextWatcher" with your activity class.
Link :https://drive.google.com/open?id=0B-yo9VvU7jyBMjJpT29xc2k5bnc
Hope you are feeling cool for this solution.
You can accept only numbers and phone number type using java code
EditText number1 = (EditText) layout.findViewById(R.id.edittext);
number1.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_CLASS_PHONE);
number1.setKeyListener(DigitsKeyListener.getInstance("0123456789”));
number1.setFilters(new InputFilter[] {new InputFilter.LengthFilter(14)}); // 14 is max digits
This code will avoid lot of validations after reading input
This code is work for me for (216) 555-5555
etphonenumber.addTextChangedListener(new TextWatcher()
{
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s)
{
String text = etphonenumber.getText().toString();
int textLength = etphonenumber.getText().length();
if (text.endsWith("-") || text.endsWith(" ") || text.endsWith(" "))
return;
if (textLength == 1) {
if (!text.contains("("))
{
etphonenumber.setText(new StringBuilder(text).insert(text.length() - 1, "(").toString());
etphonenumber.setSelection(etphonenumber.getText().length());
}
}
else if (textLength == 5)
{
if (!text.contains(")"))
{
etphonenumber.setText(new StringBuilder(text).insert(text.length() - 1, ")").toString());
etphonenumber.setSelection(etphonenumber.getText().length());
}
}
else if (textLength == 6)
{
etphonenumber.setText(new StringBuilder(text).insert(text.length() - 1, " ").toString());
etphonenumber.setSelection(etphonenumber.getText().length());
}
else if (textLength == 10)
{
if (!text.contains("-"))
{
etphonenumber.setText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
etphonenumber.setSelection(etphonenumber.getText().length());
}
}
}
});

Categories

Resources