android EditText中的几大坑

前言

项目中需要用EditText来输入金额,并且默认需要前置”¥”效果如下:
这里写图片描述
很自然的想到用一个TextView与一个EditText来实现。所以布局文件如下:

        <TextView
            android:layout_width="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"/>

        <EditText
            android:layout_width="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"/>

好,现在坑来了

1 EditText设置了hint,其布局宽度wrap_content不再”准确包裹”

因为EditText设置hint,那么初始化的时候虽然我们设置了android:layout_width=”wrap_content”,其初始宽度是刚好包裹hint的宽度,当我们在EditText中输入的字符串小于hint的长度时,其宽度不再完全包裹我们输入的字符串,会留有空白。只有当我们输入的字符串长度大于hint时,才会完全包裹住。我们改一下EditText的背景,效果如下:
这里写图片描述这里写图片描述这里写图片描述
可以看到,不能实现我们最开始给出的效果,那怎么办呢?我们自然想到,我们只用一个 EditText来实现,在每次输入的前面”添加”一个”¥”即可。为此我们需要修改布局文件

<TextView
            android:layout_width="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"/>

        <EditText
            android:layout_width="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信息,可以看到内存溢出了。那是因为又跳到另一个坑里面了。

2 EditText在addTextChangedListener中对输入的内容做更改时,需要先移除TextChangedListener监听

我们在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需要更改,需要包含”¥”

3 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还是不显示”¥”。完成的布局文件如下:

<TextView
            android:layout_width="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"/>

        <EditText
            android:layout_width="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"

4 EditText 总的Text与hint字体大小,颜色,Style要求不一致

有时有这种需要,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需要注意的包括 焦点,游标 ,键盘弹出,收起等。后面再更新吧

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值