Wiem, że jest już milion odpowiedzi na to pytanie, z których jedna została zaakceptowana. Jednak w zaakceptowanej odpowiedzi jest wiele błędów, a większość pozostałych po prostu naprawia jeden (lub może dwa) z nich, bez rozszerzania na wszystkie możliwe przypadki użycia.
Więc w zasadzie skompilowałem większość poprawek błędów sugerowanych w odpowiedziach wsparcia, a także dodałem metodę umożliwiającą ciągłe wprowadzanie liczb spoza zakresu w kierunku 0 (jeśli zakres nie zaczyna się od 0), przynajmniej do momentu pewny, że nie może już znajdować się w zasięgu. Aby było jasne, jest to jedyny przypadek, który naprawdę powoduje problemy z wieloma innymi rozwiązaniami.
Oto poprawka:
public class InputFilterIntRange implements InputFilter, View.OnFocusChangeListener {
private final int min, max;
public InputFilterIntRange(int min, int max) {
if (min > max) {
int mid = max;
max = min;
min = mid;
}
this.min = min;
this.max = max;
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String destString = dest.toString();
String inputString = destString.substring(0, dstart) + source.toString() + destString.substring(dstart);
if (inputString.equalsIgnoreCase("-") && min < 0) return null;
try {
int input = Integer.parseInt(inputString);
if (mightBeInRange(input))
return null;
} catch (NumberFormatException nfe) {}
return "";
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
if (v instanceof EditText) sanitizeValues((EditText) v);
}
}
private boolean mightBeInRange(int value) {
if (value >= 0 && value > max) return false;
if (value >= 0 && value >= min) return true;
if (value < 0 && value < min) return false;
if (value < 0 && value <= max) return true;
boolean negativeInput = value < 0;
if (numberOfDigits(min) == numberOfDigits(max)) {
if (!negativeInput) {
if (numberOfDigits(value) >= numberOfDigits(min) && value < min) return false;
} else {
if (numberOfDigits(value) >= numberOfDigits(max) && value > max) return false;
}
}
return true;
}
private int numberOfDigits(int n) {
return String.valueOf(n).replace("-", "").length();
}
private void sanitizeValues(EditText valueText) {
try {
int value = Integer.parseInt(valueText.getText().toString());
if (value < min) {
value = min;
valueText.setText(String.valueOf(value));
} else if (value > max) {
value = max;
valueText.setText(String.valueOf(value));
}
} catch (NumberFormatException nfe) {
valueText.setText("");
}
}
}
Zauważ, że niektóre przypadki wprowadzania danych są niemożliwe do obsłużenia "aktywnie" (tj. Gdy użytkownik je wprowadza), więc musimy je zignorować i zająć się nimi po zakończeniu edycji tekstu przez użytkownika.
Oto, jak możesz tego użyć:
EditText myEditText = findViewById(R.id.my_edit_text);
InputFilterIntRange rangeFilter = new InputFilterIntRange(25, 350);
myEditText.setFilters(new InputFilter[]{rangeFilter});
myEditText.setOnFocusChangeListener(rangeFilter);
Teraz, gdy użytkownik spróbuje wpisać liczbę bliższą 0 niż pozwala zakres, nastąpi jedna z dwóch rzeczy:
Jeśli min
i max
mają taką samą liczbę cyfr, nie będą mogli w ogóle jej wprowadzić, gdy dojdą do ostatniej cyfry.
Jeśli liczba spoza zakresu pozostanie w polu, gdy tekst straci ostrość, zostanie automatycznie dopasowany do najbliższej granicy.
Oczywiście, użytkownik nigdy nie będzie mógł wprowadzić wartości większej od 0, niż pozwala na to zakres, ani też nie jest możliwe, aby taka liczba „przypadkowo” znalazła się w polu tekstowym z tego powodu.
Znane problemy?)
- Działa to tylko wtedy, gdy
EditText
straci fokus, gdy użytkownik skończy z nim.
Inną opcją jest odkażanie, gdy użytkownik naciśnie klawisz „done” / Return, ale w wielu lub nawet większości przypadków powoduje to utratę koncentracji.
Jednak zamknięcie miękkiej klawiatury nie spowoduje automatycznego usunięcia fokusu elementu. Jestem pewien, że 99,99% programistów Androida by tego życzyło (a skupienie się na EditText
elementach było ogólnie mniejszym grzęzawiskiem), ale jak dotąd nie ma wbudowanej funkcjonalności. Najłatwiejszą metodą obejścia tego problemu, jeśli zajdzie taka potrzeba, jest rozszerzenie EditText
czegoś takiego:
public class EditTextCloseEvent extends AppCompatEditText {
public EditTextCloseEvent(Context context) {
super(context);
}
public EditTextCloseEvent(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditTextCloseEvent(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
for (InputFilter filter : this.getFilters()) {
if (filter instanceof InputFilterIntRange)
((InputFilterIntRange) filter).onFocusChange(this, false);
}
}
return super.dispatchKeyEvent(event);
}
}
Spowoduje to „oszukanie” filtru w celu oczyszczenia wejścia, nawet jeśli widok w rzeczywistości nie stracił ostrości. Jeśli zdarzy się, że widok sam się straci skupienie, ponownie uruchomi się sanacja wejściowa, ale nic się nie zmieni, ponieważ zostało już naprawione.
Zamknięcie
Uff. To było dużo. To, co początkowo wydawało się być dość banalnie łatwym problemem, zakończyło się odkryciem wielu małych brzydkich kawałków waniliowego Androida (przynajmniej w Javie). I jeszcze raz musisz tylko dodać odbiornik i rozszerzyć zakres, EditText
jeśli twój zakres nie zawiera w jakiś sposób 0. (I realistycznie, jeśli twój zakres nie obejmuje 0, ale zaczyna się od 1 lub -1, również nie napotkasz problemów).
Na koniec uwaga, to działa tylko dla int . Z pewnością jest sposób na zaimplementowanie go do pracy z liczbami dziesiętnymi ( double
, float
), ale ponieważ ani ja, ani pierwotny pytający nie potrzebujemy tego, nie chcę szczególnie zagłębiać się w to wszystko. Byłoby bardzo łatwo po prostu użyć filtrowania po ukończeniu wraz z następującymi wierszami:
if (value >= 0 && value > max) return false;
if (value >= 0 && value >= min) return true;
if (value < 0 && value < min) return false;
if (value < 0 && value <= max) return true;
Musiałbyś tylko zmienić z int
na float
(lub double
), zezwolić na wstawienie pojedynczego .
(lub ,
, w zależności od kraju?) I przeanalizować jako jeden z typów dziesiętnych zamiast int
.
To i tak obsługuje większość pracy, więc działałoby bardzo podobnie.