android EditText 實現小寫轉大寫源碼分析

需求

輸入框輸入小寫字母自動轉爲大寫字母
android EditText 小寫轉大寫實現分析
上面是個EditText 作爲輸入框。最下方view 記錄的是原始輸入內容。

實現


1.獲取 android.text.method.ReplacementTransformationMethod 類的實例對象

    private ReplacementTransformationMethod transformationMethod = new ReplacementTransformationMethod() {
        @Override
        protected char[] getOriginal() {
            return new char[]{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
        }

        @Override
        protected char[] getReplacement() {
            return new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
        }
    };

2.爲EditText添加 TransformationMethod 監聽

editText.setTransformationMethod(transformationMethod);

注意:使用這種方法兩個問題

  1. 通過 editText.getText().toString() 獲取到的是用戶操作的原始輸入,不是轉換後的。
  2. 在添加的 TextWatcher 監聽中,同樣獲取到的是輸入的原始值。

原理分析


android.text.method.ReplacementTransformationMethod

/**
 * 該類會將{@link #getOriginal}中的字符數組替換爲{@link #getReplacement}數組。
 */
public abstract class ReplacementTransformationMethod
implements TransformationMethod
{
    /**
     * 返回顯示時將被其他字符替換的字符列表。
     */
    protected abstract char[] getOriginal();
    /**
     * 返回替換的字符列表
     */
    protected abstract char[] getReplacement();

    /**
     * 處理替換的邏輯,getOriginal() 和  getReplacement() 方法在此方法內被調用以完成字符替換
     */
    public CharSequence getTransformation(CharSequence source, View v) {
    	...省略...
    }
 }

android.text.method.TransformationMethod

/**
 * TextView使用TransformationMethods進行字符替換操作,例如用點替換密碼字符,或防止換行符在單行文本字段中引起換行。
 */
public interface TransformationMethod
{
    /**
     * 返回源文本sourceText轉換後的數據。例如,用密碼字段中的點替換每個字符。 需要主要的是返回的文本
     * 必須與源文本長度完全相同,並且如果源文本是可編輯的,則返回的文本必須動態鏡像它,而不是進行一次複製。
     */
    public CharSequence getTransformation(CharSequence source, View view);

    /**
     * 使用該 TransformationMethod 的TextView 在獲得/失去 焦點時調用
     */
    public void onFocusChanged(View view, CharSequence sourceText,
                               boolean focused, int direction,
                               Rect previouslyFocusedRect);
}

android.widget.TextView#setTransformationMethod

TextView 內部有兩個字段來控制源文本和變換後的文本

    @ViewDebug.ExportedProperty(category = "text")
    private @Nullable CharSequence mText; //源文本
    private CharSequence mTransformed; //變換的文本

TextView 的setTransformationMethod 內部會調用 setText(java.lang.CharSequence) 方法,最終會執行到 setText(java.lang.CharSequence, android.widget.TextView.BufferType, boolean, int) 方法內。在該方法內調用 mTransformation.getTransformation(text, this) 方法對源數據進行變換,並把返回的值賦給 mTransformed 字段。

    private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) {
    	...省略其他代碼
    	
    	if (mTransformation == null) {
            mTransformed = text;
        } else {
            mTransformed = mTransformation.getTransformation(text, this);
        }

		... 省略其他代碼
		sendOnTextChanged(text, 0, oldlen, textLength);
		... 省略其他代碼
		sendAfterTextChanged((Editable) text);
		... 省略其他代碼
    }

在 sendOnTextChanged 和 sendAfterTextChanged 方法中會執行文本改變監聽的調用,而傳入的text 爲 mText ,非mTransformed,這也就解釋了爲啥在給TextView 添加 TextWatcher 監聽時,獲得的值仍然爲源文本,而非轉換後的文本

   /**
     * Not private so it can be called from an inner class without going
     * through a thunk.
     */
    void sendOnTextChanged(CharSequence text, int start, int before, int after) {
        if (mListeners != null) {
            final ArrayList<TextWatcher> list = mListeners;
            final int count = list.size();
            for (int i = 0; i < count; i++) {
                list.get(i).onTextChanged(text, start, before, after);
            }
        }

        if (mEditor != null) mEditor.sendOnTextChanged(start, before, after);
    }

    /**
     * Not private so it can be called from an inner class without going
     * through a thunk.
     */
    void sendAfterTextChanged(Editable text) {
        if (mListeners != null) {
            final ArrayList<TextWatcher> list = mListeners;
            final int count = list.size();
            for (int i = 0; i < count; i++) {
                list.get(i).afterTextChanged(text);
            }
        }

        // Always notify AutoFillManager - it will return right away if autofill is disabled.
        notifyAutoFillManagerAfterTextChangedIfNeeded();

        hideErrorIfUnchanged();
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章