I have an EditText, where I don't want to allow any special characters since I am storing this String in the SQLlite database. Right now I use:
<item name="android:digits">abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</item>
This works fine for English. But when I switch my keyboard to a different language (Japanese, Russian, Hindi, etc), I am unable to enter anything. What is the right way to do this?
Alternatively:
I am using a ContentProvider to access my SQLite database. So I have methods reading/writing from and to database use
context.getContentResolver().insert(uri, values);
context.getContentResolver().delete(uri, values);
context.getContentResolver().query(uri, values); //for a special case, but only uses id=? in the query
context.getContentResolver().update(uri, values);
So do I need to escape the special characters at all?
//try to add this fillter
InputFilter[] filters = new InputFilter[1];
filters[0] = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (end > start) {
String destTxt = dest.toString();
String resultingTxt = destTxt.substring(0, dstart) + source.subSequence(start, end) + destTxt.substring(dend);
if (!resultingTxt.matches("[a-zA-Z0-9?]*")) {
if (source instanceof Spanned) {
SpannableString sp = new SpannableString("");
return sp;
} else {
return "";
}
}
}
return null;
}
};
edtiText.setFilters(filters);
I know that is a little late to answer to your question but maybe this helps somebody in a future.
What you need is a filter like Haresh's answer but modifing the regular expresion like:
private InputFilter filter = new InputFilter() {
#Override
public CharSequence filter(CharSequence src, int start, int end, Spanned dest, int dstart, int dend) {
if(src.toString().matches("[\\p{Alnum}]*")){
return src;
}
return "";
}
};
If you don't want numbers in the editText then change
"[\\p{Alnum}]*"
with this
"[\\p{Alpha}]*"
If you need something more specific take a look to this link:
PD: I used this code in my application that supports english, spanish and french language and it's working OK.
I hope this can help somebody!
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)
}
}
Is there any way to limit the input text of edittext to english and hebrew only?
Since data is transferred to a server SQL DB, and I do not want to store other languages....
So is it a way to limit to specific language input?
Or maybe there is other way to handle these situation so server will not crash...
Yoav
You can create a custom InputFilter and set it as a filter for your EditText. There's more info about how to do this in this thread. Here's an adaptation of what's there:
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 (!isEnglishOrHebrew(source.charAt(i))) {
return "";
}
}
return null;
}
private boolean isEnglishOrHebrew(char c) {
. . .
}
};
edit.setFilters(new InputFilter[]{filter});
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 have an EditText on which I would like to let user enter only non-numeric chars (say A-Z or a-z): is there a way to do it? All the combinations I used (text, textPersonName and so on) let the user select also numbers.
I think you have to write your own InputFilter and add it to the set of filters for the EditText. Something like this might work:
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.isLetter(source.charAt(i))) {
return "";
}
}
return null;
}
};
edit.setFilters(new InputFilter[]{filter});