Android 自定义 View-->验证码输入框

Posted Kevin-Dev

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 自定义 View-->验证码输入框相关的知识,希望对你有一定的参考价值。

概述

先描述一下具体需求吧,我们在项目中可能会遇到修改用户名及密码的需求,为保证一定的完全性,服务端一般会接入短信验证码的功能。我们需要将接受到的验证码返回给服务端进行验证。可能会有以下的界面让用户输入验证码:

实现

1. 特性

  • 支持设置框数量
  • 支持设置框的风格样式
  • 支持根据状态区分框颜色
  • 基于EditText实现,更优雅

2. 效果图

3. 属性

4. 代码

  • attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SplitEditText">
        <attr name="setStrokeWidth" format="dimension"/>
        <attr name="setBorderColor" format="color"/>
        <attr name="setInputBorderColor" format="color"/>
        <attr name="setFocusBorderColor" format="color"/>
        <attr name="setBoxBackgroundColor" format="color"/>
        <attr name="setBorderCornerRadius" format="dimension"/>
        <attr name="setBorderSpacing" format="dimension"/>
        <attr name="setMaxLength" format="integer"/>
        <attr name="setBorderStyle" format="enum">
            <enum name="box" value="0"/>
            <enum name="line" value="1"/>
        </attr>
        <attr name="setTextStyle" format="enum">
            <enum name="plain_text" value="0"/>
            <enum name="cipher_text" value="1"/>
        </attr>
        <attr name="setCipherMask" format="string"/>
        <attr name="setFakeBoldText" format="boolean"/>

    </declare-styleable>
</resources>
  • SplitEditText.java
