專案需求討論--可能是用InputFilter來做的最好的金額限制

青蛙要fly發表於2017-10-25

看慣了可能是XXX最好的,可能是XXXX目前最好的,今天我也用下這個標題,哈哈。別噴我,當然我也就吹吹牛。有很多好的方法來實現。

本文主要還是用來講解下InputFilter的使用。

一般金額類的輸入需求比較多,我們這裡就用金額輸入框做例項。其他的類似的文字,大小寫字母等需求限制也是同理的。

某天產品經理 A拿著菜刀到我身邊說:

第一次交鋒

A:小B啊,這個介面的金額輸入框輸入的錢小數點後最多二位,也就是最多到分,還有那個介面的這個地方,填金額也是精度到分。

唯唯諾諾的我:好的,馬上完成。

1.控制小數點後位數:

因為有很多介面都要用到,所以我們專門抽出一個類來進行控制,並且我們知道,我們要控制EditText控制它的輸入內容,其實相當於是對其進行過濾,所以我們讓我們的類實現InputFilter介面。

public class PointLengthFilter implements InputFilter {
    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        return null;
    }
}複製程式碼

發現一定要我們實現filter方法,從字面意思看我們也知道是過濾,那我們來看下具體的引數欄位的意思:

欄位 型別 內容
source CharSequence 為即將輸入的字串
start int source的start, start 為0
end int source的end ,因為start為0,end也可理解為source長度了
dest Spanned 輸入框中原來的內容
dstart int 要替換或者新增的起始位置,即游標所在的位置
dend int 要替換或者新增的終止始位置,若為選擇一串字串進行更改,則為選中字串 最後一個字元在dest中的位置

我們來假設下,我們通過鍵盤依次輸入12345,我們可以看到相應的值:

source:1,start:0,end:1,dest:,dstart:0,dend:0
source:2,start:0,end:1,dest:1,dstart:1,dend:1
source:3,start:0,end:1,dest:12,dstart:2,dend:2
source:4,start:0,end:1,dest:123,dstart:3,dend:3
source:5,start:0,end:1,dest:1234,dstart:4,dend:4複製程式碼

大家可能會發現start一直為0,end一直為1,因為我們是依次輸入的,比如你複製三個字元,通過貼上複製的方式加入到EditText中,這時候就不是0和1了,而是0,3。

所以根據這個小數點位數需求,我們先來第一版的Filter(有問題版本)

public class PointLengthFilter implements InputFilter {

    private static final int DECIMAL_DIGITS = 2;//小數的位數

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        // 刪除等特殊字元,直接返回
        if ("".equals(source.toString())) {
            return null;
        }
        //原來輸入框已有的內容
        String dValue = dest.toString();
        //通過小數點來進行拆分,分為小數點前面的字串和小數點後面的字串
        String[] splitArray = dValue.split("\\.");
        if (splitArray.length > 1) {
            //獲的小數點後面的字串
            String dotValue = splitArray[1];
            //判斷小數點後面的當前位數是不是已經大於等於規定的2了
            //如果已經2位了,則返回"";
            if (dotValue.length() >= DECIMAL_DIGITS) {
                return "";
            }
        }
        return null;
    }
}複製程式碼

然後在我們的Activity中設定:

EditText editText = (EditText) findViewById(R.id.et_money);
editText.setFilters(new InputFilter[]{new PointInputFilter()});複製程式碼

PS :可以把過濾的條件單獨寫出來分為好幾個檔案,因為傳入的是InputFilter陣列。

這樣。我們終於實現了小數點後面的位數控制了。

如下圖所示,我們輸入12345.67之後,再輸入其他字元,在filter中就預設返回了一個空的字串"",所以就等於沒輸入其他內容進去。


第二次交鋒:

產品經理 A拿著菜刀氣沖沖的過來了。

A:你這個輸入金額的有問題你知道嗎?你都不能自己好好測試測試嗎?

低聲下氣的我:不可能啊,我測試過的啊,小數點後面的數的位數不會超過2啊。

A:位數的確不超過2了,但是你修改下小數點前面的數字試試。

我拿著手機試了下,比如上面我們已經輸入了12345.67,這時候我想在小數點前面的內容多加個數字,或者前面的12345我刪了幾個,再輸入其他數字都不行了。因為小數點後的位數是一直是2,這時候我們修改小數點前的內容,就一直觸發:

String[] splitArray = dValue.split("\\.");
        if (splitArray.length > 1) {
            //獲的小數點後面的字串
            String dotValue = splitArray[1];
            //判斷小數點後面的當前位數是不是已經大於等於規定的2了
            //如果已經2位了,則返回"";
            if (dotValue.length() >= DECIMAL_DIGITS) {
                return "";
            }
        }複製程式碼

