项目中需要用EditText来输入金额,并且默认需要前置”¥”效果如下:
很自然的想到用一个TextView与一个EditText来实现。所以布局文件如下:
"wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/cash_input_tv"
android:id="@+id/money_rmb_pre_tv"
android:text="@string/money_rmb_pre"
android:textColor="@color/reason_dialog_title"
android:textStyle="bold"
android:textSize="16sp"
android:visibility="visible"/>
"wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="@null"
android:id="@+id/cash_input_tv"
android:inputType="numberDecimal"
android:textColorHint="@color/text_gray4"
android:hint="@string/dinner_cash_input_info"
android:textColor="@color/reason_dialog_title"
android:textStyle="bold"
android:textSize="16sp"
android:visibility="visible"/>
好,现在坑来了
因为EditText设置hint,那么初始化的时候虽然我们设置了android:layout_width=”wrap_content”,其初始宽度是刚好包裹hint的宽度,当我们在EditText中输入的字符串小于hint的长度时,其宽度不再完全包裹我们输入的字符串,会留有空白。只有当我们输入的字符串长度大于hint时,才会完全包裹住。我们改一下EditText的背景,效果如下:
可以看到,不能实现我们最开始给出的效果,那怎么办呢?我们自然想到,我们只用一个 EditText来实现,在每次输入的前面”添加”一个”¥”即可。为此我们需要修改布局文件
"wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/cash_input_tv"
android:id="@+id/money_rmb_pre_tv"
android:text="@string/money_rmb_pre"
android:textColor="@color/reason_dialog_title"
android:textStyle="bold"
android:textSize="16sp"
android:visibility="gone"/>
"wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="#055493"
android:id="@+id/cash_input_tv"
android:inputType="numberDecimal"
android:textColorHint="@color/text_gray4"
android:hint="@string/dinner_cash_input_info"
android:textColor="@color/reason_dialog_title"
android:textStyle="bold"
android:textSize="16sp"
android:visibility="visible"/>
然后,在代码中对EditText添加TextChangedListener,代码如下:
cashInputTv.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().length() > 0) {
String res = s.toString().substring(1, s.toString().length());
Log.d("text", res);
if (!TextUtils.isEmpty(res)) {
try {
inputAmount = new BigDecimal(res);
} catch (Exception e) {
e.printStackTrace();
}
} else {
inputAmount = new BigDecimal(0);
}
updateUI();
}
}
@Override
public void afterTextChanged(Editable s) {
if (!s.toString().contains("¥")) {
s.insert(0, "¥");
} else {
if (s.toString().equals("¥")) { //只有一个"¥"
cashInputTv.setText("");
}
}
}
});
基本思想是在输入内容改变后再0处添加一个”¥”。并且当只有一个”¥”时,设置为空。
好,现在运行代码,发现程序直接卡死,通过log信息,可以看到内存溢出了。那是因为又跳到另一个坑里面了。
我们在onTextChanged(),afterTextChanged()中对输入的内容,CharSequence s,Editable s做更改,或者直接设置 EditText内容,setText()时,需要先移除TextChangedListener监听。否则会陷入死循环,这是因为setText()在 EditText addTextChangedListener后会回调TextWatcher中的几个方法。具体可以看源代码
更改代码如下:
cashInputTv.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().length() > 0) {
String res = s.toString().substring(1, s.toString().length());
Log.d("text", res);
if (!TextUtils.isEmpty(res)) {
try {
inputAmount = new BigDecimal(res);
} catch (Exception e) {
e.printStackTrace();
}
} else {
inputAmount = new BigDecimal(0);
}
updateUI();
}
}
@Override
public void afterTextChanged(Editable s) {
cashInputTv.removeTextChangedListener(this);
if (!s.toString().contains("¥")) {
s.insert(0, "¥");
} else {
if (s.toString().equals("¥")) { //只有一个"¥"
cashInputTv.setText("");
}
}
cashInputTv.addTextChangedListener(this);
}
});
我们先移除,更改完之后再添加。现在我们就可以实现我们想要的效果了。
但是,到这里还没有完,因为是金额输入,我们需要限制输入的整数及小数位数,如果我们设置了普通的限制整数,及小数位数的InputFilter,我们发现,EditText无法显示”¥”。这是因为我们的InputFilter需要更改,需要包含”¥”
这里我们直接给出一个Filter了,包括整数位8位,小数位2位。这个也是我在网上抄的,自己修改了下。
public class CashierInputFilter implements InputFilter {
private Pattern mPattern;
//输入的最大金额
private final int MAX_VALUE = Integer.MAX_VALUE;
//小数点后的2位数
private final int POINTER_AFTER_LENGTH = 2;
//小数点前的9位数
private int POINTER_BEFORE_LENGTH = 9;
private final String POINTER = ".";
private static final String ZERO = "0";
public CashierInputFilter() {
mPattern = Pattern.compile("(¥|[0-9]|\\.)*");
}
public CashierInputFilter(int _maxLength) {
mPattern = Pattern.compile("(¥|[0-9]|\\.)*");
POINTER_BEFORE_LENGTH= _maxLength - POINTER_AFTER_LENGTH - 1;
}
/**
* @param source 新输入的字符串
* @param start 新输入的字符串起始下标,一般为0
* @param end 新输入的字符串终点下标,一般为source长度-1
* @param dest 输入之前文本框内容
* @param dstart 原内容起始坐标,一般为0
* @param dend 原内容终点坐标,一般为dest长度-1
* @return 输入内容
*/
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
String sourceText = source.toString();
String destText = dest.toString();
Log.d("text","source: " + sourceText);
Log.d("text","dest: " + destText);
//验证删除等按键
if (TextUtils.isEmpty(sourceText)) {
return "";
}
Matcher matcher = mPattern.matcher(source);
//已经输入小数点的情况下,只能输入数字
if(destText.contains(POINTER)) {
if (!matcher.matches()) {
return "";
} else {
if (POINTER.equals(source)) { //只能输入一个小数点
return "";
}
}
int index = destText.indexOf(POINTER);
//验证小数点精度,保证小数点后只能输入两位
int afterLength = dend - index;
// 输入后,修改控制不了,//验证小数点精度,保证小数点前位数
if(dstart == POINTER_BEFORE_LENGTH && index == POINTER_BEFORE_LENGTH)
{
return dest.subSequence(dstart, dend);
}
//验证小数点精度,保证小数点后只能输入两位
if (afterLength > POINTER_AFTER_LENGTH) {
return dest.subSequence(dstart, dend);
}
} else {
//没有输入小数点的情况下,只能输入小数点和数字,但首位不能输入小数点和0
if (!matcher.matches()) {
return "";
} else {
if (POINTER.equals(source) && TextUtils.isEmpty(destText)) {
return "";
}
// //如果首位为“0”,则只能再输“.”
// if(ZERO.equals(destText)){
// if(!POINTER.equals(sourceText)){
// return "";
// }
// }
//验证小数点精度,保证小数点前只能输入8位
if(dstart == POINTER_BEFORE_LENGTH){
if(sourceText.contains(POINTER))
//return dest.subSequence(dstart,dend)+ sourceText+ZERO+ZERO;
return dest.subSequence(dstart,dend)+ sourceText;
else
//return dest.subSequence(dstart,dend)+POINTER+ZERO+ZERO;
return dest.subSequence(dstart,dend);
}
//输入的是 . 或者0
if ((POINTER.equals(source) || ZERO.equals(source)) && TextUtils.isEmpty(destText)) {
//return ZERO+POINTER+ZERO+ZERO;
return ZERO+POINTER;
}
}
}
//验证输入金额的大小
String result = destText + sourceText;
double sumText = Double.parseDouble(result.replaceAll("¥",""));//需要把"¥"替换掉,否则解析会出异常。
if (sumText >= MAX_VALUE) {
return dest.subSequence(dstart, dend);
}
return dest.subSequence(dstart, dend) + sourceText;
}
}
注意:这里必须在布局文件中添加一项
android:digits="0123456789.¥"
表示可以输入那些字符,否则还是EditText还是不显示”¥”。完成的布局文件如下:
"wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/cash_input_tv"
android:id="@+id/money_rmb_pre_tv"
android:text="@string/money_rmb_pre"
android:textColor="@color/reason_dialog_title"
android:textStyle="bold"
android:textSize="16sp"
android:visibility="gone"/>
"wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="#055493"
android:id="@+id/cash_input_tv"
android:inputType="numberDecimal"
android:digits="0123456789.¥"
android:textColorHint="@color/text_gray4"
android:hint="@string/dinner_cash_input_info"
android:textColor="@color/reason_dialog_title"
android:textStyle="bold"
android:textSize="16sp"
android:visibility="visible"/>
最终显示效果如下:
可以发现,我们最终还是无法让EditText永远始终只刚好包括住里面的内容,我们可以把背景取消掉,曲线让他包裹住。
android:background="@null"
有时有这种需要,hint的内容与Text style不一致,包括字体大小,颜色等。这个时候我们可以再xml中对EditText的Text进行设置,然后在代码中对hint进行设置。java代码中如下:
SpannableString spanString = new SpannableString("粗体斜体");
StyleSpan span = new StyleSpan(Typeface.BOLD_ITALIC);
spanString.setSpan(span, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ForegroundColorSpan span = new ForegroundColorSpan(Color.BLUE);
cashInputTv.setHint(new SpannableString(ss));
关于这个可以网上搜索SpannableString相关资料
其他的包括EditText需要注意的包括 焦点,游标 ,键盘弹出,收起等。后面再更新吧