自定义view之写一个带删除按钮的Edittext

自定义EditText的需求:

最近工做中须要一个能够删除全部字符的EditText,因此本身写了个自定义view继承Edittext,这个实现相对简单,只用到了自定义view中的部分事件。
首先咱们来看一下效果,是怎么样的:javascript

从途中能够看到总共分为两个部分,一个是标准的EditText,另外一个是右边的咱们自定义的图标,在未输入字符以前,图标是隐藏的,输入字符后,图标显示,点击图标便可删除EditText中的全部文字,同时隐藏图标。java

继承EditText

首先咱们须要编写一个类继承自EditText:android

public class ClearEditText extends android.support.v7.widget.AppCompatEditText{

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

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

    public ClearEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
       init();
    }
    private void init() {
        Log.d("顺序", "init");
        }
}复制代码

继承EditText必须实现其中的构造方法,此处咱们重写了三个,事实上只要一个便可,不定义属性时会默认设置为号为editTextStyle属性集。
咱们都知道自定义view时候一般会重写onDraw和onMeasure方法,那么这几个方法究竟是按怎样的顺序执行呢,咱们能够在代码中添加测试代码来实验一下:canvas

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("顺序", "onMeasure");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
 @Override
    protected void onDraw(Canvas canvas) {
        Log.d("顺序", "draw");
        super.onDraw(canvas);
    }复制代码

而后咱们在一个fragment中加载这个view,输出日志api

03-07 14:51:10.805 14606-14606/com.saka.customviewdemo D/顺序: init
03-07 14:51:10.820 14606-14606/com.saka.customviewdemo D/顺序: onMeasure
03-07 14:51:10.880 14606-14606/com.saka.customviewdemo D/顺序: draw复制代码

能够看到执行的顺序是按构造器—>onmeasure->onDraw来执行的。数组

设置图标

最简单的方法是让ui切图,切出不一样的分辨率,放在drawable中,直接调用。ide

此处我没有UI,我也不擅长PS,因此我用xml作了一个简单的删除按钮。函数

首先建立一个vector类型的drawableresource布局

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="16dp"
    android:height="16dp"
    android:viewportHeight="16"
    android:viewportWidth="16">
    <path
        android:pathData="M 0 8 L 16 8"
        android:strokeColor="#2c2c2c"
        android:strokeWidth="3" />
    <path
        android:pathData="M 8 0 L 8 16"
        android:strokeColor="#2c2c2c"
        android:strokeWidth="3" />

</vector>复制代码

这个图标是正方形,边长是16dp线条宽度3dp,而后作了一个十字型,大概是这个样子学习

而后再自定义一个rotate类型的drawableresource,这个也就是咱们要使用的图标资源:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/delete" android:fromDegrees="0" android:toDegrees="225" android:pivotX="50%" android:pivotY="50%"> </rotate>复制代码

这个rotaterawable设置了旋转角度是从0转到225度,旋转的中心位置在图片X轴和Y轴的中心位置。

添加Drawable

咱们实现自定义EditText的思路是占用右边的drawable位置,点击这个drawable便可触发清除字符事件。

此处咱们是在构造器中添加的Drawable,经过init()方法来加载右侧图标。那么怎样获取这个位置呢?

首先我来看一下继承关系

java.lang.Object
   ↳     android.view.View
         ↳     android.widget.TextView
               ↳     android.widget.EditText复制代码

在TextView中有这样一个属性:android:drawableRight,这个属性是设置控件右边的图标。这个属性对应的java代码是setCompoundDrawablesWithIntrinsicBounds(int,int,int,int),这三个int值的顺序对应的位置是左上右下,其中第三个位置就是drawableright属性。此处应该注意,假如xml布局中设置了drawableright属性,同时java代码中设置了setCompoundDrawablesWithIntrinsicBounds(null,null,null,null),则java代码中的设置会覆盖xml布局中的设置。

既然能设置咱们就有办法获取每个图标。经过查看api咱们找到一个方法,Drawable[] getCompoundDrawables (),注意这个方法返回的是一个drawable数组,长度是4,对应的图标位置是左上右下,即便你没有设置任何drawable,这时的四个值都为null。
另外一个方法Drawable[] getCompoundDrawablesRelative ()返回的也是一个数组,长度一样是4,对应的图标位置是start,top,end和bottom,注意和上面的方法区分。

此处为了简单起见,咱们直接在代码中设置右侧图标,覆盖xml布局中的设置,同时设置图标不可见。

