安卓开发之自定义控件实现MaterialEditText


按照常规先展示效果图并说明:

主要有以下几个效果:

  • edittext中的内容为0时,标签不出现
  • edittext中的内容不为0时,标签开始出现
  • edittext获取焦点时,下划线的颜色/粗细会发生改变
  • edittext失去焦点时,下划线同样发生改变,标签颜色也更改
  • 当限制输入字符个数时,右下角计数文字会进行计数
  • 超过个数后,下划线和计数文字颜色会发生变化

一、继承EditText

为保留EditText的一些独特属性,我们选择继承EditText来实现自定义MaterialEditText.

但在原生的EditText动手脚绘制一些文字/线,我们需要进行一些预处理:

  1. 原生EditText本身含有下划线,我们需要将原生下划线去掉,这里可以通过设置EditView背景为null来实现

    // 设置edittext的背景为空,主要为了隐藏自带的下划线
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    setBackground(null);
    } else {
    setBackgroundDrawable(null);
    }
  2. 要绘制文字/下划线,我们需要获得坐标。而EditText的内部是可以滑动的,随着文字的增加,高度也会自动的增长。这里我们获取坐标时,使用 getScrollX()getScrollY() 这两个函数获取输入状态下,我们需要的不断变化的横坐标和纵坐标:

    // 获取下划线的起点纵坐标y
    lineStartY = getScrollY()+getHeight()-getPaddingBottom()+dpToPix(5);

    // 获取下划线的起点横坐标x / 标签文字的横坐标x
    getScrollX()

    ...其他类似
  3. 在系统开始绘制MaterialEditText之前,我们需要重新设置原生EditText的padding值,为什么需要重新设置padding值呢?因为我们在原生EditText上绘制文字和下划线等,占用了输入框的高度。重新设置padding值后,我们可以在padding的地方绘制我们的文字和下划线。一般我们设置顶部和底部的padding值就好了

    // 额外的顶部内边距
    private int extraTopPadding;
    // 额外的底部内边距
    private int extraBottomPadding;

    // 获取额外的顶部内边距:用于绘制文字和文字的margin
    extraTopPadding = (int) getTextHeight(textPaint) + dpToPix(4);

    // 获取额外的底部内边距:用于绘制底部的计数的文字和文字的margin
    extraBottomPadding = (int) getTextHeight(textPaint) + dpToPix(6);

    // 重新设置内边距

    // 因为我们需要绘制标签和下划线,因此需要重新设置padding值
    private void correctPaddings() {

    super.setPadding(getPaddingLeft(),getPaddingTop()+extraTopPadding,getPaddingRight(),getPaddingBottom()+extraBottomPadding+dpToPix(5));
    // 最后底部加上5dp主要是为了包含绘制下划线的空间

    }

二、自定义属性

解决了上面的问题后,我们需要为MaterialEditText自定义一些常用的属性:

<declare-styleable name="MaterialEditText">
<attr name="preLineColor" format="color"/>
<attr name="labelText" format="string"/>
<attr name="labelColor" format="color"/>
<attr name="maxLength" format="integer"/>
<attr name="overlengthColor" format="color"/>
</declare-styleable>
  • preLineColor : 获取焦点后的下划线颜色

  • labelText :标签的文字

  • labelColor :标签文字的颜色

  • maxLength :最大的字数限制。-1是为不限制字数

  • overlengthColor :超过限制后的下划线和右下角计数文字的颜色

三、设置标签的动画

从上面的Gif动画可知,label有一个浮现和浮走的动画,这里主要使用了属性动画获取标签文字的透明度和移动的高度比例:

    // 初始化标签动画
labelAnim = ValueAnimator.ofFloat(0, 255);
labelAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 获取标签的透明度
textAlpha = (float) animation.getAnimatedValue();
// 获取文字的高度比例:
// 当透明度为0时,高度比例为1.5,透明度为255时,高度为1
// 达到一种从底部浮现的效果
yFraction = (float) (-(5.0f / 2550.0f) * textAlpha + 1.5);
// 重绘
invalidate();
}
});

我们发现,浮出动画和浮走动画正是一对相互逆序播放的动画,所以我们只设置一个动画即可,之后再进行逆序播放达到浮走的效果

四、对EditText设置监听

因输入字符的个数不同/是否得到焦点的不同,设置了 下划线 / 标签文字 / 右下角计数文字 设置不同的颜色和对标签文字播放动画:

private void initListener() {

/// 设置edittetx内容改变监听器
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) {

}

