和我一起实现EditText一键清空功能

作者: 积木Blocks | 来源:发表于2016-06-21 19:35 被阅读6120次

    在实际项目中我们经常看到这样的效果:


    一键清除功能

    这就是我们常说的一键清除功能,Android并没有自带的API供我们使用,所以我们需要自己来编写,下面我将介绍常见实现方式.


    1.常见的实现方式

    目前主要实现的方式有2种:

    2种方式的优劣

    • 第一种方式:实现的方式简单,容易上手,但是增加了布局的复杂度
    • 第二种方式:拓展EditText有一定的难度,可以使用起来会方便很多

    2.自定义EditText实现一键清除功能

    这里我会基于第二种实现方式一步步实现一键清除的EditText控件,相信你也会选择第二种实现方式吧,毕竟一切为了优化(偷懒)。

    步骤1:创建一个控件继承于EditText并实现其构造方法
    public class OneKeyClearEditText extends AppCompatEditText {
    
        public OneKeyClearEditText(Context context) {
            this(context, null);
        }
    
        public OneKeyClearEditText(Context context, AttributeSet attrs) {
            this(context, attrs, R.attr.editTextStyle);
        }
    
        public OneKeyClearEditText(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    

    特别说明:R.attr.editTextStyle是源码中AppCompatEditText,由于使用了AppCompatEditText,所以我们需要依赖V7包,如果没有可以简信我,主题也需要是Theme.AppCompat,我这里为了看的清楚在AndroidManifest.xml中使用的主题是:Theme.AppCompat.Light。

    步骤2:利用EditText的CompoundDrawables设置删除图标
    private Drawable mClearDrawable;// 一键删除的按钮
    private int colorAccent;//获得主题的颜色
    @SuppressLint("InlinedApi")
        public OneKeyClearEditText(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            TypedArray array = context.getTheme()
                    .obtainStyledAttributes(new int[] {android.R.attr.colorAccent});
            colorAccent = array.getColor(0, 0xFF00FF);
            array.recycle();
            initClearDrawable(context);
        }
    
        @SuppressLint("NewApi")
        private void initClearDrawable(Context context) {
            mClearDrawable = getCompoundDrawables()[2];// 获取EditText的DrawableRight,假如没有设置我们就使用默认的图片
            if (mClearDrawable == null) {
                mClearDrawable = getResources().getDrawable(R.drawable.ic_delete_01, context.getTheme());
            }
            DrawableCompat.setTint(mClearDrawable, colorAccent);//设置删除按钮的颜色和TextColor的颜色一致
            mClearDrawable.setBounds(0, 0, (int) getTextSize(), (int) getTextSize());//设置Drawable的宽高和TextSize的大小一致
            setClearIconVisible(true);
    
        }
    
        /**
         * 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去
         *
         * @param visible
         */
        private void setClearIconVisible(boolean visible) {
            Drawable right = visible ? mClearDrawable : null;
            setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], right, getCompoundDrawables()[3]);
        }
    

    这一步主要是为了设置删除的图标,图标的颜色和colorAccent颜色一致,大小和TextSize一致,解决适配的问题。

    先在布局设置colorAccent为红色

        <!-- Application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.Light">
            <!-- All customizations that are NOT specific to a particular API-level can go here. -->
            <item name="colorAccent">#ff0000</item>
        </style>
    

    下面我们看看效果图:

    一键清除样式.png
    效果还是不错的,到这里基本的UI样式就完成了,目前的控件还只是个“花瓶”,点击的方法还没有实现,接下来我们来实现点击的事件。
    步骤3:实现删除图标的点击事件

    我们知道Drawable本身没有点击事件,那么怎么办呢?这里从写了onTouch事件,然后模拟点击的区域(图标大小)来现实点击事件:

        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            if (mClearDrawable != null && event.getAction() == MotionEvent.ACTION_UP) {
                int x = (int) event.getX();
                // 判断触摸点是否在水平范围内
                boolean isInnerWidth = (x > (getWidth() - getTotalPaddingRight()))
                        && (x < (getWidth() - getPaddingRight()));
                // 获取删除图标的边界,返回一个Rect对象
                Rect rect = mClearDrawable.getBounds();
                // 获取删除图标的高度
                int height = rect.height();
                int y = (int) event.getY();
                // 计算图标底部到控件底部的距离
                int distance = (getHeight() - height) / 2;
                // 判断触摸点是否在竖直范围内(可能会有点误差)
                // 触摸点的纵坐标在distance到(distance+图标自身的高度)之内,则视为点中删除图标
                boolean isInnerHeight = (y > distance) && (y < (distance + height));
                if (isInnerHeight && isInnerWidth) {
                    this.setText("");
             Toast.makeText(getContext(), "一键清除", Toast.LENGTH_SHORT).show();//为了看清效果,测试
                }
            }
            return super.onTouchEvent(event);
        }
    

    如果在EditText的范围和图标的范围之类,则表示有效。

    下面看看效果:

    一键清除.gif

    到现在已经实现了一键清除功能了,可是功能还不够完善,我们需要在获取焦点并且有文字内容的时候才显示删除图标,下面继续开始优化。

    步骤4: 对获取焦点已经文字输入进行监听优化体验
        private boolean hasFocus;// 控件是否有焦点
        
        private void initClearDrawable(Context context) {
            ...
            setClearIconVisible(true);
            // 设置焦点改变的监听
            setOnFocusChangeListener(this);
            // 设置输入框里面内容发生改变的监听
            addTextChangedListener(this);
    
        }
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            this.hasFocus = hasFocus;
            if (hasFocus) {
                setClearIconVisible(getText().length() > 0);
            } else {
                setClearIconVisible(false);
            }
            
        }
       /**
         * 当输入框里面内容发生变化的时候回调的方法
         */
        @Override
        public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
            if (hasFocus) {
                setClearIconVisible(text.length() > 0);
            }
        }
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // TODO Auto-generated method stub
            
        }
    
        @Override
        public void afterTextChanged(Editable s) {
            // TODO Auto-generated method stub
            
        }
    

    基本代码就是这样,在获取焦点和文字输入之前判断是否有文字,从而决定是否显示图标。

    ** 效果:**

    一键删除1.0.gif

    3.优化OneKeyClearEditText(一键删除控件)

    上面基本是实现了一键清除的功能,对于图标的颜色,有些人可能需要自己定义,这里补充一个方法,

    添加自定义属性

            TypedArray array2 = context.obtainStyledAttributes(attrs,R.styleable.OneKeyClearEditText);
            DrawableColor = array2.getColor(R.styleable.OneKeyClearEditText_deletecolor, colorAccent);
            array2.recycle();
                    ...     
    DrawableCompat.setTint(mClearDrawable, DrawableColor);// 设置删除按钮的颜色和TextColor的颜色一致
    

    values-attrs.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <declare-styleable name="OneKeyClearEditText">
            <attr name="deletecolor" format="reference|color" />
        </declare-styleable>
    
    </resources>
    

    使用

        <com.yh.clearedittextdemo.view.OneKeyClearEditText
            android:id="@+id/okcet"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:drawableRight="@drawable/ic_delete_03"
            android:hint="请输入手机"
            android:textCursorDrawable="@drawable/cursor"
            app:deletecolor="@android:color/holo_orange_dark" />
    

    最后修改了游标的颜色,下面展示效果:(修改游标颜色:http://www.jianshu.com/p/660e93e1064f

    最后样式

    4.源码地址

    http://download.csdn.net/detail/android_yh/9555816


    5.感谢

    参考地址:


    6.总结

    核心技术:

    • 通过对CompoundDrawables的设置来确定删除图标
    • 通过获得系统的Theme来关联删除图标和警告字体的颜色
    • 通过DrawableCompat来渲染删除图标
    • 通过对onTouch()的监听来模拟点击区域
    • 通过对View.OnFocusChangeListener, TextWatcher的监听来优化用户体验

    这是EditText系列的第三篇:
    EditText(第1章)-游标位置和样式
    EditText(第2章)-对hint进行整容

    接下来我将实现EditText的密码可见与隐藏的功能。
    如有侵权,麻烦简信我,我会及时修改。
    如果这篇文章对你有帮助,请点下“喜欢”~

    相关文章

      网友评论

      • 艾特不出先生:使用你这个自定义EditText后就怎么都获取不到焦点了
      • 一轮明月照九州:正在写自定义EditText,很有帮助
      • hackware:最外层套上FrameLayout,让按钮悬在EditText上面,居右,几行代码就搞定了,用得着这么复杂
        积木Blocks:@hackware 这个不会遮盖文字的吧,是原生的方法。
        hackware:@自导自演的机器人 节约一层?多一层FrameLayoutd带来的性能影响完全可以忽略不计,相反,你用了这么复杂的代码去实现这么简单的一个功能,浪费的工作量呢?可维护性呢?复杂度?还有,是不是还要解决文本被按钮遮盖的问题?
        积木Blocks:@hackware 就是为了节约一层。。。
      • hackware:用FrameLayout不就得了?
        积木Blocks:@hackware 具体思路是什么。这个布局我用的比较少感觉不好控制。
      • 王怀智:冒出了崇拜的眼神。
        积木Blocks:@王小槑 :relieved:我只是个菜鸡
      • Terry:还是认为创造组件的时候,组合大于继承,其实继承也不错,但是不应该把控件本身的方法给占用了,比如使用setComoundDrawable。。。
        积木Blocks:@Tatastar 组合控件实际上也是加载了好几个控件,性能消耗也比继承多一点。至于这个占用方法,是因为这个一键清除的样式基本就是如此刚好可以用上,如果你觉得不是很好,可以用方法一,然后封装成组合控件。
      • 苏武难飞:这个。。。。感觉方法一更方便一点
        积木Blocks:@满时花雨再归来 方法1的话需要在EditText外面再包裹一个LinearLayout或者其他布局,加重了布局的层次,现在看起来只有2层,可是实际中可能会更多层,比如在外面包裹一个ScrollView这是很正常的事情,从布局优化角度来说,第二种方式比较适合。
      • 小呀么小黄鸡:原生就可以实现,不必自定义view
        1.设置edittext的drawableRight
        2.mEditEdt.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
        // et.getCompoundDrawables()得到一个长度为4的数组,分别表示左右上下四张图片
        Drawable drawable = mEditEdt.getCompoundDrawables()[2];
        //如果右边没有图片,不再处理
        if (drawable == null)
        return false;
        //如果不是按下事件,不再处理
        if (event.getAction() != MotionEvent.ACTION_UP)
        return false;
        if (event.getX() > mEditEdt.getWidth()
        - mEditEdt.getPaddingRight()
        - drawable.getIntrinsicWidth()) {
        mEditEdt.setText("");
        }
        return false;
        }
        });
        积木Blocks:@小呀么小黄鸡 :grin: 一起交流,一起进步
        小呀么小黄鸡:@自导自演的机器人 恩那是当然 不过如果只是为了实现这个功能的话 这个方法更简单 感谢交流
        积木Blocks:@小呀么小黄鸡 自定义View的封装就是为了简化以后的操作,因为一个项目中输入框不止一个,所以封装起来方便以后的使用。
      • 11f5b58cf509:DrawableCompat.setTint(mClearDrawable, DrawableColor);设置了这句话为什么删除图标颜色没有改变?你的Demo也时一样的
        积木Blocks: @11f5b58cf509 你用的什么版本的手机
      • SScience:楼主放github啊 :+1:
        积木Blocks:@幸运Science 多谢建议。
        SScience:@自导自演的机器人 :+1: csdn的下载渣渣,放github可以start、fork提issues
        积木Blocks:@幸运Science :grin:之后整理了会考虑的。打算整个下密码是否可见等功能,让这个控件可定制化强一点。
      • Mi氓:留名,学习
        积木Blocks:@Mi氓 :smirk_cat:

      本文标题:和我一起实现EditText一键清空功能

      本文链接:https://www.haomeiwen.com/subject/atywdttx.html