Saya tahu sudah ada jutaan jawaban untuk ini, dengan satu jawaban diterima. Namun, ada banyak bug dalam jawaban yang diterima dan sebagian besar sisanya hanya memperbaiki satu (atau mungkin dua), tanpa memperluas ke semua kasus penggunaan yang mungkin.
Jadi pada dasarnya saya mengkompilasi sebagian besar perbaikan bug yang disarankan dalam jawaban dukungan serta menambahkan metode untuk memungkinkan input nomor yang terus menerus di luar rentang ke arah 0 (jika rentang tidak mulai dari 0), setidaknya sampai yakin bahwa itu tidak lagi berada dalam jangkauan. Karena harus jelas, ini adalah satu-satunya waktu yang benar-benar menyebabkan masalah dengan banyak solusi lain.
Inilah solusinya:
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("");
}
}
}
Perhatikan bahwa beberapa kasus input tidak mungkin untuk menangani "secara aktif" (yaitu, karena pengguna memasukkannya), jadi kita harus mengabaikannya dan menanganinya setelah pengguna selesai mengedit teks.
Begini cara Anda menggunakannya:
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);
Sekarang, ketika pengguna mencoba mengetikkan angka lebih dekat ke 0 daripada rentang yang diizinkan, salah satu dari dua hal akan terjadi:
Jika min
dan max
memiliki jumlah digit yang sama, mereka tidak akan diizinkan untuk memasukkannya sama sekali begitu mereka mencapai digit terakhir.
Jika angka di luar rentang dibiarkan di bidang ketika teks kehilangan fokus, maka secara otomatis akan disesuaikan dengan batas terdekat.
Dan tentu saja, pengguna tidak akan pernah diizinkan untuk memasukkan nilai lebih jauh dari 0 daripada rentang memungkinkan, juga tidak mungkin untuk nomor seperti itu untuk "tidak sengaja" berada di bidang teks karena alasan ini.
Masalah Dikenal?)
- Ini hanya berfungsi jika
EditText
kehilangan fokus ketika pengguna selesai dengan itu.
Pilihan lain adalah membersihkan ketika pengguna menekan tombol "selesai" / kembali, tetapi dalam banyak atau bahkan kebanyakan kasus, ini menyebabkan kehilangan fokus.
Namun, menutup keyboard lunak tidak akan secara otomatis membatalkan fokus elemen. Saya yakin 99,99% pengembang Android menginginkannya (dan bahwa penanganan fokus pada EditText
elemen kurang dari rawa pada umumnya), tetapi sampai saat ini belum ada fungsi bawaan untuk itu. Metode termudah yang saya temukan untuk mengatasi hal ini, jika perlu, adalah memperluas EditText
sesuatu seperti ini:
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);
}
}
Ini akan "mengelabui" filter untuk membersihkan input meskipun pandangan belum benar - benar kehilangan fokus. Jika pandangan terjadi kemudian kehilangan fokus sendiri, sanitasi input akan memicu lagi, tetapi tidak ada yang akan berubah karena sudah diperbaiki.
Penutupan
Wah. Itu banyak. Apa yang awalnya tampak seperti itu akan menjadi masalah yang cukup mudah dan berakhir dengan mengungkap banyak potongan vanilla Android yang jelek (setidaknya di Jawa). Dan sekali lagi, Anda hanya perlu menambahkan pendengar dan memperpanjang EditText
jika rentang Anda tidak termasuk 0 dalam beberapa cara. (Dan secara realistis, jika rentang Anda tidak termasuk 0 tetapi mulai dari 1 atau -1, Anda juga tidak akan mengalami masalah.)
Sebagai catatan terakhir, ini hanya berfungsi untuk int . Tentu saja ada cara untuk mengimplementasikannya agar bekerja dengan desimal ( double
, float
), tetapi karena saya maupun penanya semula tidak membutuhkan hal itu, saya tidak ingin terlalu mendalam. Akan sangat mudah untuk hanya menggunakan pemfilteran pasca-penyelesaian bersama dengan baris berikut:
// 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;
Anda hanya perlu mengubah dari int
menjadi float
(atau double
), mengizinkan penyisipan satu .
(atau ,
, tergantung pada negara?), Dan parse sebagai salah satu tipe desimal alih-alih sebuah int
.
Itu menangani sebagian besar pekerjaan, jadi itu akan bekerja sangat mirip.