@Override
public void afterTextChanged(Editable s) {

// 当内容改变后,是否展示计数内、判断是否已经超过规定字符长度
if(maxCount!=-1) {

// maxCount = -1时,不限制字数且不显示计数文字

// 添加计数字符串
countString.delete(0, countString.length());
presentCount = s.length();
countString.append(presentCount);
countString.append(" / ");
countString.append(maxCount);

// 超过规定长度时,绘制的颜色发生变化
if (presentCount > maxCount) {
paint.setColor(overLengthColor);
countPaint.setColor(overLengthColor);
} else {
paint.setColor(preLineColor);
countPaint.setColor(Color.LTGRAY);
}
}

// 当内容长度为0并获得焦点时:
if(s.length()==0&&isShow&&isFocused()){
// 将标签动画逆序播放,将标签隐藏
labelAnim.reverse();
isShow = false;

}else if(s.length()!=0&&!isShow&&isFocused()){
// 当内容长度不为0时,播放标签浮出一次
labelAnim.start();
isShow = true; // 防止播放多次动画

}
}
});

// 添加焦点的获取通知
setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
// 获取焦点时
if(hasFocus) {
// 改变下划线的粗细
paint.setStrokeWidth(dpToPix(1.3f));
// 改变标签的颜色
textPaint.setColor(labelColor);

if(presentCount>maxCount&&maxCount!=-1){
// 超出字符长度时,设置画笔颜色
paint.setColor(overLengthColor);
countPaint.setColor(overLengthColor);
}else {
// 不超出字符长度/不设置规定长度时,设置画笔颜色
paint.setColor(preLineColor);
countPaint.setColor(Color.LTGRAY);
}

}else {
// 没有获取焦点时,改变下划线的颜色和粗细和标签颜色
textPaint.setColor(Color.LTGRAY);
paint.setColor(Color.LTGRAY);
paint.setStrokeWidth(dpToPix(0.35f));
}

}
});
}

五、最后的onDraw()

对标签的绘制 + 对下划线的绘制 + (对右下角计数文字的绘制) + 原生EditTetx的绘制:

    @Override
protected void onDraw(Canvas canvas) {

// 获取下划线的起点高度
lineStartY = getScrollY()+getHeight()-getPaddingBottom()+dpToPix(5);

// 设置标签的透明度
textPaint.setAlpha((int) textAlpha);
// 根据标签的高度比例绘制标签文字
canvas.drawText(labelText, getScrollX(),(getScrollY()-dpToPix(4)+extraTopPadding)*yFraction, textPaint);

// 绘制下划线
canvas.drawRect(getScrollX(), lineStartY, getScrollX()+getWidth()-getPaddingRight(), lineStartY + dpToPix(0.8f), paint);

// 根据是否有字符长度规定绘制右下角的计数
if(maxCount!=-1) {
canvas.drawText(countString.toString(), getScrollX()+getWidth()-getPaddingRight() - getTextWidth(countString.toString(), countPaint), lineStartY + extraBottomPadding, countPaint);
}

// 开始edittext原生的绘制
super.onDraw(canvas);

}

六、xml使用示例:

这里的MaterialEditText使用如上述效果图Gif:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent">



<LinearLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp"
android:background="#ffffff"
tools:context="com.cxmscb.cxm.newtest.MainActivity">

<com.cxmscb.cxm.newtest.MaterialEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:maxLength = "12"
app:labelText="用户名"
app:labelColor="#18b4ed"
app:preLineColor="#18b4ed"
app:overlengthColor="@android:color/holo_red_light"
android:hint="用户名"
android:singleLine="true"
android:layout_marginTop="10dp"
android:paddingLeft="0dp"
android:textSize="18sp"/>

<com.cxmscb.cxm.newtest.MaterialEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:maxLength = "12"
app:labelText="密码"
app:labelColor="#18b4ed"
app:preLineColor="#18b4ed"
app:overlengthColor="@android:color/holo_red_light"
android:hint="密码"
android:singleLine="true"
android:password="true"
android:layout_marginTop="10dp"
android:paddingLeft="0dp"
android:textSize="18sp"/>

<com.cxmscb.cxm.newtest.MaterialEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:maxLength = "-1"
app:labelText="无限制输入"
app:labelColor="#e2d66f"
app:preLineColor="#e2d66f"
android:hint="无限制输入"
android:layout_marginTop="10dp"
android:paddingLeft="0dp"
android:textSize="18sp"/>



<com.cxmscb.cxm.newtest.MaterialEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/avatar"
android:drawablePadding="4dp"
app:maxLength = "12"
app:labelText="用户名"
app:labelColor="#18b4ed"
app:preLineColor="#18b4ed"
app:overlengthColor="@android:color/holo_red_light"
android:hint="用户名"
android:singleLine="true"
android:layout_marginTop="10dp"
android:paddingLeft="0dp"
android:textSize="18sp"/>



</LinearLayout>
</ScrollView>

七、完整源码和项目: Github

因本人太懒,没有做成库,需要的可以直接提取/学习。

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告