Ich weiß, dass es bereits eine Million Antworten darauf gibt, von denen eine akzeptiert wird. Die akzeptierte Antwort enthält jedoch zahlreiche Fehler, und die meisten anderen beheben einfach einen (oder möglicherweise zwei) von ihnen, ohne auf alle möglichen Anwendungsfälle auszuweiten.
Daher habe ich im Grunde genommen die meisten in den Support-Antworten vorgeschlagenen Fehlerkorrekturen zusammengestellt und eine Methode hinzugefügt, mit der Zahlen außerhalb des Bereichs kontinuierlich in Richtung 0 eingegeben werden können (wenn der Bereich nicht bei 0 beginnt), zumindest bis dies der Fall ist sicher, dass es nicht mehr im Bereich sein kann. Denn um klar zu sein, ist dies das einzige Mal, dass bei vielen anderen Lösungen wirklich Probleme auftreten.
Hier ist das Update:
public class InputFilterIntRange implements InputFilter, View.OnFocusChangeListener {
private final int min, max;
public InputFilterIntRange(int min, int max) {
if (min > max) {
// Input sanitation for the filter itself
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) {
// Determine the final string that will result from the attempted input
String destString = dest.toString();
String inputString = destString.substring(0, dstart) + source.toString() + destString.substring(dstart);
// Don't prevent - sign from being entered first if min is negative
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) {
// Since we can't actively filter all values
// (ex: range 25 -> 350, input "15" - could be working on typing "150"),
// lock values to range after text loses focus
if (!hasFocus) {
if (v instanceof EditText) sanitizeValues((EditText) v);
}
}
private boolean mightBeInRange(int value) {
// Quick "fail"
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 min and max have the same number of digits, we can actively filter
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 is outside the range, bring it up/down to the endpoint
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("");
}
}
}
Beachten Sie, dass einige Eingabefälle nicht "aktiv" behandelt werden können (dh wenn der Benutzer sie eingibt). Daher müssen wir sie ignorieren und behandeln, nachdem der Benutzer den Text bearbeitet hat.
So können Sie es verwenden:
EditText myEditText = findViewById(R.id.my_edit_text);
InputFilterIntRange rangeFilter = new InputFilterIntRange(25, 350);
myEditText.setFilters(new InputFilter[]{rangeFilter});
// Following line is only necessary if your range is like [25, 350] or [-350, -25].
// If your range has 0 as an endpoint or allows some negative AND positive numbers,
// all cases will be handled pre-emptively.
myEditText.setOnFocusChangeListener(rangeFilter);
Wenn der Benutzer nun versucht, eine Zahl einzugeben, die näher an 0 liegt, als es der Bereich zulässt, geschieht eines von zwei Dingen:
Wenn min
und max
die gleiche Anzahl von Ziffern haben, dürfen sie diese nicht mehr eingeben, sobald sie die letzte Ziffer erreicht haben.
Wenn eine Zahl außerhalb des Bereichs im Feld verbleibt, wenn der Text den Fokus verliert, wird er automatisch an die nächstgelegene Grenze angepasst.
Und natürlich darf der Benutzer niemals einen Wert eingeben, der weiter von 0 entfernt ist, als es der Bereich zulässt, und es ist aus diesem Grund auch nicht möglich, dass sich eine solche Zahl "versehentlich" im Textfeld befindet.
Bekannte Probleme?)
- Dies funktioniert nur, wenn der
EditText
Fokus verloren geht, wenn der Benutzer damit fertig ist.
Die andere Option ist die Bereinigung, wenn der Benutzer die Taste "Fertig" / "Zurück" drückt. In vielen oder sogar den meisten Fällen führt dies jedoch zu einem Fokusverlust.
Durch Schließen der Softtastatur wird das Element jedoch nicht automatisch unscharf. Ich bin mir sicher, dass 99,99% der Android-Entwickler dies wünschen (und dass der Fokus auf EditText
Elemente im Allgemeinen weniger ein Sumpf war), aber bis jetzt gibt es keine eingebaute Funktionalität dafür. Die einfachste Methode, die ich gefunden habe, um dies zu umgehen, besteht darin, EditText
Folgendes zu erweitern :
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);
}
}
Dadurch wird der Filter dazu gebracht, die Eingabe zu bereinigen, obwohl die Ansicht den Fokus nicht wirklich verloren hat. Wenn die Ansicht später selbst den Fokus verliert, wird die Eingabesanierung erneut ausgelöst, aber es ändert sich nichts, da sie bereits behoben wurde.
Schließen
Wütend. Das war viel. Was ursprünglich so aussah, als wäre es ein ziemlich trivial einfaches Problem, und am Ende wurden viele kleine hässliche Teile von Vanille-Android (zumindest in Java) entdeckt. Und noch einmal, Sie müssen den Listener nur hinzufügen und erweitern, EditText
wenn Ihr Bereich in keiner Weise 0 enthält. (Und realistisch gesehen, wenn Ihr Bereich nicht 0 enthält, sondern bei 1 oder -1 beginnt, werden Sie auch nicht auf Probleme stoßen.)
Als letzte Anmerkung funktioniert dies nur für Ints . Es gibt sicherlich eine Möglichkeit, es so zu implementieren, dass es mit Dezimalstellen ( double
, float
) funktioniert , aber da weder ich noch der ursprüngliche Fragesteller dies benötigen, möchte ich nicht besonders tief darauf eingehen. Es wäre sehr einfach, die Filterung nach Abschluss zusammen mit den folgenden Zeilen zu verwenden:
// Quick "fail"
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;
Sie müssten nur von int
zu float
(oder double
) wechseln , das Einfügen eines einzelnen .
(oder ,
, je nach Land?) Zulassen und als einen der Dezimaltypen anstelle von a analysieren int
.
Das erledigt sowieso den größten Teil der Arbeit, also würde es sehr ähnlich funktionieren.