最近工做中须要一个能够删除全部字符的EditText,因此本身写了个自定义view继承Edittext,这个实现相对简单,只用到了自定义view中的部分事件。
首先咱们来看一下效果,是怎么样的:javascript
从途中能够看到总共分为两个部分,一个是标准的EditText,另外一个是右边的咱们自定义的图标,在未输入字符以前,图标是隐藏的,输入字符后,图标显示,点击图标便可删除EditText中的全部文字,同时隐藏图标。java
首先咱们须要编写一个类继承自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轴的中心位置。
咱们实现自定义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中一些基本流程,要深刻进去须要掌握的远远多于这些,下一节将继续咱们的学习。