在設定Android App的時候,常常會需要讓使用者輸入一些文字,但又不想要讓使用者輸入太多文字,要怎麼樣才可以讓文字輸入框能夠限制使用者輸入的長度呢?如果使用者輸入的文字不是只有半形字,而是還有全形字的話,又該怎麼去計算文字長度呢?



以EditText這個View為例,如果要限制EditText輸入文字的最大長度,可以設定它的「maxLength」屬性。在XML下,可以替EditText加入這個屬性,並指定最大的文字長度。如下面的XML語法,將EditText的可輸入的最大字元數量限制在10個:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:maxLength="10" />

如果要用Android SDK的Java程式來寫,會比較麻煩一點,可以寫成這樣:

editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(10)});

用以上的方式雖然可以快速地限制EditText的文字長度,但是卻會發生半形字和全形字雖然數量一樣,長度看起來卻差很多的問題。如下圖:

此時可以使用以下的解決方案。

MagicTextLengthWatcher

import android.text.Editable;
import android.text.TextWatcher;

/**
 * 限制文字輸入的長度
 *
 * @author magiclen
 *
 */
public class MagicTextLengthWatcher implements TextWatcher {

    private int maxLength; // 儲存最大的字串長度
    private int currentEnd = 0; // 儲存目前字串改變的結束位置,例如:abcdefg變成abcd1234efg,變化的結束位置就在索引8

    public MagicTextLengthWatcher(final int maxLength) {
        setMaxLength(maxLength);
    }

    public final void setMaxLength(final int maxLength) {
        if (maxLength >= 0) {
            this.maxLength = maxLength;
        } else {
            this.maxLength = 0;
        }
    }

    public int getMaxLength() {
        return this.maxLength;
    }

    @Override
    public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {

    }

    @Override
    public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
        currentEnd = start + count; // 取得變化的結束位置
    }

    @Override
    public void afterTextChanged(final Editable s) {
        while (calculateLength(s) > maxLength) { // 若變化後的長度超過最大長度
            // 刪除最後變化的字元
            currentEnd--;
            s.delete(currentEnd, currentEnd + 1);
        }
    }

    /**
     * 計算字串的長度
     *
     * @param c 傳入字串
     *
     * @return 傳回字串長度
     */
    protected int calculateLength(final CharSequence c) {
        int len = 0;
        final int l = c.length();
        for (int i = 0; i < l; i++) {
            final char tmp = c.charAt(i);
            if (tmp >= 0x20 && tmp <= 0x7E) {
                // 字元值 32~126 是 ASCII 半形字元的範圍
                len++;
            } else {
                // 非半形字元
                len += 2;
            }
        }
        return len;
    }
}

使用MagicTextLengthWatcher這個TextWatcher,可以很輕易地限制EditText的文字長度。預設將一個半形字元的長度用1計算,一個全形字元的長度用2計算(一個全形字的長度等於兩個半形字的意思)。使用方式如下:

editText.addTextChangedListener(new MagicTextLengthWatcher(10));