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)
}
}
Related
In my application I have to validate the EditText. It should only allow character, digits, underscores, and hyphens.
Here is my code:
edittext.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) {
// validation codes here
location_name=s.toString();
Toast.makeText(getApplicationContext(),location_name, Toast.LENGTH_SHORT).show();
if (location_name.matches(".*[^a-z^0-9].*")) {
location_name = location_name.replaceAll("[^a-z^0-9]", "");
s.append(location_name);
s.clear();
Toast.makeText(getApplicationContext(),"Only lowercase letters and numbers are allowed!",Toast.LENGTH_SHORT).show();
}
}
});
location.add(location_name);
When I enter input into EditText, the application is force closed.
Instead of using your "manual" checking method, there is something very easy in Android:
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start,
int end, Spanned dest, int dstart, int dend) {
for (int i = start;i < end;i++) {
if (!Character.isLetterOrDigit(source.charAt(i)) &&
!Character.toString(source.charAt(i)).equals("_") &&
!Character.toString(source.charAt(i)).equals("-"))
{
return "";
}
}
return null;
}
};
edittext.setFilters(new InputFilter[] { filter });
Or another approach: set the allowed characters in the XML where you are creating your EditText:
<EditText
android:inputType="text"
android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm,_,-"
android:hint="Only letters, digits, _ and - allowed" />
<EditText
android:inputType="text"
android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm,_,-"
android:hint="Only letters, digits, _ and - allowed"
/>
the above code will also include , additionally to avoid , use the following code
<EditText
android:inputType="text"
android:digits="0123456789qwertzuiopasdfghjklyxcvbnm_-"
android:hint="Only letters, digits, _ and - allowed"
/>
This can be useful, especially if your EditText should allow diacritics (in my case, Portuguese Diacritic):
<EditText
android:digits="0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzÁáÂâÃãÀàÇçÉéÊêÍíÓóÔôÕõÚú"
/>
A solution similar to the android:digits="0123456789*", is to use this:
EditText etext = new EditText(this);
etext.setKeyListener(DigitsKeyListener.getInstance("0123456789*"));
An added bonus is that it also displays the numeric keypad.
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start,
int end, Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (!Character.toString(source.charAt(i)).matches("[a-zA-Z0-9-_]+")) {
return "";
edittext.setError("Only letters, digits, _ and - allowed");
}
}
return null;
}
};
edittext.setFilters(new InputFilter[] { filter });
Try this:
public static SpannableStringBuilder getErrorMsg(String estring) {
int ecolor = Color.BLACK; // whatever color you want
ForegroundColorSpan fgcspan = new ForegroundColorSpan(ecolor);
SpannableStringBuilder ssbuilder = new SpannableStringBuilder(estring);
ssbuilder.setSpan(fgcspan, 0, estring.length(), 0);
return ssbuilder;
}
Then setError() to EditText when you want show 'only Letters and digits are allowed' as below.
etPhone.setError(getErrorMsg("Only lowercase letters and numbers are allowed!"));
Hope this will help you. I use the same for Validation Check in EditText in my apps.
please try adding the android:digits="abcde.....012345789" attribute? although the android:digits specify that it is a numeric field it does work for me to set it to accept letters as well, and special characters as well (tested on SDK-7)
You can validate this by two ways , both worked for me, hope will be helpful for you too.
1> Mentioning the chars in your edittext .
android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm,_,-"
2> Can validate pragmatically as answered by milos pragmatically
use this 2 function
public static boolean isdigit(EditText input)
{
String data=input.getText().toString().trim();
for(int i=0;i<data.length();i++)
{
if (!Character.isDigit(data.charAt(i)))
return false;
}
return true;
}
public static boolean ischar(EditText input)
{
String data=input.getText().toString().trim();
for(int i=0;i<data.length();i++)
{
if (!Character.isDigit(data.charAt(i)))
return true;
}
return false;
}
pass Edittext variable in these function .. so you can have boolean value.
I have edittext in listview. I want to restrict the user if they enter more than two digits after the decimal point. Now it allowing n number of numbers. How to restrict the user did not enter more than two numbers after decimal point without using pattern?
We can use a regular expression ( regex ) as follows:
public class DecimalDigitsInputFilter implements InputFilter {
Pattern mPattern;
public DecimalDigitsInputFilter(int digitsBeforeZero,int digitsAfterZero) {
mPattern=Pattern.compile("[0-9]{0," + (digitsBeforeZero-1) + "}+((\\.[0-9]{0," + (digitsAfterZero-1) + "})?)||(\\.)?");
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
Matcher matcher=mPattern.matcher(dest);
if(!matcher.matches())
return "";
return null;
}
}
To use it do:
editText.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(5,2)});
It might be late but i am posting my solution hope it will works for someone
in Your Text Watcher Method afterTextChanged do this .
public void afterTextChanged(Editable s) {
if(mEditText.getText().toString().contains(".")){
String temp[] = mEditText.getText().toString().split("\\.");
if(temp.length>1) {
if (temp[1].length() > 3) {
int length = mEditText.getText().length();
mEditText.getText().delete(length - 1, length);
}
}
}
}
You can use a TextWatcher and in the afterTextChanged method use a regular expression to match the required text and delete the extra numbers are they are entered.
I try to add an event handle to a EditText object (a text area the use can edit) in my Android App to control what the use can edit or not.
Say I have this text in my EditText:
* Hello
* World
* On You...
I will attach a controller that allows the user to only edit the Hello, World or On You, and if the user try to edit or remove the * and the first space after * the system will stop the edit.
On Java SE I can use Document.remove(int, int) for getting event while an user try to remove or replace a part of the text and then stop the edit.
Is there a similar API for android EditText?
I have tried using TextWatcher but from what I know this is not going to help me,
I know method public void onTextChanged(CharSequence s, int start, int before, int count) give some notes about the which text user remove but this seem unreliable.
And this give me not a way for this matter to stop the edit of use.
EDIT:
Can I use a Spanner for prevent edit to be do for a part of the text? As a readonly spanner?
Someone knows a good way to fix my problems?
I think I has final find a working solustion, but has to be try more deeply to know if this exist some buggs or errors.
public abstract class TextListener implements InputFilter {
public abstract CharSequence removeStr(CharSequence removeChars, int startPos);
public abstract CharSequence insertStr(CharSequence newChars, int startPos);
public abstract CharSequence updateStr(CharSequence oldChars, int startPos, CharSequence newChars);
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
CharSequence returnStr = source;
String curStr = dest.subSequence(dstart, dend).toString();
String newStr = source.toString();
int length = end - start;
int dlength = dend - dstart;
if (dlength > 0 && length == 0) {
// Case: Remove chars, Simple
returnStr = TextListener.this.removeStr(dest.subSequence(dstart, dend), dstart);
} else if (length > 0 && dlength == 0) {
// Case: Insert chars, Simple
returnStr = TextListener.this.insertStr(source.subSequence(start, end), dstart);
} else if (curStr.length() > newStr.length()) {
// Case: Remove string or replace
if (curStr.startsWith(newStr)) {
// Case: Insert chars, by append
returnStr = TextUtils.concat(curStr.subSequence(0, newStr.length()), TextListener.this.removeStr(curStr.subSequence(newStr.length(), curStr.length()), dstart + curStr.length()));
} else {
// Case Replace chars.
returnStr = TextListener.this.updateStr(curStr, dstart, newStr);
}
} else if (curStr.length() < newStr.length()) {
// Case: Append String or rrepace.
if (newStr.startsWith(curStr)) {
// Addend, Insert
returnStr = TextUtils.concat(curStr, TextListener.this.insertStr(newStr.subSequence(curStr.length(), newStr.length()), dstart + curStr.length()));
} else {
returnStr = TextListener.this.updateStr(curStr, dstart, newStr);
}
} else {
// No update os str...
}
// If the return value is same as the source values, return the source value.
return TextUtils.equals(source, returnStr) ? source : returnStr;
}
}
From this code I can easy prevent edit on select part of the text by lookup were in the text I try to edit.
I have a TextView in my app that i want a user to be able to only enter alpha-numeric characters in. How can this be done? Thanks!
In the XML, put this:
android:digits="abcdefghijklmnopqrstuvwxyz1234567890 "
Here is a better solution......... https://groups.google.com/forum/?fromgroups=#!topic/android-developers/hS9Xj3zFwZA
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (!Character.isLetterOrDigit(source.charAt(i))) {
return "";
}
}
return null;
}
};
edit.setFilters(new InputFilter[]{filter});
The InputFilter solution works well, and gives you full control to filter out input at a finer grain level than android:digits. The filter() method should return null if all characters are valid, or a CharSequence of only the valid characters if some characters are invalid. If multiple characters are copied and pasted in, and some are invalid, only the valid characters should be kept (#AchJ's solution will reject the entire paste if any characters a invalid).
public static class AlphaNumericInputFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
// Only keep characters that are alphanumeric
StringBuilder builder = new StringBuilder();
for (int i = start; i < end; i++) {
char c = source.charAt(i);
if (Character.isLetterOrDigit(c)) {
builder.append(c);
}
}
// If all characters are valid, return null, otherwise only return the filtered characters
boolean allCharactersValid = (builder.length() == end - start);
return allCharactersValid ? null : builder.toString();
}
}
Also, when setting your InputFilter, you must make sure not to overwrite other InputFilters set on your EditText; these could be set in XML, like android:maxLength. You must also consider the order that the InputFilters are set. When used in conjunction with a length filter, your custom filter should be inserted before the length filter, that way pasted text applies the custom filter before the length filter (#AchJ's solution will overwrite all other InputFilters and only apply the custom one).
// Apply the filters to control the input (alphanumeric)
ArrayList<InputFilter> curInputFilters = new ArrayList<InputFilter>(Arrays.asList(editText.getFilters()));
curInputFilters.add(0, new AlphaNumericInputFilter());
InputFilter[] newInputFilters = curInputFilters.toArray(new InputFilter[curInputFilters.size()]);
editText.setFilters(newInputFilters);
This should work:
textView.setInputType(InputType.TYPE_CLASS_NUMBER);
I want to restrict capital letter typing through keyboard on my form.
any one guide me how to achieve this?
In source code:
InputFilter smallFilter = new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (Character.isUpperCase(source.charAt(i))) {
char[] v = new char[end - start];
TextUtils.getChars(source, start, end, v, 0);
String s = new String(v).toLowerCase();
if (source instanceof Spanned) {
SpannableString sp = new SpannableString(s);
TextUtils
.copySpansFrom((Spanned) source, start, end, null, sp, 0);
return sp;
} else {
return s;
}
}
}
return null;
}
};
EditText vText = ...;
vText.setFilters(new InputFilter[]{smallFilter});
It is based on Android source of: InputFilter.AllCaps. Tested and works.
First, what's your goal? Why do you want to restrict capital letters? Can't you take care of them on the validation of user input and use toLowerCase()
If you do need to restrict capital letters, the only way I can think of is overriding onTextChanged(CharSequence text, int start, int before, int after) and trying to do whatever you want inside it. I haven't tested it, but it might work.
Update: radek mentioned InputFilter. That solutions seems cleaner than mine, but I've never used them.
InputFilters can be attached to Editables to constrain the changes that can be made to them.