带有一键清空功能的EditText
Posted Shen_JC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带有一键清空功能的EditText相关的知识,希望对你有一定的参考价值。
介绍
很常见的一个功能,大部分app在登录界面都会实现这个功能了。因为在掘金上看了一篇类似的文章,所以决定自己实践一下。
下图为实现效果:
常见实现方法
- 组合控件,EditText + Button
实现简单,可以单独使用。 - 自定义View,继承EditText,通过EditText自带的Drawable来实现。
布局复杂度低
继承EditText来实现一键清功能
需要考虑的问题
根据业务场景,有以下几个问题需要我们在实现中考虑到:
1. 怎么添加清空按钮
2. 怎么处理点击事件
3. 处理清空按钮的显示状态
3.1 有文字时才显示清空按钮,没有文字则掩藏。
3.2 获取焦点时才显示清空按钮,没有焦点时则隐藏
3.3 EditText的setErrot方法调用后,清空按钮怎么处理
4. 添加自定义的属性
实现流程
带着上面的问题我们开始一步步实现自定义View。
步骤1:继承EditText,实现构造方法
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中有一个静态内部类Drawables, 这个类的作用是用来保存和显示TextView上下左右的Drawable。通过android:drawable*
来设置的drawable icon就保存在Drawables中。
通过调用 Drawable drawables[] = getCompoundDrawables();
方法可以获取到Drawables中的left, top, right, and bottom的Drawable数组。而drawables[2]正好就是显示在TextView右边的drawable icon,所以这个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);
步骤三:处理点击事件
由于Drawable没办法接收处理TouchEvent,所以我们只能通过触摸区域来判断,当触摸事件的坐标在right drawable的范围内的时候就触发点击事件。
覆写onTouchEvent事件,这里我只判断了x轴的范围。那为什么不加上y轴的判断呢?个人认为没什么必要。
@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, "点击清除按钮!");
clearText();
return super.onTouchEvent(event);
步骤四:处理清空按钮的显示状态
有三种情况需要考虑:
1 有文字时才显示清空按钮,没有文字则掩藏。
2 获取焦点时才显示清空按钮,没有焦点时则隐藏
3 EditText的setErrot方法调用后,清空按钮怎么处理
为了解决1和2的两个问题,我们需要为EditText监听文字输入的状态和获取失去焦点的状态,因此我们需要实现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的具体实现:
我们可以通过setCompoundDrawables来设置TextView的left、top、right、bottom drawable。传递null表示不需要显示。
/**
* 设置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方法可以给出校验提示,从下图中大致可以看出这个提示由两部分组成,一个icon和一个popup window,而这个icon正好占据了我们上面提到的right drawable的位置也就是我们的清除按钮需要用到的位置。那么error icon是不是也用了right drawable来实现的呢?
在TextView中有这样一个方法applyErrorDrawableIfNeeded,从方法名就可以猜到它就是用来设置error drawable的,最终通过mShowing[Drawables.RIGHT] = mDrawableError;
把error drawable 放到了Drawables.RIGHT中。哦,对了。。这一切都是基于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来找到答案。当EditText中重新输入文字的后,sendAfterTextChanged会被调用,在sendAfterTextChanged方法中会调用hideErrorIfUnchanged,而 hideErrorIfUnchanged则是直接调用了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函数中获取自定义属性并做相关处理。
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);
实例效果
源码
参考
和我一起实现EditText一键清空功能
Android自定义View示例(一)—带有删除按钮的EditText
以上是关于带有一键清空功能的EditText的主要内容,如果未能解决你的问题,请参考以下文章
登录界面AutoUtils 屏幕适配自定义Edittext(显示密码可见和一键清空)和 TextInputLayout的使用。