Risposte:
L'ho trovato su un altro forum. Funziona come un campione.
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 });
InputFiltersono un po 'complicati nelle versioni di Android che mostrano suggerimenti nel dizionario. A volte si ottiene un SpannableStringBuilder, a volte un chiaro Stringnel sourceparametro.
Quanto segue InputFilterdovrebbe funzionare. Sentiti libero di migliorare questo codice!
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 SpannableStringBuilderentrare in AB mi dà AAB come quando provo la risposta precedente. Fortunatamente sono stato in grado di aggirare il problema utilizzando la soluzione @florian di seguito.
molto più facile:
<EditText
android:inputType="text"
android:digits="0,1,2,3,4,5,6,7,8,9,*,qwertzuiopasdfghjklyxcvbnm" />
",". Puoi usare qualcosa del genere"0123456789qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM"
imeOptions="actionNext", ecc.
Nessuna delle risposte postate ha funzionato per me. Sono venuto con la mia soluzione:
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 });
EditTextpuò già avere i propri filtri, ad es. Filtro di lunghezza. Quindi, invece di sovrascrivere i filtri, molto probabilmente desideri aggiungere i filtri a quelli già esistenti.
Usa questo suo lavoro al 100% le tue necessità e molto semplice.
<EditText
android:inputType="textFilter"
android:digits="@string/myAlphaNumeric" />
In strings.xml
<string name="myAlphaNumeric">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</string>
Per evitare caratteri speciali nel tipo di input
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;
}
};
Puoi impostare il filtro sul testo di modifica come di seguito
edtText.setFilters(new InputFilter[] { filter });
Oltre alla risposta accettata, è anche possibile utilizzare ad esempio: android:inputType="textCapCharacters"come attributo di <EditText>per accettare solo caratteri maiuscoli (e numeri).
Per qualche motivo il costruttore della classe android.text.LoginFilter è di tipo package, quindi non è possibile estenderlo direttamente (anche se sarebbe identico a questo codice). Ma puoi estendere LoginFilter.UsernameFilterGeneric! Quindi hai solo questo:
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;
}
}
Questo non è davvero documentato, ma fa parte della libreria principale e la fonte è semplice . Lo sto usando da un po 'di tempo, finora nessun problema, anche se ammetto di non aver provato a fare qualcosa di complesso che coinvolge spannables.
È giusto, il modo migliore per risolverlo nel layout XML stesso usando:
<EditText
android:inputType="text"
android:digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" />
come giustamente indicato da Florian Fröhlich, funziona bene anche per le visualizzazioni di testo.
<TextView
android:inputType="text"
android:digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" />
Solo una parola di cautela, i personaggi citati in android:digitsverranno visualizzati solo, quindi fai attenzione a non perdere nessun set di caratteri :)
inputTypenon influisce sul filtro.
Questa semplice soluzione ha funzionato per me quando avevo bisogno di impedire all'utente di inserire stringhe vuote in un EditText. Ovviamente puoi aggiungere più personaggi:
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});
}
Se si esegue la sottoclasse di InputFilter, è possibile creare il proprio InputFilter che filtra qualsiasi carattere non alfanumerico.
L'interfaccia InputFilter ha un metodo filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)e ti fornisce tutte le informazioni che devi sapere su quali caratteri sono stati inseriti nel EditText a cui è assegnato.
Dopo aver creato il tuo InputFilter, puoi assegnarlo a EditText chiamando setFilters (...).
http://developer.android.com/reference/android/text/InputFilter.html#filter(java.lang.CharSequence , int, int, android.text.Spanned, int, int)
Ignorando le cose di span che altre persone hanno affrontato, per gestire correttamente i suggerimenti del dizionario ho trovato che il codice seguente funziona.
La fonte cresce man mano che cresce il suggerimento, quindi dobbiamo guardare a quanti personaggi ci si aspetta che sostituiamo prima di restituire qualcosa.
Se non abbiamo caratteri non validi, restituire null in modo che si verifichi la sostituzione predefinita.
Altrimenti dobbiamo estrarre i caratteri validi dalla sottostringa che REALMENTE verrà posizionata nel 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;
}
};
per impedire le parole in edittext. crea una classe che puoi usare in qualsiasi momento.
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;
}
}
Per prima cosa aggiungi in strings.xml:
<string name="vin_code_mask">0123456789abcdefghjklmnprstuvwxyz</string>
XML :
android:digits="@string/vin_code_mask"
Codice in Kotlin:
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
}
Strano, ma funziona in modo strano sulla tastiera morbida dell'emulatore.
Avvertimento! Il codice seguente filtra tutte le lettere e gli altri simboli tranne le cifre per le tastiere del software. Sullo smartphone apparirà solo la tastiera digitale.
edit_text.keyListener = DigitsKeyListener.getInstance(context.getString(R.string.vin_code_mask))
Ho anche di solito impostato maxLength, filters, inputType.
Questo è un vecchio thread, ma le soluzioni proposte hanno tutti problemi (a seconda del dispositivo / versione Android / Tastiera).
APPROCCIO DIVERSO
Quindi alla fine sono andato con un approccio diverso, invece di usare l' InputFilterimplementazione problematica, sto usando TextWatchere TextChangedListeneril EditText.
CODICE COMPLETO (ESEMPIO)
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
}
});
Il motivo InputFilternon è una buona soluzione in Android perché dipende dall'implementazione della tastiera. L'ingresso della tastiera viene filtrato prima che l'input venga passato a EditText. Ma poiché alcune tastiere hanno implementazioni diverse per l' InputFilter.filter()invocazione, questo è problematico.
D'altra parte TextWatchernon si preoccupa dell'implementazione della tastiera, ci consente di creare una soluzione semplice ed essere sicuri che funzionerà su tutti i dispositivi.
onTextChangedbisogno semplicemente di un public voidfronte in esso.
Ho fatto qualcosa del genere per renderlo semplice:
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 ]"), "")
}
})
In questo modo stiamo sostituendo tutti i caratteri indesiderati nella nuova parte della stringa di origine con una stringa vuota.
La edit_textvariabile è l' EditTextoggetto a cui ci riferiamo.
Il codice è scritto in kotlin.
E 'possibile utilizzare setOnKeyListener. In questo metodo, possiamo personalizzare l'input edittext!
È così che ho creato il filtro per il campo Nome in Modifica testo (la prima lettera è MAIUSCOLO e consente solo uno spazio dopo ogni parola.
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)});
}
Ho la stessa risposta in Kotlin:
/**
* 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
}
}
}
e ottieni la classe con una funzione Extension:
fun EditText.filterByDataType(filterType: Int) {
this.filters = arrayOf<InputFilter>(InputFilterAlphanumeric(filterType))
}