所以一直返回一個空字串,所以就無法修改小數點前面的數字了。

2.控制小數點位數的同時,更改小數點前的數字:

我們只需要改原本控制小數點的邏輯程式碼即可:

String dValue = dest.toString();
String[] splitArray = dValue.split("\\.");
if (splitArray.length > 1) {
    String dotValue = splitArray[1];
    //獲取小數點“.”在字串中的index值
    int dotIndex = dValue.indexOf(".");
    /新增了一個條件判斷:輸入游標是在小數點的後面
    if (dotValue.length() >= DECIMAL_DIGITS && dstart > dotIndex) {
        return "";
    }
}複製程式碼

試了一下。果然可以自由的對小數點前面的數字隨意的增刪改了。哈哈。我心滿意足的再次改好上交了。


第三次交鋒:

產品經理這次拿著一把砍刀再次過來。

A:你這個輸入金額的小數點前面的數可以輸入很多,我這邊考慮了下,要求小數點前面最多輸入6位,加起來最大可輸入的值是999999.99元。也就是不超過一百萬,下班前記得完成哦。

有點氣憤的我:好的。包您滿意。

3.限制小數點前面的位數:

這時候其實我們也知道並不難,只要在小數點前面的位數增加控制就行:

public class PointInputFilter1 implements InputFilter {

    private static final int DECIMAL_DIGITS = 2;//小數的位數
    private static final int INTEGER_DIGITS = 6;//整數位的位數
    private static final int TOTAL_DIGITS = 9; //整數部分 + “小數點” + 小數部分
    private int currentLimitDigits = INTEGER_DIGITS;//當前控制的位數值

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

        // 刪除等特殊字元,直接返回
        if ("".equals(source.toString())) {
            return null;
        }
        String dValue = dest.toString();
        String[] splitArray = dValue.split("\\.");
        switch (splitArray.length) {
            case 1:
                //判斷當前是否有小數點,如果有小數點就把控制位數變為TOTAL_DIGITS(其實只要比整數位數+1大就可以)
                if (dValue.indexOf(".") != -1) {
                    currentLimitDigits = TOTAL_DIGITS;
                } else {
                    //如果沒有小數點,則繼續控制前面整數位的位數為6位
                    currentLimitDigits = INTEGER_DIGITS;
                }

                /**這裡如果我們直接輸入999999時候,其實已經不能按其他數字了,
                不然就超過一百萬了,但是這時候如果輸入的是小數點,則可以在輸入框中顯示小數點。
                而且這時候在上面已經把當前的位數限制變大,
                這時候就可以就可以輸入其他數字,然後接下去就會跳入到下面的case 2的判斷了。
                **/
                if(splitArray[0].length() >= currentLimitDigits && !".".equals(source.toString())){
                    return "";
                }
                break;

            case 2:
                String integerValue = splitArray[0];
                String dotValue = splitArray[1];
                int dotIndex = dValue.indexOf(".");
                if (integerValue.length() >= INTEGER_DIGITS && dstart <= dotIndex) {
                    return "";
                }
                if (dotValue.length() >= DECIMAL_DIGITS && dstart > dotIndex) {
                    return "";
                }
                break;
            default:
                break;
        }

        return null;
    }
}複製程式碼

這下終於可以交差了。然後沾沾自喜的把成果釋出了一版,開心的等著下班。


第四次交鋒:

產品經理推著大炮再次走了過來,

A:你的輸入有問題,你看,我都輸入了好幾百萬了。

我:不可能啊,我測試過的啊,我演示給你看,看吧。不可能輸得進去的。

A:我不是鍵盤輸入的,我是直接其他地方複製了多位數字,然後貼上複製進去的。

我: ........

A:反正我不管,你沒弄完不準下班。

我心裡暗暗說了句:MMP

4.處理通過貼上複製的方式輸入

這裡我們可以有二種處理方式:

  1. 直接就乾脆不讓多位數字貼上進來。
  2. 針對多位數字賦值貼上來進行處理。

<1> 不準複製貼上多位數字:

這個很簡單,如果客戶是複製一位數字,然後貼上複製進去的,其實就等效我們用鍵盤輸入,所以就不需要特殊處理。我們只在意的是比如現在是999.99,他在前面直接貼上了99999。就變成了99999999.99了,超過我們的範圍了。我們可以直接禁止多位數字的貼上複製,程式碼很簡單:

