ฉันรู้ว่ามีคำตอบนับล้านสำหรับสิ่งนี้อยู่แล้วพร้อมกับได้รับการยอมรับ อย่างไรก็ตามมีข้อบกพร่องมากมายในคำตอบที่ได้รับการยอมรับและส่วนที่เหลือส่วนใหญ่เพียงแก้ไขหนึ่ง (หรืออาจสอง) ของพวกเขาโดยไม่ขยายไปใช้ทุกกรณีที่เป็นไปได้
ดังนั้นฉันจึงรวบรวมการแก้ไขข้อบกพร่องส่วนใหญ่ที่แนะนำในคำตอบการสนับสนุนรวมถึงการเพิ่มวิธีการอนุญาตให้ใส่ตัวเลขอย่างต่อเนื่องนอกช่วงในทิศทางของ 0 (ถ้าช่วงไม่เริ่มที่ 0) อย่างน้อยก็จนกว่ามันจะ บางอย่างไม่สามารถอยู่ในช่วงได้อีกต่อไป เพราะเพื่อความชัดเจนนี่เป็นครั้งเดียวที่ทำให้เกิดปัญหากับโซลูชั่นอื่น ๆ มากมาย
นี่คือการแก้ไข:
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("");
}
}
}
โปรดทราบว่าบางกรณีป้อนข้อมูลเป็นไปไม่ได้ที่จะจัดการ "อย่างแข็งขัน" (เช่นในขณะที่ผู้ใช้กำลังป้อนข้อมูล) ดังนั้นเราจึงต้องละเว้นและจัดการกับพวกเขาหลังจากที่ผู้ใช้ทำการแก้ไขข้อความเสร็จแล้ว
นี่คือวิธีที่คุณจะใช้:
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);
ตอนนี้เมื่อผู้ใช้พยายามพิมพ์ตัวเลขที่ใกล้เคียงกับ 0 มากกว่าช่วงที่อนุญาตหนึ่งในสองสิ่งจะเกิดขึ้น:
หากmin
และmax
มีจำนวนตัวเลขเท่ากันพวกเขาจะไม่ได้รับอนุญาตให้ป้อนข้อมูลเลยเมื่อพวกเขาไปถึงตัวเลขสุดท้าย
หากตัวเลขที่อยู่นอกช่วงนั้นเหลืออยู่ในฟิลด์เมื่อข้อความสูญเสียโฟกัสจะถูกปรับเป็นขอบเขตที่ใกล้เคียงที่สุดโดยอัตโนมัติ
และแน่นอนว่าผู้ใช้จะไม่ได้รับอนุญาตให้ป้อนค่าที่อยู่ห่างจาก 0 มากกว่าช่วงที่อนุญาตและไม่สามารถเป็นไปได้สำหรับจำนวนเช่นนั้นถึง "ตั้งใจ" ในช่องข้อความด้วยเหตุนี้
ปัญหาที่ทราบแล้ว
- วิธีนี้จะใช้งานได้ก็ต่อ
EditText
เมื่อผู้ใช้เสียโฟกัสไปแล้ว
ตัวเลือกอื่น ๆ คือการฆ่าเชื้อเมื่อผู้ใช้กดปุ่ม "เสร็จสิ้น" / ส่งคืน แต่ในกรณีส่วนใหญ่หรือส่วนใหญ่สิ่งนี้ทำให้สูญเสียการโฟกัสตลอดเวลา
อย่างไรก็ตามการปิดคีย์บอร์ดอ่อนจะไม่ยกเลิกการโฟกัสไปที่องค์ประกอบโดยอัตโนมัติ ฉันมั่นใจว่า 99.99% ของนักพัฒนา Android ต้องการ (และการจัดการกับEditText
องค์ประกอบนั้นโดยทั่วไปแล้วจะมีจำนวนน้อยกว่าของบึง) แต่ ณ ขณะนี้ยังไม่มีฟังก์ชั่นในตัว วิธีที่ง่ายที่สุดที่ฉันพบว่าสามารถหลีกเลี่ยงได้ถ้าคุณต้องการคือการขยายEditText
สิ่งนี้:
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);
}
}
นี้จะ "เคล็ดลับ" กรองเข้าไปฆ่าเชื้ออินพุตแม้ว่ามุมมองได้ไม่จริงหายไปโฟกัส หากมุมมองเกิดขึ้นในภายหลังจะสูญเสียการโฟกัสด้วยตัวเองการสุขาภิบาลอินพุตจะทริกเกอร์อีกครั้ง แต่จะไม่มีอะไรเปลี่ยนแปลงเนื่องจากมันได้รับการแก้ไขแล้ว
ปิด
ต๊าย นั่นเป็นจำนวนมาก สิ่งที่ดูเหมือนว่าในตอนแรกมันจะเป็นปัญหาที่ง่ายนิดเดียวที่จบลงด้วยการเปิดเผยวานิลลาชิ้นเล็ก ๆ น้อย ๆ ที่น่าเกลียดของ Android (อย่างน้อยใน Java) และอีกครั้งคุณจะต้องเพิ่มผู้ฟังและขยายEditText
ถ้าช่วงของคุณไม่รวม 0 อย่างใด (และแนบเนียนถ้าช่วงของคุณไม่รวม 0 แต่เริ่มต้นที่ 1 หรือ -1 คุณจะไม่ประสบปัญหา)
ในฐานะที่เป็นบันทึกล่าสุดสิ่งนี้ใช้ได้กับintsเท่านั้น มีวิธีที่จะนำมันมาใช้เพื่อทำงานกับทศนิยม ( double
, float
) แต่เนื่องจากทั้งตัวฉันและผู้ถามดั้งเดิมไม่มีความต้องการสิ่งนั้นฉันจึงไม่ต้องการให้ลึกลงไปในนั้น มันจะง่ายมากที่จะใช้ตัวกรองหลังเสร็จสิ้นพร้อมกับบรรทัดต่อไปนี้:
// 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;
คุณจะต้องเปลี่ยนจากint
เป็นfloat
(หรือdouble
) อนุญาตการแทรกเดี่ยว.
(หรือ,
ขึ้นอยู่กับประเทศ?) และแยกเป็นทศนิยมประเภทใดประเภทหนึ่งแทนint
และแยกเป็นหนึ่งในประเภททศนิยมแทน
ที่จัดการงานส่วนใหญ่อย่างไรก็ตามมันจะทำงานคล้ายกันมาก