private RotateDrawable drawableRotate;

 private void init() {
        Log.d("顺序", "init");        
        setIconVisible(false, getCompoundDrawables());
    }

    private void setIconVisible(boolean b, Drawable[] drawables) {
        if (b) {            
            setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], getResources().getDrawable(R.drawable.mydelete), drawables[3]);
            drawableRotate = (RotateDrawable) getCompoundDrawables()[2];
        } else {
            setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], null, drawables[3]);
        }
    }复制代码

这样,咱们的图标就引入了EditText中,只是它如今是隐藏的,咱们没法看到他。

设置图标可见与不可见

咱们的目标是在有文字时显示图标,没有文字时隐藏图标,这个时候咱们最好的方法是实现TextWatcher方法,它一共有三个

public void beforeTextChanged(CharSequence s, int start,
                                  int count, int after);
public void onTextChanged(CharSequence s, int start, int before, int count);

public void afterTextChanged(Editable s);复制代码

咱们重点关注第二个方法,这个方法在更改s的时候会用这个回调来通知你,在s中,从start位置开始的count个字符刚刚替换了before开始的的旧文本。
这里咱们就能够利用这几个参数来计算此时的状态:

@Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (s.length() == 0 && before > 0) {
            //从有文字删除到无文字的时候
            startAnimatorSetResver();
            return;
        }
        if (start == 0 && s.length() > 0) {
            //从无文字到有文字
            setIconVisible(true, getCompoundDrawables());
            setAnimator();
            startAnimatorSet();
        }
    }复制代码

注解中已经说明了两个方法的做用,动画函数稍后讲。此时试试你就能够显示和隐藏图标了。

添加消失和出现的动画

private ValueAnimator alphaAnimator = ValueAnimator.ofInt(0, 255);
private ValueAnimator rotateAnimator = ValueAnimator.ofInt(0, 10000);复制代码

此处咱们设置了两个动画,一个是用来设置通明度变化,一个是用来设置旋转角度的。drawable有两个属性能够用来设置,一个是setLevel(),这个level就是设置的旋转角度,范围是1-10000(假如你是用的是ScaleDrawable,这个level控制就是你的图片的大小)。另外一个就是setAlpha(),这个alpha就是透明度,范围是0-255。

private void setAnimator() {
        alphaAnimator.setDuration(1000);
        alphaAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                drawableRotate.setAlpha((Integer) animation.getAnimatedValue());
            }
        });

        rotateAnimator.setDuration(1000);
        rotateAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                drawableRotate.setLevel((Integer) animation.getAnimatedValue());
            }
        });
    }复制代码

而后咱们定义一个同时启动动画的集合:

private void startAnimatorSet() {
        AnimatorSet setVisible = new AnimatorSet();
        setVisible.playTogether(alphaAnimator, rotateAnimator);
        setVisible.start();
    }复制代码

这样,咱们设置图片显示的动画就完成了。当你输入字符时,就能够看到图标慢慢旋转出现了。

同理能够设置图标消失的动画,不详细写出了,能够看demo(个人代码水平有点懒,没有优化)。

设置点击事件

其实咱们此处并非真正的设置点击事件,而是经过判断用户的触摸行为来模拟点击事件:

@Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {

            case MotionEvent.ACTION_UP:
                if (getCompoundDrawables()[2] != null) {
                    if (getWidth() - getTotalPaddingRight() < event.getX() &&
                            getWidth() - getPaddingRight() > event.getX()) {
                        this.setText("");
                        Log.d("点击了图片", "图片");
                    }
                }
                break;
        }
        return super.onTouchEvent(event);
    }复制代码

咱们并非重写onTouchEvent事件,咱们只是在onTouchEvent中添加了一个在手指离开屏幕时是否正在图片上的判断,而后将内容设置为空,通过次操做之后,继续原有的onTouchEvent流程。

判断手指离开屏幕的位置的方法是这样的,api中有这样的方法:getWidth返回的是控件的宽度,getTotalPadingRight返回的是空间右边的padding,包含了drawable,getPaddingRight返回的是view右边的padding,要是包含滚动条,滚动条的宽度也在pading内。

至此咱们的自定义EditText就完成了,可使用。这篇文章只是简单的降解了一下自定义view中一些基本流程,要深刻进去须要掌握的远远多于这些,下一节将继续咱们的学习。

相关文章
相关标签/搜索