讓你的 EditText 所有清除

weixin_34037977發表於2017-07-11

原文地址:讓你的 EditText 所有清除

參考原文:Giving your Edit Texts the All Clear

專案地址(歡迎 Star):ClearEditText

在輸入文字的時候,通常當前輸入的地方的末尾會有一個 ‘x’ 來結束。它的作用是,假設我們想要清空這一整行輸入的時候,點一下 ‘x’ 就能夠了。它的存在。還是非常有必要的。

然後。Android UI 元件並沒有提供這種功能,假設 Android 使用者在輸入了一段非常長的文字的時候,發現他全然輸錯了。這時候想要刪除整行內容的話,他必須一直按刪除鍵,或者長按選中整段文字,然後刪除。所以說。事實上,這樣一個簡單 ‘x’ 的存在是非常又必要的。

對於這個 ‘x’ 我們有什麼要求呢:

  • ‘x’ 應該僅僅在我們編輯這一項文字的時候而且我們獲得了焦點的情況下才顯示
  • ‘x’ 應該顯示在這項文字內部,即它應該顯示在 EditText 裡
  • 按下 ‘x’ 應該是清除所有內容
  • ‘x’ 的顏色應該是與編輯文字的主題色是一致的

這些要求意味著我們須要自己定義 EditText。對於第一個要求,我們須要一個 TextWatcher。這種話我們就能夠看到內容欄位發生的改變。還有我們須要實現 onFocusChangeListener。第二個要求。我們能夠實現一個 ‘x’ 作為一個 compound drawable,由於這裡沒有 onClick 事件去監聽 compound drawable,我們須要使用 OnTouch 來監聽,這樣第三個要求也實現了。對於第四個要求,我覺得 ‘x’ 的顏色應該是和 hint text 的 color 是一樣的。

這樣才會有“想要就要”的感覺,而不是“我要我要”的感覺。

構建我們的 EditText

建立新的 class。繼承自 AppCompatEditText 而非 EditText,以確保我們在所有的裝置上都有和 Android5.0 一樣的效果。

public class ClearEditText extends AppCompatEditText implements View.OnTouchListener, View.OnFocusChangeListener, TextWatcher {

建立建構函式和進行初始化:

public ClearEditText(final Context context) {
    super(context);
    init(context);
}

public ClearEditText(final Context context, final AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

public ClearEditText(final Context context, final AttributeSet attrs, final int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
}

在 init 方法中,定義一個清除的圖示。由於我們用了 support library。所以能夠使用這個圖示:abc_ic_clear_mtrl_alpha。可是由於它是白色的,所以我們使用著色(tint)的辦法讓它在 Android5.0 曾經的裝置上也能工作。用 DrawableCompat 類來完畢我們的工作:

private void init(final Context context) {
    final Drawable drawable = ContextCompat.getDrawable(context, R.drawable.abc_ic_clear_mtrl_alpha);
    final Drawable wrappedDrawable = DrawableCompat.wrap(drawable); //Wrap the drawable so that it can be tinted pre Lollipop
    DrawableCompat.setTint(wrappedDrawable, getCurrentHintTextColor());
    mClearTextIcon = wrappedDrawable;
    mClearTextIcon.setBounds(0, 0, mClearTextIcon.getIntrinsicHeight(), mClearTextIcon.getIntrinsicHeight());
    setClearIconVisible(false);
    super.setOnTouchListener(this);
    super.setOnFocusChangeListener(this);
    addTextChangedListener(this);
}

setClearIconVisible 來處理 EditText 的清除圖示是否是顯示。我們通過焦點的變化和文字的觀察(text watchers)來決定顯示與否。

private void setClearIconVisible(final boolean visible) {
    mClearTextIcon.setVisible(visible, false);
    final Drawable[] compoundDrawables = getCompoundDrawables();
    setCompoundDrawables(
            compoundDrawables[0],
            compoundDrawables[1],
            visible ? mClearTextIcon : null,
            compoundDrawables[3]);
}

這當然意味著使用這種控制你就不應該自己去設定 right 的 compound drawable,由於它總是會被重寫為 ‘x’ 或者 null。當文字改變或者焦點變化的時候。

你能夠使用 TextInputLayout 來包裹 EditText。

這是非常棒的。

監聽其它的控制

細心的你肯定已經注意到了,在 init 方法中我呼叫了父類的 Touch,和 Focus 的監聽事件。

這麼做是由於,我想重寫標準的設定器,這樣我們就能第一時間捕獲這些監聽。這樣就能應用到我們的邏輯裡,或者在邏輯裡去設定監聽。通過這種方式,假設我們用 TextInputLayout 包裹住了 EditText, 然後有 focus 監聽。

這種話這些設定在 EditText 也仍然會被 fired。

我們不須要 text watchers,由於你已經有了多個的 multiple text watchers 在編輯文字上。

@Override
public void setOnFocusChangeListener(final OnFocusChangeListener onFocusChangeListener) {
    mOnFocusChangeListener = onFocusChangeListener;
}

@Override
public void setOnTouchListener(final OnTouchListener onTouchListener) {
    mOnTouchListener = onTouchListener;
}

為這個新類定義一些欄位:

private Drawable mClearTextIcon;
private OnFocusChangeListener mOnFocusChangeListener;
private OnTouchListener mOnTouchListener;

終於

實現3個監聽。首先是 focus:

@Override
public void onFocusChange(final View view, final boolean hasFocus) {
    if (hasFocus) {
        setClearIconVisible(getText().length() > 0);
    } else {
        setClearIconVisible(false);
    }
    if (mOnFocusChangeListener != null) {
        mOnFocusChangeListener.onFocusChange(view, hasFocus);
    }
}

這樣當 focus 改變的時候,假設 editText 是獲得焦點的,而且文字內容不為空,我們就顯示 清除的 icon,否則。我們就設定為 null(即不顯示 icon),在這兩種情況下。我們要對其它所有的 focus 改變進行監聽,來確保它的邏輯是正常進行的。

onTouch 事件:

@Override
public boolean onTouch(final View view, final MotionEvent motionEvent) {
    final int x = (int) motionEvent.getX();
    if (mClearTextIcon.isVisible() && x > getWidth() - getPaddingRight() - mClearTextIcon.getIntrinsicWidth()) {
        if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
            setText("");
        }
        return true;
    }
    return mOnTouchListener != null && mOnTouchListener.onTouch(view, motionEvent);
}

我們先檢查清除的 icon 是否是顯示的。然後當我們點選右側區域,我們就應該始終消耗這個事件,我們不希望其它的 touch 監聽獲取到。最後,假設使用者的手指是從 ‘x’ 所在的區域抬起來的。我們就要去清除文字。

假設 ‘x’ 是不顯示的或者 onTouch 事件不是在 right drawable 區域內的,我們就應該處理其它的相應的 touch 監聽。

最後是簡單的 TextWatcher:

@Override
public final void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
    if (isFocused()) {
        setClearIconVisible(s.length() > 0);
    }
}

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

@Override
public void afterTextChanged(Editable s) {
}

這樣,我們就擁有了帶有 ‘x’ 可清除的全功能的的 EditText。你能夠在你的佈局中替換常規的 EditText 了。

假設你是使用 AutoCompleteTextView 。也是能夠改動的。僅僅要該類名為例如以下即可了:

public class ClearableAutoCompleteTextView extends AppCompatAutoCompleteTextView implements View.OnTouchListener, View.OnFocusChangeListener, TextWatcher {

相關文章