I made a code where user can't enter first space in a string.
User is allowed to enter white space after min 2 characters.
I need to redefine my method so user enters white space once, and only once after the two or more characters. After that it should be prevented. How do I do that?
case UPDATE_NAME:
if (firstName.getText().toString().startsWith(" "))
firstName.setText(firstName.getText().toString().trim());
if (firstName.getText().toString().contains(" "))
firstName.setText(firstName.getText().toString().replace(" ", " "));
int indexOfSpace = firstName.getText().toString().lastIndexOf(" ");
if (indexOfSpace > 0) {
String beforeSpace = firstName.getText().toString().substring(0, indexOfSpace);
String[] splitted = beforeSpace.split(" ");
if (splitted != null && splitted.length > 0) {
if (splitted[splitted.length - 1].length() < 2)
firstName.setText(firstName.getText().toString().trim());
}
}
Use a regex pattern. I made one that should match your requirements.
\S{2}\S*\s\S*\n
Explanation:
\S{2} two non whitespace
\S* n non whitespace
\s a whitespace
\S* n non whitespace
\n newline (i only added that for regexr, you may not need it)
Alternate way:
Iterate over String.charAt(int), return false if there is a whitespace in the first two chars, count all whitespaces, return false if n > 1.
This method should meet your requirements:
private static boolean isValidFirstName(String firstName) {
if (firstName != null && !firstName.startsWith(" ")) {
int numberOfSpaces = firstName.length() - firstName.replace(" ", "").length();
if (firstName.length() < 2 || numberOfSpaces <= 1) {
return true;
}
}
return false;
}
What you need to do is use a TextWatcher
public class CustomWatcher implements TextWatcher {
private String myText;
private int count = 0;
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after){
myText= s;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
//check if there is a space in the first 2 characters, if so, sets the string to the previous before the space
if(s.length() < 3 && s.contains(" "))
s= myText;
//if the length is higher than 2, and the count is higher than 0 (1 space added already), puts the string back if a space is entered
else if(s.contains(" ") && count > 0)
s= myText;
//If none of the above is verified and you enter a space, increase count so the previous if statement can do its job
else if(s.contains(" "))
count++;
}
}
And then, set it to your EditText
mTargetEditText.addTextChangedListener(new CustomWatcher());
You can control your editText(I assume) with a TextWatcher, you would only need to check inside afterTextChanged() if length is <2 and else if the string contains the char " ".
I have an EditText where the user wants to enter the price. In order to maintain uniformity, I decided to automatically include the decimal values.
Hence "1" entered by user would become "1.00" for example.
Now, after working with my inefficient code, I can across a better code in StackOverFlow
amountEditText.setRawInputType(Configuration.KEYBOARD_12KEY);
amountEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(!s.toString().matches("^\\$(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$"))
{
String userInput= ""+s.toString().replaceAll("[^\\d]", "");
StringBuilder cashAmountBuilder = new StringBuilder(userInput);
while (cashAmountBuilder.length() > 3 && cashAmountBuilder.charAt(0) == '0') {
cashAmountBuilder.deleteCharAt(0);
}
while (cashAmountBuilder.length() < 3) {
cashAmountBuilder.insert(0, '0');
}
cashAmountBuilder.insert(cashAmountBuilder.length()-2, '.');
cashAmountBuilder.insert(0, '$');
amountEditText.setText(cashAmountBuilder.toString());
// keeps the cursor always to the right
Selection.setSelection(amountEditText.getText(), cashAmountBuilder.toString().length());
}
}
});
With the above code, the decimal value starts getting included once the user starts entering the number. But, the numerical value added by the user will be displayed with $ symbol. Hence, I made a little modification to the code in Line 07 and deleted Line 19.
The new code is
returnPrice.setRawInputType(Configuration.KEYBOARD_12KEY);
returnPrice.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(!s.toString().matches("^(\\d{1,3}(\\,\\d{3})*|(\\d+))(\\.\\d{2})?$"))
{
String userInput= ""+s.toString().replaceAll("[^\\d]", "");
StringBuilder cashAmountBuilder = new StringBuilder(userInput);
while (cashAmountBuilder.length() > 3 && cashAmountBuilder.charAt(0) == '0') {
cashAmountBuilder.deleteCharAt(0);
}
while (cashAmountBuilder.length() < 3) {
cashAmountBuilder.insert(0, '0');
}
cashAmountBuilder.insert(cashAmountBuilder.length()-2, '.');
returnPrice.setText(cashAmountBuilder.toString());
// keeps the cursor always to the right
Selection.setSelection(returnPrice.getText(), cashAmountBuilder.toString().length());
}
}
});
My Problem
So the problem I am facing in case 02 is, when I am entering the numeric value in EditText - unless I am entering the decimal dot, the numeric value in EditText gets displayed as an integer.
For example : Case 01
For displaying 100.00 as I go on typing, the value starts displaying as $0.01, then $0.10, then $1.00, $10.00 and finally $100.00. At any instance, I don't need to enter a decimal dot manually in this case.
For example : Case 02
For displaying 100.00 as I go on typing, the value starts displaying as 1, then 10, then 100. Now, if I press the decimal dot on soft keyboard, the 100 becomes 1.00 and on typing the value turns 10.00 and finally 100.00. If the decimal dot is not touched on the keyboard, the resulting value on entering 10000 is 10000 and not 100.00.
What's the error I made? Any suggestions?
I use InputFilter to check my String from editText.
final InputFilter filter = new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start,
int end, Spanned dest, int dstart, int dend) {
int num = 0;
try {
num = Integer.parseInt(source.toString());
if (!(num > 0 && num < 30)) {
return "";
}
} catch (Exception e) {
}
return null;
}
};
etStatDaysCount = (EditText) inflate2.findViewById(R.id.et_settings_days_to_statistics);
etStatDaysCount.setFilters(new InputFilter[]{filter});
But I have a problem. I want to check if user put number <0 or >30 I want to replace this text. This filter check number one by one. So if I put number 56, he check number 5 and after that 6 so this filter let user put number 56 in edit text. I want to check all string which I put in edittext. So if I put 56 he should check number 5 and after that check number 56. How can I do that?
It seems like you're answering your own question. If the problem is that the filter checks the numbers one by one then don't use it! Just grab the value from the EditText whenever you're ready to test it and make sure it meets your criteria.
etStatDaysCount = (EditText) inflate2.findViewById(R.id.et_settings_days_to_statistics);
//when you're ready to look at the value...
String input = etStatDaysCount.getText().toString()
if(input.trim().equals("") || input.matches("\\D") || Integer.valueOf(input) < 0 || Integer.valueOf(input) > 30)
//this is a value that you don't want
Use TextChangeListener. It is fired up whenever an input is made to editText.
etStatDaysCount = (EditText) inflate2.findViewById(R.id.et_settings_days_to_statistics);
etStatDaysCount.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3 ) {
// you can check every number/alphabet of input here
}
If you don't want to check each individual character in your input, you can always do like:
String yourInput = etStatDaysCount.getText().toString();
if(Integer.valueOf(yourInput) <0 && Integer.valueOf(yourInput) >30 )
// undesired value
Hope this helps
I'm trying to implement an EditText that limits input to Capital chars only [A-Z0-9] with digits as well.
I started with the InputFilter method from some post.But here I am getting one problem on Samsung Galaxy Tab 2 but not in emulator or Nexus 4.
Problem is like this :
When I type "A" the text shows as "A" its good
Now when I type "B" so text should be "AB" but it gives me "AAB"
this looks very Strange.
In short it repeats chars
Here's the code I'm working with this code :
public class DemoFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
int dend) {
if (source.equals("")) { // for backspace
return source;
}
if (source.toString().matches("[a-zA-Z0-9 ]*")) // put your constraints
// here
{
return source.toString().toUpperCase();
}
return "";
}
}
XML file code :
<EditText
android:id="#+id/et_licence_plate_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="0"
android:imeOptions="actionNext"
android:inputType="textNoSuggestions"
android:maxLength="3"
android:singleLine="true"
android:textSize="18px" >
</EditText>
I'm totally stuck up on this one, so any help here would be greatly appreciated.
The problem of characters duplication comes from InputFilter bad implementation. Rather return null if replacement should not change:
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
boolean keepOriginal = true;
StringBuilder sb = new StringBuilder(end - start);
for (int i = start; i < end; i++) {
char c = source.charAt(i);
if (isCharAllowed(c)) // put your condition here
sb.append(c);
else
keepOriginal = false;
}
if (keepOriginal)
return null;
else {
if (source instanceof Spanned) {
SpannableString sp = new SpannableString(sb);
TextUtils.copySpansFrom((Spanned) source, start, end, null, sp, 0);
return sp;
} else {
return sb;
}
}
}
private boolean isCharAllowed(char c) {
return Character.isUpperCase(c) || Character.isDigit(c);
}
I've run into the same issue, after fixing it with solutions posted here there was still a remaining issue with keyboards with autocomplete. One solution is to set the inputType as 'visiblePassword' but that's reducing functionality isn't it?
I was able to fix the solution by, when returning a non-null result in the filter() method, use the call
TextUtils.copySpansFrom((Spanned) source, start, newString.length(), null, newString, 0);
This copies the auto-complete spans into the new result and fixes the weird behaviour of repetition when selecting autocomplete suggestions.
I have found many bugs in the Android's InputFilter, I am not sure if those are bugs or intended to be so. But definitely it did not meet my requirements. So I chose to use TextWatcher instead of InputFilter
private String newStr = "";
myEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String str = s.toString();
if (str.isEmpty()) {
myEditText.append(newStr);
newStr = "";
} else if (!str.equals(newStr)) {
// Replace the regex as per requirement
newStr = str.replaceAll("[^A-Z0-9]", "");
myEditText.setText("");
}
}
#Override
public void afterTextChanged(Editable s) {
// Do nothing
}
});
The above code does not allow users to type any special symbol into your EditText. Only capital alphanumeric characters are allowed.
InputFilters can be attached to Editable S to constrain the changes that can be made to them.
Refer that it emphasises on changes made rather than whole text it contains..
Follow as mentioned below...
public class DemoFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
int dend) {
if (source.equals("")) { // for backspace
return source;
}
if (source.toString().matches("[a-zA-Z0-9 ]*")) // put your constraints
// here
{
char[] ch = new char[end - start];
TextUtils.getChars(source, start, end, ch, 0);
// make the characters uppercase
String retChar = new String(ch).toUpperCase();
return retChar;
}
return "";
}
}
The following solution also supports the option of an autocomplete keyboard
editTextFreeNote.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) {
String newStr = s.toString();
newStr = newStr.replaceAll( "[a-zA-Z0-9 ]*", "" );
if(!s.toString().equals( newStr )) {
editTextFreeNote.setText( newStr );
editTextFreeNote.setSelection(editTextFreeNote.getText().length());
}
}
#Override
public void afterTextChanged(Editable s) {}
} );
Same for me, InputFilter duplicates characters. This is what I've used:
Kotlin version:
private fun replaceInvalidCharacters(value: String) = value.replace("[a-zA-Z0-9 ]*".toRegex(), "")
textView.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
val newValue = replaceInvalidCharacters(s.toString())
if (newValue != s.toString()) {
textView.setText(newValue)
textView.setSelection(textView.text.length)
}
}
})
works well.
try this:
class CustomInputFilter implements InputFilter {
StringBuilder sb = new StringBuilder();
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
Log.d(TAG, "filter " + source + " " + start + " " + end + " dest " + dest + " " + dstart + " " + dend);
sb.setLength(0);
for (int i = start; i < end; i++) {
char c = source.charAt(i);
if (Character.isUpperCase(c) || Character.isDigit(c) || c == ' ') {
sb.append(c);
} else
if (Character.isLowerCase(c)) {
sb.append(Character.toUpperCase(c));
}
}
return sb;
}
}
this also allows filtering when filter() method accepts multiple characters at once e.g. pasted text from a clipboard
I've met this problem few times before.
Setting some kinds of inputTypes in xml propably is the source of problem.
To resolve it without any additional logic in InputFilter or TextWatcher just set input type in code instead xml like this:
editText.setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
recently i faced same problem
reason of the problem is... if there is a no change in the input string then don't return source string return null, some device doesn't handle this properly that's why characters are repating.
in your code you are returning
return source.toString().toUpperCase();
don't return this , return null; in place of return source.toString().toUpperCase(); , but it will be a patch fix , it will not handle all scenarios , for all scenario you can use this code.
public class SpecialCharacterInputFilter implements InputFilter {
private static final String PATTERN = "[^A-Za-z0-9]";
// if you want to allow space use this pattern
//private static final String PATTERN = "[^A-Za-z\\s]";
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
// Only keep characters that are letters and digits
String str = source.toString();
str = str.replaceAll(PATTERN, AppConstants.EMPTY_STRING);
return str.length() == source.length() ? null : str;
}
}
what is happening in this code , there is a regular expression by this we will find all characters except alphabets and digits , now it will replace all characters with empty string, then remaining string will have alphabets and digits.
The problem with most the answers here is that they all mess up the cursor position.
If you simply replace text, your cursor ends up in the wrong place for the next typed character
If you think you handled that by putting their cursor back at the end, well then they can't add prefix text or middle text, they are always jumped back to the end on each typed character, it's a bad experience.
You have an easy way to handle this, and a more universal way to handle it.
The easy way
<EditText
android:id="#+id/itemNameEditText"
android:text="#={viewModel.selectedCartItemModel.customName}"
android:digits="abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
android:inputType="textVisiblePassword"/>
DONE!
Visible password will fix the issue of double callbacks and problems like that. Problem with this solution is it removes your suggestions and autocompletes, and things like that. So if you can get away with this direction, PLEASE DO!!! It will eliminate so many headaches of trying to handle every possible issue of the hard way lol.
The Hard Way
The issue is related to the inputfilter callback structure being triggered by autocomplete. It is easy to reproduce. Just set your inputType = text, and then type abc# you'll see it get called two times and if you can end up with abcabc instead of just abc if you were trying to ignore # for example.
First thing you have to handle is deleting to do this, you must
return null to accept "" as that is triggered by delete.
Second thing you have to handle is holding delete as that updates every so often, but can come in as a long string of characters, so you need to see if your text length shrunk before doing replacement text or you can end up duplicating your text while holding delete.
Third thing you need to handle is the duplicate callback, by keeping track of the previous text change call to avoid getting it twice. Don't worry you can still type the same letters back to back, it won't prevent that.
Here is my example. It's not perfect, and still has some kinks to work out, but it's a good place to start.
The following example is using databinding, but you are welcome to just use the intentFilter without databinding if that's your style. Abbreviated UI for showing only the parts that matter.
In this example, I restrict to alpha, numeric, and spaces only. I was able to cause a semi-colon to show up once while pounding on the android keyboard like crazy. So there is still some tweaking I believe that may need done.
DISCLAIMER
--I have not tested with auto complete
--I have not tested with suggestions
--I have not tested with copy/paste
--This solution is a 90% there solution to help you, not a battle tested solution
XML FILE
<layout
xmlns:bind="http://schemas.android.com/apk/res-auto"
>
<EditText
bind:allowAlphaNumericOnly="#{true}
OBJECT FILE
#JvmStatic
#BindingAdapter("allowAlphaNumericOnly")
fun restrictTextToAlphaNumericOnly(editText: EditText, value: Boolean) {
val tagMap = HashMap<String, String>()
val lastChange = "repeatCheck"
val lastKnownSize = "handleHoldingDelete"
if (value) {
val filter = InputFilter { source, start, end, dest, dstart, dend ->
val lastKnownChange = tagMap[lastChange]
val lastKnownLength = tagMap[lastKnownSize]?.toInt()?: 0
//handle delete
if (source.isEmpty() || editText.text.length < lastKnownLength) {
return#InputFilter null
}
//duplicate callback handling, Android OS issue
if (source.toString() == lastKnownChange) {
return#InputFilter ""
}
//handle characters that are not number, letter, or space
val sb = StringBuilder()
for (i in start until end) {
if (Character.isLetter(source[i]) || Character.isSpaceChar(source[i]) || Character.isDigit(source[i])) {
sb.append(source[i])
}
}
tagMap[lastChange] = source.toString()
tagMap[lastKnownSize] = editText.text.length.toString()
return#InputFilter sb.toString()
}
editText.filters = arrayOf(filter)
}
}
Stuck on this which im sure there is an easy solution to, just cannot work it out!!
I have tried decmialformat, numberformat, string.format() etc and nothing works. .
code below, i want to calculation to just show the output limited to 2 decimal places. Have spent the last 2 hours trying various methods all of which causes the app to crash when run...
Output = (Output1 / (1 -(Output2/100)))
String OutputString = String.valueOf(Output);
Num.setText(OutputString);
Try this :
String OutputString = String.format("%.2f", Output);
Num.setText(OutputString);
String.format() to make sure you only get 2 decimal places in your output.
please try this:
double Output = (Output1 / (1 -(Output2/100d)))
Num.setText(String.format("%.2f",Output));
Hope this solves your problem.
Best regards
If you want to Limit the number of Digits before and after the 'decimal_point' then you can use my solution.
private class DecimalNumberFormatTextWatcher implements TextWatcher{
int pos;
int digitsBeforeDecimal = 6;
int digitsAfterDecimal = 2;
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(s.length() > 2)
pos = start;
else {
pos = start + 2;
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
mEdittext.removeTextChangedListener(this);
String text = s.toString();
if(text!= null && !text.equals("")){
if(!text.contains("$")){ //if it does not contains $
text = "$"+text;
} else {
if (text.indexOf("$") > 0) { //user entered value before $
text = s.delete(0, text.indexOf("$")).toString();
}else {
if(!text.contains(".")){ // not a fractional value
if(text.length() > digitsBeforeDecimal+1) { //cannot be more than 6 digits
text = s.delete(pos, pos+1).toString();
}
} else { //a fractional value
if(text.indexOf(".") - text.indexOf("$") > digitsBeforeDecimal+1){ //non fractional part cannot be more than 6
text = s.delete(pos,pos+1).toString();
}
if((text.length() - text.indexOf(".")) > digitsAfterDecimal+1) { //fractinal part cannot be more than 2 digits
text = s.delete(text.indexOf(".") + 2, text.length() - 1).toString();
}
}
}
}
}
mEdittext.setText(text);
mEdittext.setSelection(pos);
mEdittext.addTextChangedListener(this);
}
}
mEdittext.addTextChangedListener(new DecimalNumberFormatTextWatcher());
This also adds a currency sign as soon as the user types the value.
HOPE THIS HELPS ANYONE.