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

摘要:
继承EditText以实现自定义验证代码边框InputCode1,修改边框焦点颜色和文本颜色大小,并支持自定义图片。

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

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

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

演示:

安卓 验证码输入框InputCode(同时支持密码输入)第1张     安卓 验证码输入框InputCode(同时支持密码输入)第2张     安卓 验证码输入框InputCode(同时支持密码输入)第3张



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

   

免责声明:文章转载自《安卓 验证码输入框InputCode(同时支持密码输入)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇PHP日期操作类代码-农历-阳历转换、闰年、计算天数等原生js和vue之间的数据通讯--EventEmitter下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

键盘优雅弹出与ios光标乱飘解决方案

前言 在移动开发中,会遇到这样的情况,比如说有一个输入框在最底部的时候,我们弹起输入框,输入框不会在输入键盘上。 说明白简单点就是,输入框被键盘挡住了。而且在原生中,输入框应该正好在输入键盘上,但是h5 没有这种体验,那么我们需要自己实现。 再次用图说明情况。 情况一: 键盘挡住了,输入框。 情况二: 红色代表有滚动条,输入框,没有正好卡在下面,用户体验...

input输入框默认文字,点击消失

经过查询,发现2种实现方式: 1. <input type="text" value="请输入用户名" onfocus="if(value=='请输入用户名') {value=''}" onblur="if (value=='') {value='请输入用户名'}"> 直接在input中定义onfocus和onblur事件,缺点:鼠标一离开就...

iOS 键盘 隐藏系统的 toolBar

有些情况下,我们可能需要隐藏键盘,但是保持输入框的编辑状态,光标不消失. 比如:在界面上加上语音输入功能. //1.视图取消编辑态 [self.view endEditing:YES]; //2.输入框取消第一响应 if ([self.textView canResignFirstResponder]) {...

Antd Form表单中Input输入框 在IE9下按下任何按键都会报校验失败

antd Form表单中Input输入框 在IE9下按下任何按键都会报校验失败,导致输入框输入不了任何内容! 可能你的react及react-dom版本由于过高导致antd组件不能兼容,需要对react及react-dom进行降级处理; 在这里附上我的版本号: "antd": "^2.13.14", "react": "^16.2.0","react-d...

账号系统登录流程

客户端弹出登录弹窗,如果用户上次登录勾选了“记住我”,则默认给填上000000。 客户端先自行校验各项输入框的值是否符合要求,用正则表达式校验。   如果是客户端,可以在登录后自行维护登录状态;如果是Web前端,使用Access Token或者Session维护登录状态。看具体需求。 Token的生成规则可以是:hash("sha256", user_id...

Vue中如何给字符串前面加空格

  背景:        最近再用element UI写一个页面,输入框是必输的话,如果没有输入点提交,需要有个红色的提示信息。但是提示信息没有个输入框对齐,就想用加空格的方法来实现。后来发现通常的加空格的方法都无效       一、效果和代码如下:        二 、 想要的效果是提示信息和输入框对齐,网上没找到很好的方法,可能我加了label-w...