按照常规先展示效果图并说明:
主要有以下几个效果:
为保留EditText的一些独特属性,我们选择继承EditText来实现自定义MaterialEditText.
但在原生的EditText动手脚绘制一些文字/线,我们需要进行一些预处理:
原生EditText本身含有下划线,我们需要将原生下划线去掉,这里可以通过设置EditView背景为null来实现
// 设置edittext的背景为空,主要为了隐藏自带的下划线
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
setBackground(null);
} else {
setBackgroundDrawable(null);
}
要绘制文字/下划线,我们需要获得坐标。而EditText的内部是可以滑动的,随着文字的增加,高度也会自动的增长。这里我们获取坐标时,使用 getScrollX() 和 getScrollY() 这两个函数获取输入状态下,我们需要的不断变化的横坐标和纵坐标:
// 获取下划线的起点纵坐标y
lineStartY = getScrollY()+getHeight()-getPaddingBottom()+dpToPix(5);
// 获取下划线的起点横坐标x / 标签文字的横坐标x
getScrollX()
...其他类似
在系统开始绘制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();
}
});
我们发现,浮出动画和浮走动画正是一对相互逆序播放的动画,所以我们只设置一个动画即可,之后再进行逆序播放达到浮走的效果
因输入字符的个数不同/是否得到焦点的不同,设置了 下划线 / 标签文字 / 右下角计数文字 设置不同的颜色和对标签文字播放动画:
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));
}
}
});
}
对标签的绘制 + 对下划线的绘制 + (对右下角计数文字的绘制) + 原生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);
}
这里的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>
因本人太懒,没有做成库,需要的可以直接提取/学习。
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。