安卓 验证码输入框InputCode(同时支持密码输入)

继承EditText实现自定义验证码边框InputCode

1、可修改边框焦点颜色及文字颜色大小,同时还支持自定义图片。

2、键盘密码可随机仅供参考。

演示:

          

 

 

 

 



InputCodeEditText.java 自定义控件
package com.dzw.inputcode;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

/**
 * 自定义文本输入框,增加清空按钮
 */
public class InputCodeEditText extends EditText implements TextWatcher {

    private Paint paint;//绘制方框
    private Paint textPaint;//绘制字体
    private float bgCenterY;
    private OnCodeCompleteListener onCodeCompleteListener;
    /**
     * 输入框的宽高
     */
    private int tvWidthSize = dip2px(40);
    /**
     * 文本的长度
     */
    private int mTextLen = 6;
    /**
     * 是否为密码输入框
     */
    private boolean isPassWord = true;//是否为密码输入框
    /**
     * 验证码间隔
     */
    private int intervalSize = dip2px(0);
    /**
     * 文字大小
     */
    private int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics());
    /**
     * 圆角大小
     */
    private int radius = dip2px(0);
    /**
     * 文字颜色
     */
    private int mTextColor = Color.BLACK;
    /**
     * 边框颜色
     */
    private int mBorderColor = Color.GRAY;

    /**
     * 边框颜色
     */
    private int mFocusBorderColor = -1;

    /**
     * 边框样式
     * -1表示自定义图片
     */
    private int mStyle = 0;

    /**
     * 自定义密码图片选中
     */
    private int mSelect = R.mipmap.input_selelct;

    /**
     * 自定义密码图片未选中选中
     */
    private int mUnSelect = R.mipmap.input_unselect;
    /**
     * 设置paint宽度
     */
    private int mStrokeWidth;


    public InputCodeEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public InputCodeEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }


    private void init(AttributeSet attrs) {
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CodeEditText);
        intervalSize = typedArray.getDimensionPixelSize(R.styleable.CodeEditText_tvIntervalSize, intervalSize);
        radius = typedArray.getDimensionPixelSize(R.styleable.CodeEditText_radius, radius);
        textSize = typedArray.getDimensionPixelSize(R.styleable.CodeEditText_tvTextSize, textSize);
        tvWidthSize = typedArray.getDimensionPixelSize(R.styleable.CodeEditText_tvWidth, tvWidthSize);
        mTextLen = typedArray.getInt(R.styleable.CodeEditText_tvLen, mTextLen);
        isPassWord = typedArray.getBoolean(R.styleable.CodeEditText_tvIsPwd, isPassWord);
        mTextColor = typedArray.getColor(R.styleable.CodeEditText_tvTextColor, mTextColor);
        mBorderColor = typedArray.getColor(R.styleable.CodeEditText_tvBorderColor, mBorderColor);
        mFocusBorderColor = typedArray.getColor(R.styleable.CodeEditText_tvFocusBorderColor, mFocusBorderColor);
        mStyle = typedArray.getInt(R.styleable.CodeEditText_tvStyle, mStyle);
        mSelect = typedArray.getResourceId(R.styleable.CodeEditText_tvCustomSelectIcon, mSelect);
        mUnSelect = typedArray.getResourceId(R.styleable.CodeEditText_tvUnCustomSelectIcon, mUnSelect);
        mStrokeWidth = typedArray.getDimensionPixelOffset(R.styleable.CodeEditText_tvStrokeWidth, dip2px(1));
        typedArray.recycle();
        setBackgroundColor(Color.WHITE);
        // 增加文本监听器.
        paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(mStrokeWidth);
        paint.setColor(mBorderColor);

        // 增加文本监听器.
        textPaint = new Paint();
        textPaint.setColor(mTextColor);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(textSize);
        setCursorVisible(false);
        setTextSize(0);
        disableCopyAndPaste(this);
        setFilters(new InputFilter[]{new InputFilter.LengthFilter(mTextLen)});
        addTextChangedListener(this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        float bgWidth;
        bgWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
        float bgHeight;
        if (heightMode == MeasureSpec.EXACTLY) {
            bgHeight = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
        } else {
            bgHeight = tvWidthSize + dip2px(2);
        }
        bgCenterY = bgHeight / 2;
        setMeasuredDimension((int) bgWidth, (int) bgHeight);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mStyle == 0) {
            drawDefaultRect(mTextLen, canvas);
        } else if (mStyle == 1) {
            drawStatEndRadiusRect(mTextLen, canvas);
        } else if (mStyle == 2) {
            drawWeChatRect(mTextLen, canvas);
        } else if (mStyle == 3) {
            drawLineRect(mTextLen, canvas);
        } else if (mStyle == -1) {
            drawBitmap(mTextLen, canvas);
        }
        drawText(mTextLen, canvas);
    }


    /**
     * 绘制前后圆角输入框不支持焦点输入框
     */
    private void drawStatEndRadiusRect(int count, Canvas canvas) {
        if (radius == 0) {
            radius = dip2px(5);
        }
        intervalSize = 0;
        int left = (getWidth() - count * (tvWidthSize + intervalSize)) / 2;
        for (int i = 1; i < count - 1; i++) {
            RectF rectF = new RectF(left + (tvWidthSize + intervalSize) * i,
                    bgCenterY - tvWidthSize / 2, left + (tvWidthSize + intervalSize) * i + tvWidthSize, bgCenterY + tvWidthSize / 2);
            canvas.drawRoundRect(rectF, 0, 0, paint);
        }
        RectF rectF = new RectF(left, bgCenterY - tvWidthSize / 2, left + (tvWidthSize + intervalSize) * (count - 1) + tvWidthSize, bgCenterY + tvWidthSize / 2);
        canvas.drawRoundRect(rectF, radius, radius, paint);
    }

    /**
     * 绘制输入框
     */
    private void drawDefaultRect(int count, Canvas canvas) {
        int left = (getWidth() - count * (tvWidthSize + intervalSize)) / 2;
        for (int i = 0; i < count; i++) {
            setFocusColor(i);
            RectF rectF = new RectF(left + (tvWidthSize + intervalSize) * i,
                    bgCenterY - tvWidthSize / 2, left + (tvWidthSize + intervalSize) * i + tvWidthSize, bgCenterY + tvWidthSize / 2);
            canvas.drawRoundRect(rectF, radius, radius, paint);
        }
    }


    /**
     * 仿安卓最新支付输入框
     */
    private void drawWeChatRect(int count, Canvas canvas) {
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        int left = (getWidth() - count * (tvWidthSize + intervalSize)) / 2;
        for (int i = 0; i < count; i++) {
            setFocusColor(i);
            RectF rectF = new RectF(left + (tvWidthSize + intervalSize) * i,
                    bgCenterY - tvWidthSize / 2, left + (tvWidthSize + intervalSize) * i + tvWidthSize, bgCenterY + tvWidthSize / 2);
            canvas.drawRoundRect(rectF, radius, radius, paint);
        }
    }


    /**
     * 绘制输入框
     */
    private void drawLineRect(int count, Canvas canvas) {
        if (intervalSize == 0) {
            intervalSize = dip2px(5);
        }
        paint.setStrokeWidth(dip2px(2));
        int left = (getWidth() - count * (tvWidthSize + intervalSize)) / 2;
        for (int i = 0; i < count; i++) {
            setFocusColor(i);
            canvas.drawLine(left + (tvWidthSize + intervalSize) * i, bgCenterY + tvWidthSize / 2, left + (tvWidthSize + intervalSize) * i + tvWidthSize, bgCenterY + tvWidthSize / 2, paint);
        }
    }

    /**
     * 绘制图片
     */
    private void drawBitmap(int count, Canvas canvas) {
        if (intervalSize == 0) {
            intervalSize = dip2px(5);
        }
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mUnSelect);
        Bitmap focusBitmap = BitmapFactory.decodeResource(getResources(), mSelect);
        tvWidthSize = bitmap.getWidth();
        int left = (getWidth() - count * (tvWidthSize + intervalSize)) / 2;
        for (int i = 0; i < count; i++) {
            if (getText().length() > i) {
                canvas.drawBitmap(focusBitmap, left + (tvWidthSize + intervalSize) * i + tvWidthSize / 2 - bitmap.getWidth() / 2, bgCenterY - bitmap.getHeight() / 2, new Paint(Paint.ANTI_ALIAS_FLAG));
            } else {
                canvas.drawBitmap(bitmap, left + (tvWidthSize + intervalSize) * i + tvWidthSize / 2 - bitmap.getWidth() / 2, bgCenterY - bitmap.getHeight() / 2, new Paint(Paint.ANTI_ALIAS_FLAG));
            }
        }
        if (bitmap.isRecycled()) {
            bitmap.recycle();
        }
        if (focusBitmap.isRecycled()) {
            focusBitmap.recycle();
        }
    }


    /**
     * 绘制文字
     */
    private void drawText(int count, Canvas canvas) {
        if (mStyle == -1) {
            return;
        }
        int left = (getWidth() - count * (tvWidthSize + intervalSize)) / 2;
        for (int i = 0; i < length(); i++) {
            String text = getText().toString().substring(i, i + 1);
            if (isPassWord) {
                text = "●";
            }
            float textWidth = textPaint.measureText(text);
            Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
            float y = bgCenterY + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
            canvas.drawText(text, left + (tvWidthSize + intervalSize) * i + tvWidthSize / 2 - textWidth / 2, y, textPaint);
        }
    }


    /**
     * 设置焦点颜色
     */
    private void setFocusColor(int i) {
        if (getText().length() == i && mFocusBorderColor != -1) {
            paint.setColor(mFocusBorderColor);
        } else {
            paint.setColor(mBorderColor);
        }
    }


    /**
     * 添加密碼
     */
    public void addText(String text) {
        if (getText().length() < mTextLen) {
            setText(getText().toString() + text);
        }
    }

    /**
     * 每次删除一个
     */
    public void removeText() {
        if (getText().length() > 0) {
            setText(getText().toString().substring(0, getText().length() - 1));
        }
    }


    /**
     * 把密度转换为像素
     */
    private int dip2px(float px) {
        final float scale = getScreenDensity();
        return (int) (px * scale + 0.5);
    }

    /**
     * 得到设备的密度
     */
    private float getScreenDensity() {
        return getResources().getDisplayMetrics().density;
    }

    /**
     * 禁止输入框复制粘贴菜单
     */
    public void disableCopyAndPaste(final EditText editText) {
        try {
            if (editText == null) {
                return;
            }

            editText.setOnLongClickListener(v -> true);
            editText.setLongClickable(false);

            editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
                @Override
                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                    return false;
                }

                @Override
                public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                    return false;
                }

                @Override
                public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                    return false;
                }

                @Override
                public void onDestroyActionMode(ActionMode mode) {

                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        invalidate();
    }

    @Override
    public void afterTextChanged(Editable s) {
        if (onCodeCompleteListener != null) {
            if (getText().length() == mTextLen) {
                onCodeCompleteListener.inputCodeComplete(getText().toString());
            } else {
                onCodeCompleteListener.inputCodeInput(getText().toString());
            }
        }
    }


    /**
     * 输入完成回调接口
     */
    public interface OnCodeCompleteListener {
        //完成输入
        void inputCodeComplete(String verificationCode);

        //未完成输入
        void inputCodeInput(String verificationCode);
    }


    public void setOnCodeCompleteListener(OnCodeCompleteListener onCodeCompleteListener) {
        this.onCodeCompleteListener = onCodeCompleteListener;
    }
}

 

 

