帶有一鍵清空功能的EditText

介紹#

很常見的一個(gè)功能稻薇,大部分app在登錄界面都會(huì)實(shí)現(xiàn)這個(gè)功能了。因?yàn)樵诰蚪鹕峡戳艘黄愃频奈恼拢詻Q定自己實(shí)踐一下晓殊。
  下圖為實(shí)現(xiàn)效果:

一鍵清空

常見實(shí)現(xiàn)方法#

  • 組合控件,EditText + Button
     實(shí)現(xiàn)簡(jiǎn)單伤提,可以單獨(dú)使用巫俺。
  • 自定義View,繼承EditText肿男,通過EditText自帶的Drawable來實(shí)現(xiàn)介汹。
     布局復(fù)雜度低

繼承EditText來實(shí)現(xiàn)一鍵清功能#

需要考慮的問題##

根據(jù)業(yè)務(wù)場(chǎng)景,有以下幾個(gè)問題需要我們?cè)趯?shí)現(xiàn)中考慮到:

  1. 怎么添加清空按鈕
  2. 怎么處理點(diǎn)擊事件
  3. 處理清空按鈕的顯示狀態(tài)
    3.1 有文字時(shí)才顯示清空按鈕舶沛,沒有文字則掩藏嘹承。
    3.2 獲取焦點(diǎn)時(shí)才顯示清空按鈕,沒有焦點(diǎn)時(shí)則隱藏
    3.3 EditText的setErrot方法調(diào)用后如庭,清空按鈕怎么處理
  4. 添加自定義的屬性

實(shí)現(xiàn)流程##

帶著上面的問題我們開始一步步實(shí)現(xiàn)自定義View赶撰。

步驟1:繼承EditText,實(shí)現(xiàn)構(gòu)造方法###

public class ClearableEditText extends EditText
        implements EditText.OnFocusChangeListener {

    public static final String TAG = "ClearableEditText";

    public ClearableEditText(Context context) {
       this(context, null);
    }

    public ClearableEditText(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.editTextStyle);
    }

    public ClearableEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ClearableEditText(Context context, AttributeSet attrs, int defStyleAttr, int
            defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }
}

步驟二:添加清空按鈕###

TextView中有一個(gè)靜態(tài)內(nèi)部類Drawables柱彻, 這個(gè)類的作用是用來保存和顯示TextView上下左右的Drawable豪娜。通過android:drawable* 來設(shè)置的drawable icon就保存在Drawables中。

通過調(diào)用 Drawable drawables[] = getCompoundDrawables();方法可以獲取到Drawables中的left, top, right, and bottom的Drawable數(shù)組哟楷。而drawables[2]正好就是顯示在TextView右邊的drawable icon瘤载,所以這個(gè)drawable 正好滿足我們的需求。


    private Drawable mClearDrawable;
    /**
     * Right Drawable 是否可見
     */
    private boolean mIsClearVisible;

    private void init(Context context, AttributeSet attrs, int defStyleAttr, int
            defStyleRes) {

        Drawable drawables[] = getCompoundDrawables();
        mClearDrawable = drawables[2]; // Right Drawable;

        // 第一次隱藏
        setClearDrawableVisible(false);
    }

步驟三:處理點(diǎn)擊事件###

由于Drawable沒辦法接收處理TouchEvent卖擅,所以我們只能通過觸摸區(qū)域來判斷鸣奔,當(dāng)觸摸事件的坐標(biāo)在right drawable的范圍內(nèi)的時(shí)候就觸發(fā)點(diǎn)擊事件。

覆寫onTouchEvent事件惩阶,這里我只判斷了x軸的范圍挎狸。那為什么不加上y軸的判斷呢?個(gè)人認(rèn)為沒什么必要断楷。

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // error drawable 不顯示 && clear drawable 顯示 && action up
        if (getError() == null && mIsClearVisible && event.getAction() == MotionEvent.ACTION_UP) {

            float x = event.getX();
            if (x >= getWidth() - getTotalPaddingRight() && x <= getWidth() - getPaddingRight()) {
                Log.d(TAG, "點(diǎn)擊清除按鈕锨匆!");

                clearText();
            }
        }

        return super.onTouchEvent(event);
    }



步驟四:處理清空按鈕的顯示狀態(tài)###

有三種情況需要考慮:
1 有文字時(shí)才顯示清空按鈕,沒有文字則掩藏冬筒。
2 獲取焦點(diǎn)時(shí)才顯示清空按鈕恐锣,沒有焦點(diǎn)時(shí)則隱藏
3 EditText的setErrot方法調(diào)用后茅主,清空按鈕怎么處理

為了解決1和2的兩個(gè)問題,我們需要為EditText監(jiān)聽文字輸入的狀態(tài)和獲取失去焦點(diǎn)的狀態(tài)土榴,因此我們需要實(shí)現(xiàn)onFocusChange和addTextChangedListener诀姚。

    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        
        if (getError() == null) {
            if (hasFocus) {
                if (getText().length() > 0) {
                    setClearDrawableVisible(true);
                }
            } else {
                setClearDrawableVisible(false);
            }
        }
    }

        // 添加TextChangedListener
        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) {
                Log.d(TAG, "onTextChanged " + s);

                setClearDrawableVisible(s.length() > 0);
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

setClearDrawableVisible的具體實(shí)現(xiàn):
我們可以通過setCompoundDrawables來設(shè)置TextView的left、top玷禽、right赫段、bottom drawable。傳遞null表示不需要顯示矢赁。

    /**
     * 設(shè)置Right Drawable是否可見
     *
     * @param isVisible true for visible , false for invisible
     */
    public void setClearDrawableVisible(boolean isVisible) {

        setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1],
                isVisible ? mClearDrawable : null, getCompoundDrawables()[3]);

        mIsClearVisible = isVisible;
    }

