背景
最近項目要求可以輸入體重,而且小數點是自動輸入的,不需要用戶輸入。
這樣就帶來一個問題,體重的整數位可以是兩位,比如 60.5,也可以是三位,比如 100.5,如果單位是 g,也可以是四位,比如 1000.5。
那么小數點自動輸入的話,是在第二位整數后面輸入?還是在第三第四位整數后面輸入?
這個根本沒有辦法判斷!
所以 UX 提出了一個方案:小數點固定為兩位,輸入時從右邊開始輸入。
舉個例子:如果想輸入 60.55 的體重的時候
輸入6 → 0.06
輸入0 → 0.60
輸入5 → 6.05
輸入5 → 60.55
這樣的話我要輸入 66,豈不是要輸入 6600 ?這樣 Usability 真的好么?
后來在中石化圈存加油卡的時候,發現金額的輸入規則也是從右邊開始輸入的,我要圈存 500 元,輸入了 50000。看來是我孤陋寡聞了(=?Д?=)
自定義 EditText
為了達到從右邊開始輸入的效果,我們來自定義 EditText。
直接上代碼吧(講解請看代碼中的注釋)
/**
* 從右邊開始輸入的 EditText
*/
public class InputFromEndDecimalEditText extends AppCompatEditText {
// 整數位位數
private int integerLength;
// 小數位位數
private int decimalLength;
public InputFromEndDecimalEditText(Context context) {
super(context);
init(context, null);
}
public InputFromEndDecimalEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public InputFromEndDecimalEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.InputFromEndDecimalEditText);
integerLength = a.getInteger(R.styleable.InputFromEndDecimalEditText_integerLength, 0);
decimalLength = a.getInteger(R.styleable.InputFromEndDecimalEditText_decimalLength, 0);
a.recycle();
// 限制 EditText 的輸入長度
setFilters(new InputFilter[]{new InputFilter.LengthFilter(integerLength + decimalLength + 1)});
// 光標不可見(因為要控制光標始終在最后,所以不讓用戶看見光標)
setCursorVisible(false);
setTextChangeListener();
}
private void setTextChangeListener() {
addTextChangedListener(new SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 利用正則表達式來判斷數字的格式是否達到要求
if (!TextUtils.isEmpty(s) &&
!s.toString().matches("^(\\d{1," + integerLength + "}.\\d{" + decimalLength + "})$")) {
// 去掉非數字,也就是小數點
Integer userInputWithoutDecimalPoint = Integer.valueOf(s.toString().replaceAll("[^\\d]", ""));
if (count == 0 && userInputWithoutDecimalPoint == 0) {
// 刪除之后為 0 的話,全部刪除
setText(null);
} else {
StringBuilder userInputBuilder = new StringBuilder(userInputWithoutDecimalPoint.toString());
// 補 0
while (userInputBuilder.length() <= decimalLength) {
userInputBuilder.insert(0, '0');
}
// 插入小數點
userInputBuilder.insert(userInputBuilder.length() - decimalLength, '.');
setText(userInputBuilder.toString());
moveCursorToEnd();
}
}
}
});
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
if (getText() != null && (selStart != getText().length() || selEnd != getText().length())) {
moveCursorToEnd();
return;
}
super.onSelectionChanged(selStart, selEnd);
}
private void moveCursorToEnd() {
setSelection(getText().length());
}
}
XML 中的用法如下
<com.teletian.inputfromenddecimaledittext.InputFromEndDecimalEditText
android:id="@+id/edit_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:gravity="right"
android:hint="000.00"
android:inputType="numberDecimal"
android:textColor="@android:color/holo_blue_dark"
android:textColorHint="@android:color/darker_gray"
app:decimalLength="2"
app:integerLength="3" />
沒有輸入的時候顯示 hint,提示用戶有幾位整數,有幾位小數。
為了把 hint 和正常的數值區分開,我們設置了 textColor 和 textColorHint 為不同的顏色。
以上代碼還有個小小的問題:
如果數值是以 double 類型存在數據庫中的,那么 66.00 會變成 66.0。那么下次顯示到 EditText 的時候就會變成 6.60。
為了解決這個問題,我們讓傳入到 EditText 的值變換成指定的小數位。
只要復寫 EditText 的 setText 方法就行了
@Override
public void setText(CharSequence text, BufferType type) {
DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setMinimumIntegerDigits(1);
decimalFormat.setMaximumIntegerDigits(integerLength);
decimalFormat.setMinimumFractionDigits(decimalLength);
decimalFormat.setMaximumFractionDigits(decimalLength);
super.setText(TextUtils.isEmpty(text) ?
text : decimalFormat.format(Double.valueOf(text.toString())), type);
}
源碼
https://github.com/teletian/Android/tree/master/InputFromEndDecimalEditText