attrs.xml属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CodeEditText">
        <attr name="tvWidth" format="dimension" />
        <!--文字大小-->
        <attr name="tvTextSize" format="dimension" />
        <!--输入个数 默认6个-->
        <attr name="tvLen" format="integer" />
        <!--是否为密码输入框-->
        <attr name="tvIsPwd" format="boolean" />
        <!--间隔-->
        <attr name="tvIntervalSize" format="dimension" />
        <!--圆角-->
        <attr name="radius" format="dimension" />
        <!--文字颜色-->
        <attr name="tvTextColor" format="reference|color" />
        <!--边框颜色-->
        <attr name="tvBorderColor" format="reference|color" />
        <!--边框焦点颜色-->
        <attr name="tvFocusBorderColor" format="reference|color" />
        <!--自定义选中图片-->
        <attr name="tvCustomSelectIcon" format="reference" />
        <!--自定义未选中图片-->
        <attr name="tvUnCustomSelectIcon" format="reference" />
        <!--设置边框粗细-->
        <attr name="tvStrokeWidth" format="dimension" />

        <!--边框样式-->
        <attr name="tvStyle">
            <!--默认无间隔正方形边框(间隔可自己设置通过tvIntervalSize)-->
            <enum name="defaultStyle" value="0" />
            <!--前后圆角边框 此样式不支持焦点颜色设置-->
            <enum name="startEndRadiusStyle" value="1" />
            <!--模拟最新微信支付边框-->
            <enum name="weChatPayStyle" value="2" />
            <!--设置底部横线边框-->
            <enum name="lineStyle" value="3" />
            <!--自定义图片样式,如果需要设置tvCustomSelectIcon和tvUnCustomSelectIcon 必须选择该属性-->
            <enum name="customStyle" value="-1" />
        </attr>
    </declare-styleable>
</resources>

 

源码:https://github.com/zhangwei-wade/inputcodeview

 

 

 

 

 

   

 

posted @ 2021-04-07 12:04  别人眼中的过客  阅读(760)  评论(0编辑  收藏  举报