Odpowiedzi:
Znalazłem to na innym forum. Działa jak mistrz.
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 });
InputFilter
Są trochę skomplikowane w wersjach Androida, które wyświetlają sugestie ze słownika. Czasami w parametrze pojawia się a SpannableStringBuilder
, czasami zwykły .String
source
Poniższe InputFilter
powinny działać. Zapraszam do ulepszania tego kodu!
new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
if (source instanceof SpannableStringBuilder) {
SpannableStringBuilder sourceAsSpannableBuilder = (SpannableStringBuilder)source;
for (int i = end - 1; i >= start; i--) {
char currentChar = source.charAt(i);
if (!Character.isLetterOrDigit(currentChar) && !Character.isSpaceChar(currentChar)) {
sourceAsSpannableBuilder.delete(i, i+1);
}
}
return source;
} else {
StringBuilder filteredStringBuilder = new StringBuilder();
for (int i = start; i < end; i++) {
char currentChar = source.charAt(i);
if (Character.isLetterOrDigit(currentChar) || Character.isSpaceChar(currentChar)) {
filteredStringBuilder.append(currentChar);
}
}
return filteredStringBuilder.toString();
}
}
}
String replacement = source.subSequence(start, end).toString(); return replacement.replaceAll("[^A-Za-z0-9_\\-@]", "");
source instanceof SpannableStringBuilder
wpisanie AB daje mi AAB, tak jak przy próbowaniu poprzedniej odpowiedzi. Na szczęście udało mi się to obejść, korzystając z rozwiązania @florian poniżej.
dużo łatwiej:
<EditText
android:inputType="text"
android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
","
. Możesz użyć czegoś takiego"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
imeOptions="actionNext"
itp.
Żadna z opublikowanych odpowiedzi nie zadziałała dla mnie. Przyszedłem z własnym rozwiązaniem:
InputFilter filter = new InputFilter() {
@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, sb.length(), null, sp, 0);
return sp;
} else {
return sb;
}
}
}
private boolean isCharAllowed(char c) {
return Character.isLetterOrDigit(c) || Character.isSpaceChar(c);
}
}
editText.setFilters(new InputFilter[] { filter });
EditText
może mieć już własne filtry, np. Filtr długości. Dlatego zamiast po prostu zastępować filtry, najprawdopodobniej chcesz dodać filtry do już istniejących.
Wykorzystaj to w 100% swojej pracy i bardzo proste.
<EditText
android:inputType="textFilter"
android:digits="@string/myAlphaNumeric" />
W strings.xml
<string name="myAlphaNumeric">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</string>
Aby uniknąć znaków specjalnych w typie wprowadzania
public static InputFilter filter = new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String blockCharacterSet = "~#^|$%*!@/()-'\":;,?{}=!$^';,?×÷<>{}€£¥₩%~`¤♡♥_|《》¡¿°•○●□■◇◆♧♣▲▼▶◀↑↓←→☆★▪:-);-):-D:-(:'(:O 1234567890";
if (source != null && blockCharacterSet.contains(("" + source))) {
return "";
}
return null;
}
};
Możesz ustawić filtr do tekstu edycji, jak poniżej
edtText.setFilters(new InputFilter[] { filter });
Oprócz zaakceptowanej odpowiedzi możliwe jest również użycie np .: android:inputType="textCapCharacters"
jako atrybutu <EditText>
w celu akceptowania wyłącznie wielkich liter (i cyfr).
Z jakiegoś powodu konstruktor klasy android.text.LoginFilter jest objęty zakresem pakietu, więc nie można go bezpośrednio rozszerzyć (nawet jeśli byłby identyczny z tym kodem). Ale możesz rozszerzyć LoginFilter.UsernameFilterGeneric! Wtedy po prostu masz to:
class ABCFilter extends LoginFilter.UsernameFilterGeneric {
public UsernameFilter() {
super(false); // false prevents not-allowed characters from being appended
}
@Override
public boolean isAllowed(char c) {
if ('A' <= c && c <= 'C')
return true;
if ('a' <= c && c <= 'c')
return true;
return false;
}
}
Nie jest to tak naprawdę udokumentowane, ale jest częścią podstawowej biblioteki, a źródło jest proste . Używam go już od jakiegoś czasu, do tej pory żadnych problemów, chociaż przyznaję, że nie próbowałem robić nic skomplikowanego z udziałem spannables.
To prawda, najlepszym sposobem na naprawienie tego w samym układzie XML za pomocą:
<EditText
android:inputType="text"
android:digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" />
jak słusznie zauważył Florian Fröhlich, działa dobrze nawet w przypadku widoków tekstowych.
<TextView
android:inputType="text"
android:digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" />
Tylko słowo ostrzeżenia, znaki wymienione w android:digits
będą wyświetlane tylko, więc uważaj, aby nie przegapić żadnego zestawu znaków :)
inputType
nie wpływa na filtrowanie.
To proste rozwiązanie zadziałało, gdy musiałem uniemożliwić użytkownikowi wprowadzanie pustych ciągów do tekstu edycji. Możesz oczywiście dodać więcej znaków:
InputFilter textFilter = new InputFilter() {
@Override
public CharSequence filter(CharSequence c, int arg1, int arg2,
Spanned arg3, int arg4, int arg5) {
StringBuilder sbText = new StringBuilder(c);
String text = sbText.toString();
if (text.contains(" ")) {
return "";
}
return c;
}
};
private void setTextFilter(EditText editText) {
editText.setFilters(new InputFilter[]{textFilter});
}
Jeśli utworzysz podklasę InputFilter, możesz utworzyć własny InputFilter, który odfiltrowałby wszelkie znaki inne niż alfanumeryczne.
Interfejs InputFilter ma jedną metodę filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)
i zapewnia wszystkie potrzebne informacje o tym, które znaki zostały wprowadzone do EditText, do którego jest przypisany.
Po utworzeniu własnego filtra InputFilter możesz przypisać go do EditText, wywołując setFilters (...).
http://developer.android.com/reference/android/text/InputFilter.html#filter(java.lang.CharSequence , int, int, android.text.Spanned, int, int)
Ignorując rzeczy z zakresu rozpiętości, z którymi mieli do czynienia inni ludzie, aby poprawnie obsłużyć sugestie ze słownika, stwierdziłem, że następujący kod działa.
Źródło rośnie wraz ze wzrostem sugestii, więc musimy przyjrzeć się, ile znaków faktycznie oczekuje, że zastąpimy, zanim cokolwiek zwrócimy.
Jeśli nie mamy żadnych nieprawidłowych znaków, zwróć wartość null, aby nastąpiła domyślna zamiana.
W przeciwnym razie musimy wyodrębnić prawidłowe znaki z podciągu, który AKTUALNIE zostanie umieszczony w EditText.
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
boolean includesInvalidCharacter = false;
StringBuilder stringBuilder = new StringBuilder();
int destLength = dend - dstart + 1;
int adjustStart = source.length() - destLength;
for(int i=start ; i<end ; i++) {
char sourceChar = source.charAt(i);
if(Character.isLetterOrDigit(sourceChar)) {
if(i >= adjustStart)
stringBuilder.append(sourceChar);
} else
includesInvalidCharacter = true;
}
return includesInvalidCharacter ? stringBuilder : null;
}
};
aby zapobiec słowom w edittext. stwórz klasę, której będziesz mógł użyć w dowolnym momencie.
public class Wordfilter implements InputFilter
{
@Override
public CharSequence filter(CharSequence source, int start, int end,Spanned dest, int dstart, int dend) {
// TODO Auto-generated method stub
boolean append = false;
String text = source.toString().substring(start, end);
StringBuilder str = new StringBuilder(dest.toString());
if(dstart == str.length())
{
append = true;
str.append(text);
}
else
str.replace(dstart, dend, text);
if(str.toString().contains("aaaaaaaaaaaa/*the word here*/aaaaaaaa"))
{
if(append==true)
return "";
else
return dest.subSequence(dstart, dend);
}
return null;
}
}
Najpierw dodaj do strings.xml
:
<string name="vin_code_mask">0123456789abcdefghjklmnprstuvwxyz</string>
XML :
android:digits="@string/vin_code_mask"
Kod w Kotlinie:
edit_text.filters += InputFilter { source, start, end, _, _, _ ->
val mask = getString(R.string.vin_code_mask)
for (i in start until end) {
if (!mask.contains(source[i])) {
return@InputFilter ""
}
}
null
}
Dziwne, ale działa dziwnie na miękkiej klawiaturze emulatora.
Ostrzeżenie! Poniższy kod odfiltruje wszystkie litery i inne symbole z wyjątkiem cyfr w klawiaturach programowych. Na smartfonach pojawi się tylko klawiatura cyfrowa.
edit_text.keyListener = DigitsKeyListener.getInstance(context.getString(R.string.vin_code_mask))
Ja też zwykle ustawione maxLength
, filters
, inputType
.
To stary wątek, ale wszystkie celowe rozwiązania mają problemy (w zależności od urządzenia / wersji Androida / klawiatury).
ODMIENNE PODEJŚCIE
Więc w końcu pojechałem z innego podejścia, zamiast korzystania z InputFilter
problematyczną wdrażania, używam TextWatcher
i TextChangedListener
z EditText
.
PEŁNY KOD (PRZYKŁAD)
editText.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable editable) {
super.afterTextChanged(editable);
String originalText = editable.toString();
int originalTextLength = originalText.length();
int currentSelection = editText.getSelectionStart();
// Create the filtered text
StringBuilder sb = new StringBuilder();
boolean hasChanged = false;
for (int i = 0; i < originalTextLength; i++) {
char currentChar = originalText.charAt(i);
if (isAllowed(currentChar)) {
sb.append(currentChar);
} else {
hasChanged = true;
if (currentSelection >= i) {
currentSelection--;
}
}
}
// If we filtered something, update the text and the cursor location
if (hasChanged) {
String newText = sb.toString();
editText.setText(newText);
editText.setSelection(currentSelection);
}
}
private boolean isAllowed(char c) {
// TODO: Add the filter logic here
return Character.isLetter(c) || Character.isSpaceChar(c);
}
@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) {
// Do Nothing
}
});
Powodem InputFilter
nie jest dobre rozwiązanie w systemie Android, ponieważ zależy to od implementacji klawiatury. Dane wejściowe z klawiatury są filtrowane przed przekazaniem ich do EditText
. Ale ponieważ niektóre klawiatury mają różne implementacje InputFilter.filter()
wywołania, jest to problematyczne.
Z drugiej strony TextWatcher
nie dba o implementację klawiatury, pozwala nam stworzyć proste rozwiązanie i mieć pewność, że zadziała na wszystkich urządzeniach.
onTextChanged
prostu potrzebuje public void
przed sobą.
Zrobiłem coś takiego, aby było to proste:
edit_text.filters = arrayOf(object : InputFilter {
override fun filter(
source: CharSequence?,
start: Int,
end: Int,
dest: Spanned?,
dstart: Int,
dend: Int
): CharSequence? {
return source?.subSequence(start, end)
?.replace(Regex("[^A-Za-z0-9 ]"), "")
}
})
W ten sposób zamieniamy wszystkie niechciane znaki w nowej części ciągu źródłowego na pusty ciąg.
edit_text
Zmienna jest EditText
obiektem mamy na myśli.
Kod jest napisany kotlin
.
Jest możliwe użycie setOnKeyListener
. W tej metodzie możemy dostosować dane wejściowe edittext
!
W ten sposób utworzyłem filtr dla pola Nazwa w Edytuj tekst (pierwsza litera to WIELKIE LITERY i zezwalaj tylko na jedną spację po każdym słowie.
public void setNameFilter() {
InputFilter filter = new InputFilter() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (dend == 0) {
if (Character.isSpaceChar(source.charAt(i)) ||
!Character.isAlphabetic(source.charAt(i))) {
return Constants.Delimiter.BLANK;
} else {
return String.valueOf(source.charAt(i)).toUpperCase();
}
} else if (Character.isSpaceChar(source.charAt(i)) &&
String.valueOf(dest).endsWith(Constants.Delimiter.ONE_SPACE)) {
return Constants.Delimiter.BLANK;
} else if ((!Character.isSpaceChar(source.charAt(i)) &&
!Character.isAlphabetic(source.charAt(i)))) {
return Constants.Delimiter.BLANK;
}
}
return null;
}
};
editText.setFilters(new InputFilter[]{filter, new InputFilter.LengthFilter(Constants.Length.NAME_LENGTH)});
}
Taką samą odpowiedź mam w Kotlinie:
/**
* Returns the filter of the editText'es CharSequence value when [filterType] is:
* 1 -> letters; 2 -> letters and digits; 3 -> digits;
* 4 -> digits and dots
*/
class InputFilterAlphanumeric(private val filterType: Int): InputFilter {
override fun filter(source: CharSequence?, start: Int, end: Int, dest: Spanned?, dstart: Int, dend: Int): CharSequence {
(source as? SpannableStringBuilder)?.let {sourceAsSpannableBuilder ->
for (i in (end - 1) downTo start) {
val currentChar = source[i]
when(filterType) {
1 -> {
if (!currentChar.isLetter() && !currentChar.isWhitespace()) {
sourceAsSpannableBuilder.delete(i, i + 1)
}
}
2 -> {
if (!currentChar.isLetterOrDigit() && !currentChar.isWhitespace()) {
sourceAsSpannableBuilder.delete(i, i + 1)
}
}
3 -> {
if (!currentChar.isDigit()) {
sourceAsSpannableBuilder.delete(i, i + 1)
}
}
4 -> {
if (!currentChar.isDigit() || !currentChar.toString().contains(".")) {
sourceAsSpannableBuilder.delete(i, i + 1)
}
}
}
}
return source
} ?: run {
val filteredStringBuilder = StringBuilder()
for (i in start until end) {
val currentChar = source?.get(i)
when(filterType) {
1 -> {
if (currentChar?.isLetter()!! || currentChar.isWhitespace()) {
filteredStringBuilder.append(currentChar)
}
}
2 -> {
if (currentChar?.isLetterOrDigit()!! || currentChar.isWhitespace()) {
filteredStringBuilder.append(currentChar)
}
}
3 -> {
if (currentChar?.isDigit()!!) {
filteredStringBuilder.append(currentChar)
}
}
4 -> {
if (currentChar?.isDigit()!! || currentChar.toString().contains(".")) {
filteredStringBuilder.append(currentChar)
}
}
}
}
return filteredStringBuilder
}
}
}
i pobierz klasę z funkcją rozszerzenia:
fun EditText.filterByDataType(filterType: Int) {
this.filters = arrayOf<InputFilter>(InputFilterAlphanumeric(filterType))
}