Skip to content

Commit 8eabdaa

Browse files
committed
Merge branch 'develop'
2 parents 30a501d + b9ed1a1 commit 8eabdaa

File tree

4 files changed

+159
-21
lines changed

4 files changed

+159
-21
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ editText.setShowThousandsSeparator(true);//是否显示千位分隔符`,`
131131
editText.setAutoFillNumbers(true);//是否自动填充小数,如设置小数保留2位,那么当位数不足时会自动填充`0`
132132
editText.setDecimalLength(2);//小数位长度
133133
editText.setCurrencySymbol("¥");//设置货币符号,不设置即不显示
134+
editText.setMaxNumberValue(6666.66)//指定能输入的最大数值
134135
```
135136
#### Xml属性
136137
##### ClearEditText
@@ -161,6 +162,7 @@ editText.setCurrencySymbol("¥");//设置货币符号,不设置即不显示
161162
|fet_showThousandsSeparator|boolean|指定是否显示千位分隔符|
162163
|fet_autoFillNumbers|boolean|指定是否自动填充数字|
163164
|fet_autoFillNumbersTextColor|boolean|指定自动填充数字的文字颜色,不设置的话默认使用当前提示文字颜色|
165+
|fet_maxNumberValue|string|指定能输入的最大数值|
164166
#### 掩码
165167
`FormattedEditText` 在模式为`MODE_MASK``MODE_HINT`时,格式化样式中的以下字符具有特殊含义:
166168

README_EN.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ editText.setShowThousandsSeparator(true);
131131
editText.setAutoFillNumbers(true);
132132
editText.setDecimalLength(2);
133133
editText.setCurrencySymbol("¥");
134+
editText.setMaxNumberValue(6666.66)
134135
```
135136
#### Xml attr
136137
##### ClearEditText
@@ -161,6 +162,7 @@ editText.setCurrencySymbol("¥");
161162
|fet_showThousandsSeparator|boolean|Specify whether to display thousands separator|
162163
|fet_autoFillNumbers|boolean|Specify whether to automatically fill in numbers|
163164
|fet_autoFillNumbersTextColor|boolean|Specify the text color of auto-filled numbers|
165+
|fet_maxNumberValue|string|Specify the maximum number value that can be entered|
164166
#### Mask
165167
`FormattedEditText` When the modes are `MODE_MASK` and` MODE_HINT`, the following characters in the formatting style have special meanings:
166168

core/src/main/java/me/dkzwm/widget/fet/MaskNumberEditText.java

Lines changed: 154 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@
2929
import android.os.Parcelable;
3030
import android.text.Editable;
3131
import android.text.InputFilter;
32+
import android.text.NoCopySpan;
3233
import android.text.Selection;
3334
import android.text.SpannableStringBuilder;
3435
import android.text.Spanned;
36+
import android.text.TextUtils;
3537
import android.text.TextWatcher;
3638
import android.text.style.ForegroundColorSpan;
3739
import android.util.AttributeSet;
40+
import android.util.Log;
41+
import androidx.annotation.CallSuper;
3842
import java.util.ArrayList;
3943
import java.util.List;
4044

@@ -60,6 +64,7 @@ public class MaskNumberEditText extends ClearEditText {
6064
private String mCurrencySymbol;
6165
private int mCurrencySymbolTextColor = -1;
6266
private int mDecimalLength = -1;
67+
private double mMaxNumberValue = -1;
6368
private boolean mAutoFillNumbers = false;
6469
private int mAutoFillNumbersTextColor = -1;
6570
private boolean mShowThousandsSeparator = true;
@@ -98,6 +103,17 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr) {
98103
setShowThousandsSeparator(
99104
ta.getBoolean(
100105
R.styleable.MaskNumberEditText_fet_showThousandsSeparator, true));
106+
String maxValue = ta.getString(R.styleable.MaskNumberEditText_fet_maxNumberValue);
107+
if (maxValue != null) {
108+
try {
109+
double max = Double.parseDouble(maxValue);
110+
setMaxNumberValue(max);
111+
} catch (NumberFormatException e) {
112+
Log.e(
113+
getClass().getSimpleName(),
114+
"The value of attribute fet_maxNumberValue is not a Double");
115+
}
116+
}
101117
} finally {
102118
ta.recycle();
103119
}
@@ -109,7 +125,7 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr) {
109125
}
110126
return;
111127
}
112-
formatNumber(text);
128+
formatEditable(text);
113129
Selection.setSelection(text, text.length());
114130
}
115131