public class SplitEditText extends AppCompatEditText 
    /**
     * 画笔
     */
    private Paint mPaint;

    /**
     * 画笔宽度
     */
    private float mStrokeWidth;

    /**
     * 边框颜色
     */
    private int mBorderColor = 0xFF666666;
    /**
     * 输入的边框颜色
     */
    private int mInputBorderColor = 0xFF1E90FF;
    /**
     * 焦点的边框颜色
     */
    private int mFocusBorderColor;

    /**
     * 框的背景颜色
     */
    private int mBoxBackgroundColor;

    /**
     * 框的圆角大小
     */
    private float mBorderCornerRadius;

    /**
     * 框与框之间的间距大小
     */
    private float mBorderSpacing;

    /**
     * 输入框宽度
     */
    private float mBoxWidth;

    /**
     * 输入框高度
     */
    private float mBoxHeight;

    /**
     * 允许输入的最大长度
     */
    private int mMaxLength = 6;

    /**
     * 文本长度
     */
    private int mTextLength;
    /**
     * 路径
     */
    private Path mPath;

    private RectF mRectF;
    private float[] mRadiusFirstArray;
    private float[] mRadiusLastArray;

    /**
     * 边框风格
     */
    private @BorderStyle int mBorderStyle = BorderStyle.BOX;

    /**
     * 边框风格
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(BorderStyle.BOX, BorderStyle.LINE)
    public @interface BorderStyle 
        /**
         * 框
         */
        int BOX = 0;
        /**
         * 线
         */
        int LINE = 1;
    

    /**
     * 文本风格
     */
    private @TextStyle int mTextStyle = TextStyle.PLAIN_TEXT;


    /**
     * 文本风格
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(TextStyle.PLAIN_TEXT, TextStyle.CIPHER_TEXT)
    public @interface TextStyle 
        /**
         * 明文
         */
        int PLAIN_TEXT = 0;
        /**
         * 密文
         */
        int CIPHER_TEXT = 1;
    

    /**
     * 密文掩码
     */
    private String mCipherMask;

    /**
     * 是否是粗体
     */
    private boolean isFakeBoldText;

    private static final String DEFAULT_CIPHER_MASK = "*";

    private boolean isDraw;

    private OnTextInputListener mOnTextInputListener;

    public SplitEditText(@NonNull Context context) 
        this(context,null);
    

    public SplitEditText(@NonNull Context context, @Nullable AttributeSet attrs) 
        this(context, attrs,android.R.attr.editTextStyle);
    

    public SplitEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    


    private void init(@NonNull Context context, @Nullable AttributeSet attrs)

        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        mStrokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1f,displayMetrics);
        mBorderSpacing = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,8f,displayMetrics);
        setPadding(0,0,0,0);

        TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.SplitEditText);
        final int count = a.getIndexCount();
        for (int i = 0; i < count; i++) 
            int attr = a.getIndex(i);
            if(attr == R.styleable.SplitEditText_setStrokeWidth)
                mStrokeWidth = a.getDimension(attr,mStrokeWidth);
            else if (attr == R.styleable.SplitEditText_setBorderColor)
                mBorderColor = a.getColor(attr,mBorderColor);
            else if (attr == R.styleable.SplitEditText_setInputBorderColor)
                mInputBorderColor = a.getColor(attr,mInputBorderColor);
            else if (attr == R.styleable.SplitEditText_setFocusBorderColor)
                mFocusBorderColor = a.getColor(attr,mFocusBorderColor);
            else if (attr == R.styleable.SplitEditText_setBoxBackgroundColor)
                mBoxBackgroundColor = a.getColor(attr,mBoxBackgroundColor);
            else if (attr == R.styleable.SplitEditText_setBorderCornerRadius)
                mBorderCornerRadius = a.getDimension(attr,mBorderCornerRadius);
            else if (attr == R.styleable.SplitEditText_setBorderSpacing)
                mBorderSpacing = a.getDimension(attr,mBorderSpacing);
            else if (attr == R.styleable.SplitEditText_setMaxLength)
                mMaxLength = a.getInt(attr,mMaxLength);
            else if (attr == R.styleable.SplitEditText_setBorderStyle)
                mBorderStyle = a.getInt(attr,mBorderStyle);
            else if (attr == R.styleable.SplitEditText_setTextStyle)
                mTextStyle = a.getInt(attr,mTextStyle);
            else if (attr == R.styleable.SplitEditText_setCipherMask)
                mCipherMask = a.getString(attr);
            else if (attr == R.styleable.SplitEditText_setFakeBoldText)
                isFakeBoldText = a.getBoolean(attr,false);
            
        

        a.recycle();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);
        mPaint.setTextAlign(Paint.Align.CENTER);

        mPath = new Path();
        mRadiusFirstArray = new float[8];
        mRadiusLastArray = new float[8];
        mRectF = new RectF(0,0,0,0);

        if(TextUtils.isEmpty(mCipherMask))
            mCipherMask = DEFAULT_CIPHER_MASK;
        else if(mCipherMask.length() > 1)
            mCipherMask = mCipherMask.substring(0,1);
        

        setBackground(null);
        setCursorVisible(false);
        setFilters(new InputFilter[]new InputFilter.LengthFilter(mMaxLength));
    


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
        super.onSizeChanged(w, h, oldw, oldh);

        int width = w - getPaddingLeft() - getPaddingRight();
        int height = h - getPaddingTop() - getPaddingBottom();
        updateSizeChanged(width,height);
    

    private void updateSizeChanged(int width,int height)
        //如果框与框之间的间距小于0或者总间距大于控件可用宽度则将间距重置为0
        if(mBorderSpacing < 0 || (mMaxLength - 1) * mBorderSpacing > width)
            mBorderSpacing = 0;
        
        //计算出每个框的宽度
        mBoxWidth = (width - (mMaxLength - 1) * mBorderSpacing) / mMaxLength - mStrokeWidth;
        mBoxHeight = height - mStrokeWidth;
    

    @Override
    protected void onDraw(Canvas canvas) 
        //移除super.onDraw(canvas);不绘制EditText相关的
        //绘制边框
        drawBorders(canvas);
    

    private void drawBorders(Canvas canvas)
        isDraw = true;
        //遍历绘制未输入文本的框边界
        for(int i = mTextLength; i < mMaxLength; i++)
            drawBorder(canvas,i,mBorderColor);
        

        int color = mInputBorderColor != 0 ? mInputBorderColor : mBorderColor;
        //遍历绘制已输入文本的框边界
        for(int i = 0; i < mTextLength; i++)
            drawBorder(canvas,i,color);
        

        //绘制焦点框边界
        if(mTextLength < mMaxLength && mFocusBorderColor != 0 && isFocused())
            drawBorder(canvas,mTextLength,mFocusBorderColor);
        
    

    private void drawBorder(Canvas canvas,int position,int borderColor)
        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setFakeBoldText(false);
        mPaint.setColor(borderColor);

        //计算出对应的矩形
        float left = getPaddingLeft() + mStrokeWidth / 2 + (mBoxWidth + mBorderSpacing) * position;
        float top = getPaddingTop() + mStrokeWidth / 2;
        mRectF.set(left,top,left + mBoxWidth,top + mBoxHeight);

        //边框风格
        switch (mBorderStyle)
            case BorderStyle.BOX:
                drawBorderBox(canvas,position,borderColor);
                break;
            case BorderStyle.LINE:
                drawBorderLine(canvas);
                break;
        
        if(mTextLength > position && !TextUtils.isEmpty(getText()))
            drawText(canvas,position);
        
    

    private void drawText(Canvas canvas,int position)
        mPaint.setStrokeWidth(0);
        mPaint.setColor(getCurrentTextColor());
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setTextSize(getTextSize());
        mPaint.setFakeBoldText(isFakeBoldText);
        float x = mRectF.centerX();
        //y轴坐标 = 中心线 + 文字高度的一半 - 基线到文字底部的距离(也就是bottom)
        float y = mRectF.centerY() + (mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top) / 2 - mPaint.getFontMetrics().bottom;
        switch (mTextStyle)
            case TextStyle.PLAIN_TEXT:
                canvas.drawText(String.valueOf(getText().charAt(position)),x,y,mPaint);
                break;
            case TextStyle.CIPHER_TEXT:
                canvas.drawText(mCipherMask,x,y,mPaint);
                break;
        
    

    /**
     * 绘制框风格
     * @param canvas
     * @param position
     */
    private void drawBorderBox(Canvas canvas,int position,int borderColor)
        if(mBorderCornerRadius > 0)//当边框带有圆角时
            if(mBorderSpacing == 0)//当边框之间的间距为0时,只需要开始一个和最后一个框有圆角
                if(position == 0 || position == mMaxLength - 1)Android 自定义验证码输入框(支持粘贴连续性)

Android 自定义验证码输入框(支持粘贴连续性)

Android 自定义验证码输入框(支持粘贴连续性)

android自定义view,打造绚丽的验证码

uniapp自定义验证码输入框,隐藏光标

微信小程序-----自定义验证码实现