//在最前面多新增一個判斷,就是當輸入的字元是多位的時候,直接返回空字串。
//因為通過鍵盤輸入我們都是一位位輸入的,而多位的情況一般就是複製貼上進來的。
if(source.length() > 1){
    return "";
}複製程式碼

<2> 處理貼上複製的方式的輸入:

我們假設這幾種情況:

(1)輸入框裡面的內容是整數,比如1234,然後我們複製整數9999進去,這時候應該是123499。
(2)輸入框裡面的內容是整數,比如1234,然後我們複製整數999.999進去,這時候應該是123499.99。
(3)輸入框裡面的內容是小數,比如1234.1,然後我們複製整數999進去,如果複製在小數點前面,應該是123499.1,如果複製在小數點後面,應該是1234.1。
(4)輸入框裡面的內容是小數,比如1234.1,然後我們複製的也是小數進去,比如9.9,我們貼上在小數點前,則變為了123499.1,因為輸入框內預設就一個小數點,複製進來的9.9我們就作為99加入到整數部分。如果加到小數點後面則變為1234.19。
(5)輸入框裡面是空的內容,我們輸入12345678.87654321;小數點前面也超出,後面也超出,取有效部分,變為123456.87。

PS:每個人在具體的業務中可能要求不同,主要是按實際業務來,我這邊是當貼上的數字太大的時候,擷取了還能放下的位數,你也可以乾脆發現貼上的數加進去後超標了。直接返回空字串。

附上最終的程式碼:

PointInputFilter.java:

public class PointInputFilter implements InputFilter {

    private static final int DECIMAL_DIGITS = 2;//小數的位數
    private static final int INTEGER_DIGITS = 6;//整數位的位數
    private static final int TOTAL_DIGITS = 9; //整數部分 + “小數點” + 小數部分
    private int currentLimitDigits = INTEGER_DIGITS;

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

        // 刪除等特殊字元,直接返回
        if ("".equals(source.toString())) {
            return null;
        }

        /*
        如果想要直接禁止複製貼上多個數字,直接這邊限制。
        if(source.length() > 1){
            return "";
        }*/

        String dValue = dest.toString();
        String[] splitArray = dValue.split("\\.");
        switch (splitArray.length) {
            case 1:

                if (dValue.indexOf(".") != -1) {
                    currentLimitDigits = TOTAL_DIGITS;
                } else {
                    currentLimitDigits = INTEGER_DIGITS;
                }

                if (source.length() > 1) {

                    String sValue = source.toString();
                    String[] subSplitArray = sValue.split("\\.");
                    switch (subSplitArray.length) {
                        case 1:
                            if (source.length() + dest.length() > currentLimitDigits) {
                                return source.subSequence(0, currentLimitDigits - dest.length());
                            }
                            break;
                        case 2:
                            String content = "";

                            if (dstart == dest.length()) {
                                if (subSplitArray[0].length() + dest.length() > INTEGER_DIGITS) {
                                    content += subSplitArray[0].subSequence(0, INTEGER_DIGITS - dest.length());
                                } else {
                                    content += subSplitArray[0];
                                }

                                if (subSplitArray[1].length() > DECIMAL_DIGITS) {
                                    content += "."+ subSplitArray[1].substring(0, DECIMAL_DIGITS);

                                } else {
                                    content += "."+ subSplitArray[1];
                                }
                                return content;

                            } else {
                                if (subSplitArray[0].length() + dest.length() > INTEGER_DIGITS) {
                                    content += subSplitArray[0].subSequence(0, INTEGER_DIGITS - dest.length());
                                } else {
                                    content += subSplitArray[0];
                                }
                            }
                            return content;

                        default:
                            break;
                    }

                }

                if (splitArray[0].length() >= currentLimitDigits && !".".equals(source.toString())) {
                    return "";
                }

                break;

            case 2:
                String integerValue = splitArray[0];
                String dotValue = splitArray[1];
                int dotIndex = dValue.indexOf(".");

                if (dstart <= dotIndex) {

                    if (integerValue.length() >= INTEGER_DIGITS) {
                        return "";
                    } else if (source.length() + integerValue.length() >= INTEGER_DIGITS) {
                        return source.subSequence(0, INTEGER_DIGITS - integerValue.length());
                    }

                } else {

                    if (dotValue.length() >= DECIMAL_DIGITS) {
                        return "";
                    } else if (source.length() + dotValue.length() >= DECIMAL_DIGITS) {
                        return source.subSequence(0, DECIMAL_DIGITS - dotValue.length());
                    }
                }

                break;
            default:
                break;
        }

        return null;
    }
}複製程式碼

總結:

完畢。終於可以安心下班了。哈哈。。。

相關文章