今天我要好好說(shuō)道說(shuō)道TextInputEditText

這是一篇記敘文

記敘文六要素:人物、時(shí)間、地點(diǎn)、事件發(fā)生的起因、經(jīng)過(guò)、結(jié)果

人物:

保密

時(shí)間:

2017-06-28

地點(diǎn):

保密

  • 上來(lái)就貼代碼
    <android.support.design.widget.TextInputLayout
        android:id="@+id/til_new_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="5"
        android:gravity="center_vertical"
        android:hint="提示的話"
        app:counterEnabled="true"
        app:counterMaxLength="10"
        app:counterOverflowTextAppearance="@style/TextAppearance.Design.Counter.Overflow"
        app:counterTextAppearance="@style/TextAppearance.Design.Counter">

        <android.support.design.widget.TextInputEditText
            android:id="@+id/et_new_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:digits="0123456789ABCDEFabcdef"
            android:maxLength="10"
            android:singleLine="true"
            android:textSize="24sp"
            tools:text="00FF010206"/>
    </android.support.design.widget.TextInputLayout>
  • 對(duì)應(yīng)的圖圖

Paste_Image.png

TextInputLayout繼承自LinearLayout,TextInputEditText繼承自AppCompatEditText,二者結(jié)合使用就是為了實(shí)現(xiàn)上圖的風(fēng)騷效果的。需要依賴Design包compile 'com.android.support:design:25.3.1'

起因:

我想通過(guò)繼承TextInputEditText自定義一個(gè)TadIDEditText,在上圖風(fēng)騷效果的基礎(chǔ)上實(shí)現(xiàn)TagID號(hào)(eg:0102030405)每?jī)晌缓竺孀詣?dòng)加一個(gè)空格(Space)的效果,代碼如下

public class TadIDEditText extends TextInputEditText {
    private boolean shouldStopChange = false;

    public TadIDEditText(Context context) {
        super(context);
    }

    public TadIDEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TadIDEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private TextWatcher mTagIDEidtTextWatcher = 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) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            if (shouldStopChange) {
                shouldStopChange = false;
                return;
            }
            shouldStopChange = true;
            String str = s.toString().trim().replaceAll(" ", "");
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < str.length(); i++) {
                sb.append(str.charAt(i));
                if (i % 2 == 1 && i != str.length() - 1) {
                    sb.append(" ");
                }
            }
            setText(sb);
            setSelection(sb.length());
        }
    };

    @Override
    public void addTextChangedListener(TextWatcher watcher) {
        super.addTextChangedListener(mTagIDEidtTextWatcher);
    }

    @Override
    public void removeTextChangedListener(TextWatcher watcher) {
        super.removeTextChangedListener(mTagIDEidtTextWatcher);
    }

    public String getInputTagIDText() {
        return getText().toString().replace(" ", "").trim();
    }

    public TextWatcher getTagIDEditTextWatcher() {
        return mTagIDEidtTextWatcher;
    }
}

經(jīng)過(guò):

  • 先說(shuō)明一下shouldStopChange的作用。
    因?yàn)槭窃赼fterTextChanged方法中判斷、添加空格的,所以執(zhí)行完afterTextChanged方法后,EditText的Text又變了,又會(huì)觸發(fā)TextChanged,如果不通過(guò)這個(gè)變量控制,就會(huì)進(jìn)入死循環(huán),界面卡死,方法棧溢出。
  • addTextChangedListener(TextWatcher watcher)的源碼(在TextView中)如下
    public void addTextChangedListener(TextWatcher watcher) {
        if (mListeners == null) {
            mListeners = new ArrayList<TextWatcher>();
        }
        mListeners.add(watcher);
    }
    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);
            }
        }
        hideErrorIfUnchanged();
    }

也就是你可以給TextView及其繼承者們添加多個(gè)TextWatcher,當(dāng)其中的Text發(fā)生變化時(shí),會(huì)依次調(diào)用這些TextWatcher中的幾個(gè)方法。
注意:依次、依次、依次。

  • 為了實(shí)現(xiàn)良好的封裝,我希望添加空格的邏輯寫(xiě)在TadIDEditText中,鑒于此,我寫(xiě)好了一個(gè)mTagIDEidtTextWatcher,為了讓我這個(gè)EditText功能專一,我重寫(xiě)了addTextChangedListener,如下,就是讓它只能設(shè)置這一個(gè)監(jiān)聽(tīng)器。
@Override
    public void addTextChangedListener(TextWatcher watcher) {
        super.addTextChangedListener(mTagIDEidtTextWatcher);
    }

結(jié)果造成了下面的問(wèn)題

  • 使用TadIDEditText的時(shí)候,多次調(diào)用addTextChangedListener會(huì)造成多次添加mTagIDEidtTextWatcher,比如添加了2次,依據(jù)上面的分析我shouldStopChange這個(gè)變量就失去作用了,照樣造成死循環(huán)、方法棧溢出。

  • 我在Activity中調(diào)用了一次addTextChangedListener,結(jié)果還是界面卡死、方法棧溢出了。然后通過(guò)一個(gè)簡(jiǎn)單的log,我發(fā)現(xiàn)盡管我只調(diào)用了一次,但是addTextChangedListener執(zhí)行了兩次,而且即使我不調(diào)用,addTextChangedListener也會(huì)執(zhí)行一次。這就奇怪了???而且TextInputLayout右下角的計(jì)數(shù)也不計(jì)數(shù)了,這下就明白了,TextInputLayout和TextInput搭配使用,之所以能計(jì)數(shù),不就是加了一個(gè)TextWatcher嗎?看源碼,果不其然。終于在TextInputLayout中發(fā)現(xiàn)了問(wèn)題所在。以下貼出TextInputLayout中的幾個(gè)方法。

@Override
    public void addView(View child, int index, final ViewGroup.LayoutParams params) {
        if (child instanceof EditText) {
            // Make sure that the EditText is vertically at the bottom, so that it sits on the
            // EditText's underline
            FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(params);
            flp.gravity = Gravity.CENTER_VERTICAL | (flp.gravity & ~Gravity.VERTICAL_GRAVITY_MASK);
            mInputFrame.addView(child, flp);

            // Now use the EditText's LayoutParams as our own and update them to make enough space
            // for the label
            mInputFrame.setLayoutParams(params);
            updateInputLayoutMargins();

            setEditText((EditText) child);<<<<<<<<<<<<<<<<<<<重點(diǎn)看這里
        } else {
            // Carry on adding the View...
            super.addView(child, index, params);
        }
    }
private void setEditText(EditText editText) {
        // If we already have an EditText, throw an exception
            ......
        // Add a TextWatcher so that we know when the text input has changed
        mEditText.addTextChangedListener(new TextWatcher() {<<<<<<<<<<<<<<<<<<<重點(diǎn)看這里
            @Override
            public void afterTextChanged(Editable s) {
                updateLabelState(!mRestoringSavedState);
                if (mCounterEnabled) {
                    updateCounter(s.length());//更新計(jì)數(shù)
                }
            }

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

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {}
        });

        // Use the EditText's hint colors if we don't have one set
            ......
    }

看到這兒總算找出了我的bug,看來(lái)我這種寫(xiě)法問(wèn)題多多啊,不過(guò)也通過(guò)問(wèn)題對(duì)這兩個(gè)控件了解了一下。

結(jié)果:

我想想還能怎么改。繼續(xù)擼碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容