最后考慮第三種情況糯笙,EditText的setErrot方法可以給出校驗(yàn)提示,從下圖中大致可以看出這個(gè)提示由兩部分組成坯台,一個(gè)icon和一個(gè)popup window炬丸,而這個(gè)icon正好占據(jù)了我們上面提到的right drawable的位置也就是我們的清除按鈕需要用到的位置瘫寝。那么error icon是不是也用了right drawable來實(shí)現(xiàn)的呢蜒蕾?


setError.jpg

在TextView中有這樣一個(gè)方法applyErrorDrawableIfNeeded,從方法名就可以猜到它就是用來設(shè)置error drawable的焕阿,最終通過mShowing[Drawables.RIGHT] = mDrawableError; 把error drawable 放到了Drawables.RIGHT中咪啡。哦,對(duì)了暮屡。撤摸。這一切都是基于LTR的Layout方向。

            // then, if needed, assign the Error drawable to the correct location
            if (mDrawableError != null) {
                switch(layoutDirection) {
                    case LAYOUT_DIRECTION_RTL:
                        mDrawableSaved = DRAWABLE_LEFT;

                        mDrawableTemp = mShowing[Drawables.LEFT];
                        mDrawableSizeTemp = mDrawableSizeLeft;
                        mDrawableHeightTemp = mDrawableHeightLeft;

                        mShowing[Drawables.LEFT] = mDrawableError;
                        mDrawableSizeLeft = mDrawableSizeError;
                        mDrawableHeightLeft = mDrawableHeightError;
                        break;
                    case LAYOUT_DIRECTION_LTR:
                    default:
                        mDrawableSaved = DRAWABLE_RIGHT;

                        mDrawableTemp = mShowing[Drawables.RIGHT];
                        mDrawableSizeTemp = mDrawableSizeRight;
                        mDrawableHeightTemp = mDrawableHeightRight;

                        mShowing[Drawables.RIGHT] = mDrawableError;
                        mDrawableSizeRight = mDrawableSizeError;
                        mDrawableHeightRight = mDrawableHeightError;
                        break;
                }
            }

那么我們就可以處理第三種情況褒纲。覆寫setError方法准夷。想要知道error drawable是否顯示,可以通過“getError() == null”來判斷莺掠,為ture則表示不顯示衫嵌,false表示已顯示。

    @Override
    public void setError(CharSequence error, Drawable icon) {
        if (error != null) {
            setClearDrawableVisible(true);
        }
        super.setError(error, icon);
    }

為什么是覆寫setError方法彻秆?我們可以通過TextView.sendAfterTextChanged來找到答案楔绞。當(dāng)EditText中重新輸入文字的后,sendAfterTextChanged會(huì)被調(diào)用唇兑,在sendAfterTextChanged方法中會(huì)調(diào)用hideErrorIfUnchanged酒朵,而 hideErrorIfUnchanged則是直接調(diào)用了setError(null, null)。通過setError(null, null)方法隱藏 error drawable扎附。

步驟五:添加自定義屬性###

attrs.xml中申明style

    <declare-styleable name="ClearableEditText">

        <attr name="right_drawable_color" format="color|reference" />

    </declare-styleable>

在init函數(shù)中獲取自定義屬性并做相關(guān)處理蔫耽。

        final Resources.Theme theme = context.getTheme();

        TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.ClearableEditText,
                defStyleAttr, defStyleRes);

        int rightDrawableColor = a.getColor(R.styleable.ClearableEditText_right_drawable_color,
                Color.BLACK);

        a.recycle();

        // 給mRightDrawable上色
        DrawableCompat.setTint(mClearDrawable, rightDrawableColor);

實(shí)例效果#

一鍵清空

源碼#

ClearableEditText

參考#

和我一起實(shí)現(xiàn)EditText一鍵清空功能
Android自定義View示例(一)—帶有刪除按鈕的EditText

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市留夜,隨后出現(xiàn)的幾起案子针肥,更是在濱河造成了極大的恐慌饼记,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慰枕,死亡現(xiàn)場(chǎng)離奇詭異具则,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)具帮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門博肋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蜂厅,你說我怎么就攤上這事匪凡。” “怎么了掘猿?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵病游,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我稠通,道長(zhǎng)衬衬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任改橘,我火速辦了婚禮滋尉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘飞主。我一直安慰自己狮惜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布碌识。 她就那樣靜靜地躺著碾篡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筏餐。 梳的紋絲不亂的頭發(fā)上开泽,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音胖烛,去河邊找鬼眼姐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛佩番,可吹牛的內(nèi)容都是我干的众旗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼趟畏,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼贡歧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤利朵,失蹤者是張志新(化名)和其女友劉穎律想,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绍弟,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡技即,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了樟遣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片而叼。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖豹悬,靈堂內(nèi)的尸體忽然破棺而出葵陵,到底是詐尸還是另有隱情,我是刑警寧澤瞻佛,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布脱篙,位于F島的核電站,受9級(jí)特大地震影響伤柄,放射性物質(zhì)發(fā)生泄漏绊困。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一响迂、第九天 我趴在偏房一處隱蔽的房頂上張望考抄。 院中可真熱鬧细疚,春花似錦蔗彤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吧彪,卻和暖如春待侵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姨裸。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工秧倾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人傀缩。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓那先,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赡艰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子售淡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容