Textwatcher - Different Last Char Situations - android

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

Related

Android - Formatting phone number in EditText

I need a little assistance making this to work, because server side API requires special format for phone number field. Maybe I could edit that field just before sending API request by replacing characters at specific positions, however that would still give the user freedom to insert wrong format for phone number. I need to make EditText assist him just right after he changed the text and direct him to right format.
For that I have used TextWatcher method afterTextChanged() and format which I need is next: (063)22-22-333
This is what I have tried:
private static final char space = '-';
private static final char brackets = '(';
private static final char brackets1 = ')';
etPhone.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
// (063)22-22-333
// -> Error
if (s.length() > 0 && !s.toString().startsWith("(")) {
s.replace(0, s.length(), "(0");
}
if (s.length() > 0 && s.length() == 8) {
final char c = s.charAt(s.length() - 1);
if (space == c) {
s.delete(s.length() - 1, s.length());
}
} else if (s.length() > 0 && s.length() == 5) {
final char c = s.charAt(s.length() - 1);
if (brackets1 == c) {
s.delete(s.length() - 1, s.length());
}
}
// Insert char where needed.
if (s.length() > 0 && s.length() == 8) {
char c = s.charAt(s.length() - 1);
// Only if its a digit where there should be a space we insert a space
if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(space)).length <= 7) {
s.insert(s.length() - 1, String.valueOf(space));
}
} else if (s.length() > 0 && s.length() == 5) {
char c = s.charAt(s.length() - 1);
if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(brackets1)).length <= 4) {
s.insert(s.length() - 1, String.valueOf(brackets1));
}
}
}
});
I'm getting an error after writing 4 character. Error is showing at the beginning.
You are using the split() method which needs as its 2nd parameter a regular expression and here is your problem: you need to escape "(" and ")" in regular expressions. So I made some changes:
private static final char space = '-';
private static final char brackets = '(';
private static final char brackets1 = ')';
private static final String sspace = "\\x32";
private static final String sbrackets = "\\x28";
private static final String sbrackets1 = "\\x29";
etPhone.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
// (063)22-22-333
// -> Error
if (s.length() > 0 && !s.toString().startsWith("(")) {
s.replace(0, s.length(), "(0");
}
if (s.length() > 0 && s.length() == 8) {
final char c = s.charAt(s.length() - 1);
if (space == c) {
s.delete(s.length() - 1, s.length());
}
} else if (s.length() > 0 && s.length() == 5) {
final char c = s.charAt(s.length() - 1);
if (brackets1 == c) {
s.delete(s.length() - 1, s.length());
}
}
// Insert char where needed.
if (s.length() > 0 && s.length() == 8) {
char c = s.charAt(s.length() - 1);
// Only if its a digit where there should be a space we insert a space
if (Character.isDigit(c) && TextUtils.split(s.toString(), sspace).length <= 7) {
s.insert(s.length() - 1, String.valueOf(space));
}
} else if (s.length() > 0 && s.length() == 5) {
char c = s.charAt(s.length() - 1);
if (Character.isDigit(c) && TextUtils.split(s.toString(), sbrackets1).length <= 4) {
s.insert(s.length() - 1, String.valueOf(brackets1));
}
}
}
});
Now you get no crash after the 3d char.
Every time you want to use split() instead of String.valueOf(brackets1) use brackets1 or sbrackets or sspace
You might have an easier time converting from a common format to the format that the server side is expecting. To get to a standard format, there are a couple of classes which already exist that might help you. Have you looked at PhoneNumberFormattingTextWatcher?
If you absolutely need to use your own watcher, there are other utils that can help you:
// this will result in the string "(212) 555-2232"
String number = PhoneNumberUtils.formatNumber("2125552232", "US")
After getting a predictable result, it should be trivial to convert to whatever your server needs.

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 slow when using setText

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.

Categories

Resources