栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

概述

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

实现

1. 特性

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

2. 效果图

3. 属性

4. 代码

  • attrs.xml


    
        
        
        
        
        
        
        
        
        
            
            
        
        
            
            
        
        
        

    

  • 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;
        }
    }

    
    private void drawBorderBox(Canvas canvas,int position,int borderColor){
        if(mBorderCornerRadius > 0){//当边框带有圆角时
            if(mBorderSpacing == 0){//当边框之间的间距为0时,只需要开始一个和最后一个框有圆角
                if(position == 0 || position == mMaxLength - 1){
                    if(mBoxBackgroundColor != 0){
                        mPaint.setStyle(Paint.Style.FILL);
                        mPaint.setColor(mBoxBackgroundColor);
                        canvas.drawPath(getRoundRectPath(mRectF,position == 0),mPaint);
                    }
                    mPaint.setStyle(Paint.Style.STROKE);
                    mPaint.setColor(borderColor);
                    canvas.drawPath(getRoundRectPath(mRectF,position == 0),mPaint);
                }else{
                    if(mBoxBackgroundColor != 0){
                        mPaint.setStyle(Paint.Style.FILL);
                        mPaint.setColor(mBoxBackgroundColor);
                        canvas.drawRect(mRectF,mPaint);
                    }
                    mPaint.setStyle(Paint.Style.STROKE);
                    mPaint.setColor(borderColor);
                    canvas.drawRect(mRectF,mPaint);
                }
            }else{
                if(mBoxBackgroundColor != 0){
                    mPaint.setStyle(Paint.Style.FILL);
                    mPaint.setColor(mBoxBackgroundColor);
                    canvas.drawRoundRect(mRectF,mBorderCornerRadius,mBorderCornerRadius,mPaint);
                }
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setColor(borderColor);
                canvas.drawRoundRect(mRectF,mBorderCornerRadius,mBorderCornerRadius,mPaint);
            }
        }else{
            if(mBoxBackgroundColor != 0){
                mPaint.setStyle(Paint.Style.FILL);
                mPaint.setColor(mBoxBackgroundColor);
                canvas.drawRect(mRectF,mPaint);
            }
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(borderColor);
            canvas.drawRect(mRectF,mPaint);
        }
    }

    
    private void drawBorderLine(Canvas canvas){
        float y = getPaddingTop() + mBoxHeight;
        canvas.drawLine(mRectF.left,y,mRectF.right,y,mPaint);
    }

    private Path getRoundRectPath(RectF rectF,boolean isFirst){
        mPath.reset();
        if(isFirst){
            //左上角
            mRadiusFirstArray[0] = mBorderCornerRadius;
            mRadiusFirstArray[1] = mBorderCornerRadius;
            //左下角
            mRadiusFirstArray[6] = mBorderCornerRadius;
            mRadiusFirstArray[7] = mBorderCornerRadius;
            mPath.addRoundRect(rectF,mRadiusFirstArray, Path.Direction.CW);
        }else{
            //右上角
            mRadiusLastArray[2] = mBorderCornerRadius;
            mRadiusLastArray[3] = mBorderCornerRadius;
            //右下角
            mRadiusLastArray[4] = mBorderCornerRadius;
            mRadiusLastArray[5] = mBorderCornerRadius;
            mPath.addRoundRect(rectF,mRadiusLastArray, Path.Direction.CW);
        }
        return mPath;
    }


    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        mTextLength = text.length();
        refreshView();
        //改变监听
        if(mOnTextInputListener != null){
            mOnTextInputListener.onTextInputChanged(text.toString(),mTextLength);
            if(mTextLength == mMaxLength){
                mOnTextInputListener.onTextInputCompleted(text.toString());
            }
        }
    }

    @Override
    protected void onSelectionChanged(int selStart, int selEnd) {
        super.onSelectionChanged(selStart, selEnd);
        if (selStart == selEnd) {
            setSelection(getText() == null ? 0 : getText().length());
        }
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        //焦点改变时刷新状态
        refreshView();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        isDraw = false;
    }

    public int getBorderColor() {
        return mBorderColor;
    }

    public int getInputBorderColor() {
        return mInputBorderColor;
    }

    public int getFocusBorderColor() {
        return mFocusBorderColor;
    }

    public int getBoxBackgroundColor() {
        return mBoxBackgroundColor;
    }

    public float getBorderCornerRadius() {
        return mBorderCornerRadius;
    }

    public float getBorderSpacing() {
        return mBorderSpacing;
    }

    @BorderStyle
    public int getBorderStyle() {
        return mBorderStyle;
    }


    public void setBorderColor(int borderColor) {
        this.mBorderColor = borderColor;
        refreshView();
    }

    public void setInputBorderColor(int inputBorderColor) {
        this.mInputBorderColor = inputBorderColor;
        refreshView();
    }

    public void setFocusBorderColor(int focusBorderColor) {
        this.mFocusBorderColor = focusBorderColor;
        refreshView();
    }

    public void setBoxBackgroundColor(int boxBackgroundColor) {
        this.mBoxBackgroundColor = boxBackgroundColor;
        refreshView();
    }

    public void setBorderCornerRadius(float borderCornerRadius) {
        this.mBorderCornerRadius = borderCornerRadius;
        refreshView();
    }

    public void setBorderSpacing(float borderSpacing) {
        this.mBorderSpacing = borderSpacing;
        refreshView();
    }

    public void setBorderStyle(@TextStyle int borderStyle) {
        this.mBorderStyle = borderStyle;
        refreshView();
    }

    @TextStyle
    public int getTextStyle() {
        return mTextStyle;
    }

    public void setTextStyle(@TextStyle int textStyle) {
        this.mTextStyle = textStyle;
        refreshView();
    }

    public String getCipherMask() {
        return mCipherMask;
    }

    
    public void setFakeBoldText(boolean fakeBoldText) {
        isFakeBoldText = fakeBoldText;
        refreshView();
    }

    
    public void setCipherMask(String cipherMask) {
        this.mCipherMask = cipherMask;
        refreshView();
    }

    
    private void refreshView(){
        if(isDraw){
            invalidate();
        }
    }

    
    public void setOnTextInputListener(OnTextInputListener onTextInputListener) {
        this.mOnTextInputListener = onTextInputListener;
    }

    public static abstract class OnSimpleTextInputListener implements OnTextInputListener{

        @Override
        public void onTextInputChanged(String text, int length) {

        }

    }

    
    public interface OnTextInputListener{
        
        void onTextInputChanged(String text,int length);

        
        void onTextInputCompleted(String text);
    }
}

  • 使用



    

    

    

    

	//设置监听
    splitEditText.setOnTextInputListener(new SplitEditText.OnTextInputListener(){

        @Override
        public void onTextInputChanged(String text, int length) {
            //TODO 文本输入改变
        }

        @Override
        public void onTextInputCompleted(String text) {
            //TODO 文本输入完成
        }
    });

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/926546.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号