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

来源:互联网 发布:当量直径算法 编辑:程序博客网 时间:2024/05/01 16:05

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

主要有以下几个效果:

  • 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() 这两个函数获取输入状态下,我们需要的不断变化的横坐标和纵坐标:

    // 获取下划线的起点纵坐标ylineStartY = getScrollY()+getHeight()-getPaddingBottom()+dpToPix(5); // 获取下划线的起点横坐标x / 标签文字的横坐标xgetScrollX()...其他类似
  3. 在系统开始绘制MaterialEditText之前,我们需要重新设置原生EditText的padding值,为什么需要重新设置padding值呢?因为我们在原生EditText上绘制文字和下划线等,占用了输入框的高度。重新设置padding值后,我们可以在padding的地方绘制我们的文字和下划线。一般我们设置顶部和底部的padding值就好了

    // 额外的顶部内边距private int extraTopPadding;// 额外的底部内边距private int extraBottomPadding;// 获取额外的顶部内边距:用于绘制文字和文字的marginextraTopPadding = (int) getTextHeight(textPaint) + dpToPix(4);// 获取额外的底部内边距:用于绘制底部的计数的文字和文字的marginextraBottomPadding = (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

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

http://blog.csdn.net/cxmscb/article/details/52808790 

0 0
原创粉丝点击