@@ -128,6 +144,29 @@ public void removeTextChangedListener(TextWatcher watcher) {
128144
}
129145
}
130146

147+
@Override
148+
@CallSuper
149+
public void setFilters(InputFilter[] filters) {
150+
if (filters == null) {
151+
throw new IllegalArgumentException("filters can not be null");
152+
}
153+
boolean havingFilter = false;
154+
for (InputFilter filter : filters) {
155+
if (filter instanceof MaxNumberValueFilter) {
156+
havingFilter = true;
157+
break;
158+
}
159+
}
160+
if (!havingFilter) {
161+
InputFilter[] replaceFilters = new InputFilter[filters.length + 1];
162+
replaceFilters[0] = new MaxNumberValueFilter();
163+
System.arraycopy(filters, 0, replaceFilters, 1, filters.length);
164+
super.setFilters(replaceFilters);
165+
return;
166+
}
167+
super.setFilters(filters);
168+
}
169+
131170
public String getCurrencySymbol() {
132171
return mCurrencySymbol;
133172
}
@@ -180,6 +219,18 @@ public void setShowThousandsSeparator(boolean showThousandsSeparator) {
180219
mShowThousandsSeparator = showThousandsSeparator;
181220
}
182221

222+
public double getMaxNumberValue() {
223+
return mMaxNumberValue;
224+
}
225+
226+
public void setMaxNumberValue(double maxNumberValue) {
227+
if (maxNumberValue < 0) {
228+
throw new IllegalArgumentException(
229+
"maxNumberValue must be greater than or equal to zero");
230+
}
231+
mMaxNumberValue = maxNumberValue;
232+
}
233+
183234
public String getRealNumber() {
184235
return getRealNumber(false);
185236
}
@@ -189,10 +240,21 @@ private String getRealNumber(boolean saved) {
189240
if (editable == null || editable.length() == 0) {
190241
return "";
191242
}
192-
SpannableStringBuilder value = new SpannableStringBuilder(editable);
193-
clearPlaceholders(value);
194-
final String realText = value.toString();
195-
value.clear();
243+
SpannableStringBuilder builder = new SpannableStringBuilder(editable);
244+
String number = getRealNumber(builder, saved);
245+
if (!saved && TextUtils.isEmpty(number)) {
246+
return "0";
247+
}
248+
return number;
249+
}
250+
251+
private String getRealNumber(Editable editable, boolean saved) {
252+
if (editable == null || editable.length() == 0) {
253+
return "";
254+
}
255+
clearPlaceholders(editable);
256+
final String realText = editable.toString();
257+
editable.clear();
196258
if (!saved) {
197259
if (realText.length() > 0) {
198260
if (realText.charAt(realText.length() - 1) == DECIMAL_POINT_CHAR) {
@@ -242,7 +304,7 @@ private void sendAfterTextChanged(Editable s) {
242304
}
243305
}
244306

245-
private void formatNumber(final Editable editable) {
307+
private void formatEditable(final Editable editable) {
246308
mIsFormatted = true;
247309
final boolean filter = mFilterRestoreTextChangeEvent;
248310
super.removeTextChangedListener(mTextWatcher);
@@ -254,6 +316,24 @@ private void formatNumber(final Editable editable) {
254316
selectionEnd = Selection.getSelectionEnd(editable);
255317
editable.setSpan(SELECTION_SPAN, selectionStart, selectionEnd, Spanned.SPAN_MARK_MARK);
256318
}
319+
formatNumber(editable);
320+
if (!filter) {
321+
selectionStart = editable.getSpanStart(SELECTION_SPAN);
322+
selectionEnd = editable.getSpanEnd(SELECTION_SPAN);
323+
editable.removeSpan(SELECTION_SPAN);
324+
editable.setFilters(filters);
325+
Selection.setSelection(
326+
editable,
327+
Math.min(selectionStart, editable.length()),
328+
Math.min(selectionEnd, editable.length()));
329+
} else {
330+
editable.setFilters(filters);
331+
}
332+
mIsFormatted = false;
333+
super.addTextChangedListener(mTextWatcher);
334+
}
335+
336+
private void formatNumber(final Editable editable) {
257337
clearPlaceholders(editable);
258338
DecimalPointSpan[] spans = editable.getSpans(0, editable.length(), DecimalPointSpan.class);
259339
if (spans.length > 0) {
@@ -313,20 +393,6 @@ private void formatNumber(final Editable editable) {
313393
}
314394
}
315395
formatCurrencySymbol(editable);
316-
if (!filter) {
317-
selectionStart = editable.getSpanStart(SELECTION_SPAN);
318-
selectionEnd = editable.getSpanEnd(SELECTION_SPAN);
319-
editable.removeSpan(SELECTION_SPAN);
320-
editable.setFilters(filters);
321-
Selection.setSelection(
322-
editable,
323-
Math.min(selectionStart, editable.length()),
324-
Math.min(selectionEnd, editable.length()));
325-
} else {
326-
editable.setFilters(filters);
327-
}
328-
mIsFormatted = false;
329-
super.addTextChangedListener(mTextWatcher);
330396
}
331397

332398
private void formatCurrencySymbol(Editable editable) {
@@ -576,6 +642,73 @@ public void writeToParcel(Parcel out, int flags) {
576642
}
577643
}
578644

645+
private class MaxNumberValueFilter implements InputFilter {
646+
private SpannableStringBuilder mBuilder;
647+
648+
@Override
649+
public CharSequence filter(
650+
CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
651+
if (mRestoring || mIsFormatted) {
652+
return null;
653+
}
654+
if (mMaxNumberValue != -1) {
655+
String destString = dest.toString();
656+
Object[] spans = dest.getSpans(0, dest.length(), Object.class);
657+
if (mBuilder == null) {
658+
mBuilder = new SpannableStringBuilder();
659+
}
660+
resetDestSpanned(dest, destString, spans);
661+
if (dstart - dend != 0) {
662+
mBuilder.delete(dstart, dend);
663+
} else {
664+
String number = getRealNumber(mBuilder, false);
665+
if (isLarger(number)) {
666+
return "";
667+
}
668+
}
669+
int endIndex = end;
670+
for (int i = start; i < end; i++) {
671+
resetDestSpanned(dest, destString, spans);
672+
mBuilder.insert(dstart, source.subSequence(start, i + 1));
673+
formatNumber(mBuilder);
674+
String number = getRealNumber(mBuilder, false);
675+
if (isLarger(number)) {
676+
endIndex = i;
677+
break;
678+
}
679+
}
680+
mBuilder.clear();
681+
return source.subSequence(start, endIndex);
682+
}
683+
return null;
684+
}
685+
686+
private void resetDestSpanned(Spanned dest, String destString, Object[] spans) {
687+
mBuilder.clear();
688+
mBuilder.append(destString);
689+
for (Object obj : spans) {
690+
if (obj instanceof NoCopySpan) {
691+
continue;
692+
}
693+
mBuilder.setSpan(
694+
obj, dest.getSpanStart(obj), dest.getSpanEnd(obj), dest.getSpanFlags(obj));
695+
}
696+
}
697+
698+
private boolean isLarger(String number) {
699+
if (TextUtils.isEmpty(number)) {
700+
return mMaxNumberValue < 0;
701+
}
702+
try {
703+
double value = Double.parseDouble(number);
704+
return mMaxNumberValue < value;
705+
} catch (NumberFormatException e) {
706+
// ignored
707+
}
708+
return true;
709+
}
710+
}
711+
579712
private class MaskNumberTextWatcher implements TextWatcher {
580713
@Override
581714
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -592,7 +725,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
592725
}
593726
sendOnTextChanged(s, start, before, count);
594727
if (!mIsFormatted && s instanceof Editable) {
595-
formatNumber((Editable) s);
728+
formatEditable((Editable) s);
596729
}
597730
}
598731

core/src/main/res/values/styles.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@
3131
<attr name="fet_showThousandsSeparator" format="boolean" />
3232
<attr name="fet_autoFillNumbers" format="boolean" />
3333
<attr name="fet_autoFillNumbersTextColor" format="color" />
34+
<attr name="fet_maxNumberValue" format="string" />
3435
</declare-styleable>
3536
</resources>

0 commit comments

Comments
 (0)