输入框设置右侧清空输入框图标(原生drawableEnd图标方法)

Author Avatar
Zhu Yuexin Aug 11, 2017

功能需求

  • 当输入框没有输入时,不显示清除图标
  • 当有字符输入时,显示清除图标
  • 失去焦点时隐藏图标,取得焦点并且输入框中有字符时显示图标
  • 点击清除图标时清空输入框,并且隐藏图标

实现过程

原生EditText有自带drawableEnd属性,即可以设置一个右侧图标(其实有drawableStart, drawableTop, drawableEnd, drawableBottom四种属性,分别对应左、上、右、下侧图标)。但是Google并没有对drawableEnd图标提供相应的点击事件,点击图标无法执行相关操作。

针对上面的需求,大多数人都选择自定义EditText实现,但个人觉得比较麻烦。一番搜索之后发现一个方法,可以通过判断EditText的点击位置,即如果是图标所在位置,就执行相关操作,以此来实现上述需求。

EditText点击位置监听

public class InputClearOnTouchListener implements View.OnTouchListener {
    private EditText editText;

    public InputClearOnTouchListener(EditText editText){
        this.editText = editText;
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        //获取右侧图标
        Drawable drawableRight = editText.getCompoundDrawables()[2];
        //如果右侧没有图标,则不做处理
        if (drawableRight == null){
            return false;
        }else if (motionEvent.getAction() != MotionEvent.ACTION_UP){
            //如果不是按下事件,不做处理
            return false;
        }else if (motionEvent.getX() > editText.getWidth()-editText.getPaddingEnd()-drawableRight.getIntrinsicWidth()){
            //如果点击的位置在右侧图标处,则清空输入框
            editText.setText("");
        }
        return false;
    }
}

上面的代码实现了需求4,针对需求1,2可以通过监听EditText的输入来实现。在此之前,有一个地方需要注意,就是通过代码设置EditText的右侧图标:

editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, context.getDrawable(R.drawable.ic_cancel), null);
或者
editText.setCompoundDrawablesWithIntrinsicBounds(null, null, context.getDrawable(R.drawable.ic_cancel), null);

其他几个方法似乎无法设置图标:

editText.setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom);
editText.setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom);

具体原因暂时没有深入探究。

EditText输入监听

public class InputClearOnTextChangedListener implements TextWatcher {
    private Context context;
    private EditText editText;

    public InputClearOnTextChangedListener(Context context, EditText editText){
        this.context = context;
        this.editText = editText;
    }

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void afterTextChanged(Editable editable) {
        //当输入框有字符输入时,显示清除图标,没有字符时隐藏清除图标
        if (editable.length()>0){
            editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, context.getDrawable(R.drawable.ic_cancel), null);
            //editText.setCompoundDrawablesWithIntrinsicBounds(null, null, context.getDrawable(R.drawable.ic_cancel), null);
        }else {
            editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null);
            //editText.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
        }
    }
}

对于需求3,可以监听EditText的焦点来实现

EditText焦点监听

public class InputClearOnFocusChangeListener implements View.OnFocusChangeListener {
    private Context context;
    private EditText editText;

    public InputClearOnFocusChangeListener(Context context, EditText editText){
        this.context = context;
        this.editText = editText;
    }

    @Override
    public void onFocusChange(View view, boolean hasFocus) {
        //失去焦点时隐藏图标,取得焦点且输入框中有字符时,显示图标
        if (hasFocus && editText.getText().length()>0){
            editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, context.getDrawable(R.drawable.ic_cancel), null);
        }else {
            editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null);
        }
    }
}

在Activity中对相应的EditText绑定监听器

//当输入框有字符输入时,显示清除图标,没有字符时隐藏清除图标
usernameInput.addTextChangedListener(new InputClearOnTextChangedListener(this, usernameInput));
//根据点击位置设置清除图标的功能(drawableRight没有点击事件)
usernameInput.setOnTouchListener(new InputClearOnTouchListener(usernameInput));
//失去焦点时隐藏图标,取得焦点且输入框中有字符时,显示图标
usernameInput.setOnFocusChangeListener(new InputClearOnFocusChangeListener(this, usernameInput));
passInput.addTextChangedListener(new InputClearOnTextChangedListener(this, passInput));
passInput.setOnTouchListener(new InputClearOnTouchListener(passInput));
passInput.setOnFocusChangeListener(new InputClearOnFocusChangeListener(this, passInput));

实现效果

代码改进

将三个监听器的代码写在一个类里,并且提供一个静态方法绑定监听器,减少代码量

public class InputClearListener implements View.OnTouchListener, TextWatcher, View.OnFocusChangeListener {
    private Context context;
    private EditText editText;

    public InputClearListener(Context context, EditText editText){
        this.context = context;
        this.editText = editText;
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        //获取右侧图标
        Drawable drawableRight = editText.getCompoundDrawables()[2];
        //如果右侧没有图标,则不做处理
        if (drawableRight == null){
            return false;
        }else if (motionEvent.getAction() != MotionEvent.ACTION_UP){
            //如果不是按下事件,不做处理
            return false;
        }else if (motionEvent.getX() > editText.getWidth()-editText.getPaddingEnd()-drawableRight.getIntrinsicWidth()){
            //如果点击的位置在右侧图标处,则清空输入框
            editText.setText("");
        }
        return false;
    }

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void afterTextChanged(Editable editable) {
        //当输入框有字符输入时,显示清除图标,没有字符时隐藏清除图标
        if (editable.length()>0){
            editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, context.getDrawable(R.drawable.ic_cancel), null);
            //editText.setCompoundDrawablesWithIntrinsicBounds(null, null, context.getDrawable(R.drawable.ic_cancel), null);
        }else {
            editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null);
            //editText.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
        }
    }

    @Override
    public void onFocusChange(View view, boolean hasFocus) {
        //失去焦点时隐藏图标,取得焦点且输入框中有字符时,显示图标
        if (hasFocus && editText.getText().length()>0){
            editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, context.getDrawable(R.drawable.ic_cancel), null);
        }else {
            editText.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null);
        }
    }

    /**
     * 给EditText对象绑定监听器
     * @param context EditText所在的activity
     * @param editText 需要绑定监听器的EditText
     */
    public static void addListener(Context context, EditText editText){
        InputClearListener listener = new InputClearListener(context, editText);
        editText.addTextChangedListener(listener);
        editText.setOnTouchListener(listener);
        editText.setOnFocusChangeListener(listener);
    }
}

修改Activity中绑定监听器的代码

InputClearListener.addListener(this, usernameInput);
InputClearListener.addListener(this, passInput);

代码看起来比以前的简洁了不少。