继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

【Android】自定义View---验证码View

JFSL
关注TA
已关注
手记 9
粉丝 14
获赞 93

效果图
验证码正确

验证码错误

知识点主要有:
获取自定义的属性值
计算View大小
绘制
生成验证码
回调


1.获取自定义属性

  /**
     * 获取自定义属性
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    private void obtainCustomAttribute(Context context,AttributeSet attrs,int defStyle)
    {
        /**
         * 获得我们所定义的自定义样式属性
         */
        TypedArray typedArray = context.getTheme()
                .obtainStyledAttributes(attrs,R.styleable.ValidateCodeView,defStyle,0);
        //遍历获取自定义属性值
        for(int i = 0;i < typedArray.getIndexCount();i++)
        {
            //当前属性
            int attribute = typedArray.getIndex(i);
            switch(attribute)
            {
                //验证码颜色 默认黑色
                case R.styleable.ValidateCodeView_codeColor:
                    mCodeColor = typedArray.getColor(attribute,Color.parseColor(DEFAULT_CODE_COLOR));
                    break;
                //验证码背景色 默认白色
                case R.styleable.ValidateCodeView_codeBackground:
                    mCodeBackground = typedArray.getColor(attribute,Color.parseColor(DEFAULT_CODE_BACKGROUND));
                    break;
                //验证码字体大小,默认16sp
                case R.styleable.ValidateCodeView_codeSize:
                    mCodeSize = sp2px(typedArray,attribute);
                    break;
                //验证码长度,默认为6
                case R.styleable.ValidateCodeView_codeLength:
                    mCodeLength = typedArray.getInt(attribute,DEFAULT_CODE_LENGTH);
                    break;
            }

        }
        typedArray.recycle();
    }
 /**
     * sp转换成px
     *
     * @param typedArray
     * @return
     */
    private int sp2px(TypedArray typedArray,int index)
    {
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        //默认值
        int defaultValue = (int)TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP,DEFAULT_TEXT_SIZE,metrics);
        return typedArray.getDimensionPixelSize(index,defaultValue);
    }

2.计算View大小

@Override
    protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec)
    {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = getActualWidth(widthMode,widthSize);
        int height = getActualHeight(heightMode,heightSize);

        //重新设置属性
        setMeasuredDimension(width,height);

    }
 /**
     * 获取到实际的宽度
     *
     * @param widthMode
     * @param widthSize
     * @return
     */
    private int getActualWidth(int widthMode,int widthSize)
    {
        mPaintCode.getTextBounds(mCurrentCode,0,mCurrentCode.length(),mCodeBounds);
        //精确的宽度
        if(widthMode == MeasureSpec.EXACTLY)
            return widthSize;
        //wrap_content
        //测量出验证码的宽度
        float textWidth = mCodeBounds.width();
        return (int)(getPaddingLeft() + textWidth + getPaddingRight());
    }

    /**
     * 获取到实际的高度
     *
     * @param heightMode
     * @param heightSzie
     * @return
     */
    private int getActualHeight(int heightMode,int heightSzie)
    {
        mPaintCode.getTextBounds(mCurrentCode,0,mCurrentCode.length(),mCodeBounds);
        //精确的高度
        if(heightMode == MeasureSpec.EXACTLY)
            return heightSzie;
        //wrap_content
        //测量出验证码的高度
        float textHeight = mCodeBounds.height();
        return (int)(getPaddingTop() + textHeight + getPaddingBottom());
    }

3.绘制

  /**
     * 绘制方法
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas)
    {

        //绘制背景
        canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaintBackground);
        //绘制验证码
        float x = getWidth() / 2 - mCodeBounds.width() / 2;
        float y = getHeight() / 2 + mCodeBounds.height() / 2;
        canvas.drawText(mCurrentCode,x,y,mPaintCode);
    }

4.生成验证码

    //随机字符集
    private static final char[] RANDOM_CHARS = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };
 /**
     * 改变验证码
     */
    private void changeValidateCode()
    {
        mCurrentCode = getValidateCode();
        //请求重新测量(不同的验证码字符串大小也不一样)
        requestLayout();
        //刷新
        invalidate();
    }

    /**
     * 获取验证码
     *
     * @return
     */
    private String getValidateCode()
    {
        //验证码
        char[] result =new char[mCodeLength];
        Random random = new Random();
        //随机获取,并填充
        for(int i=0;i<mCodeLength;i++)
        {
            result[i] = RANDOM_CHARS[random.nextInt(RANDOM_CHARS.length)];
        }
        String code = new String(result);
        //回调
        if(mListener != null)
            mListener.changed(code);

        return code;
    }

5.回调

    /********************************************************
     *
     *   生成随机数完成回调接口
     *
     ********************************************************
     */
    public interface ValidateCodeChangedListener
    {
        void changed(String validateCode);
    }

    private ValidateCodeChangedListener mListener;

    public void setValidateCodeChangedListener(ValidateCodeChangedListener listener)
    {
        mListener = listener;
    }


全部代码:

package jfsl.customview.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;

import java.util.Random;

import jfsl.customview.R;

/**
 * 验证码View
 *  1.文字大小
 *  2.颜色
 *  3.背景色
 *  4.验证码长度
 ********************************************
 * 未完成:
 *  1.添加干扰线
 *  2.改变角度
 ********************************************
 * @author JFSL
 * @version 1.0.0
 * @date 2017-04-29 20:05
 */

public class ValidateCodeView extends View
{
    public static final int DEFAULT_TEXT_SIZE = 16;
    public static final int DEFAULT_CODE_LENGTH = 6;
    public static final String DEFAULT_CODE_COLOR = "#444444";
    public static final String DEFAULT_CODE_BACKGROUND = "#FAFAFA";
    //随机字符集
    private static final char[] RANDOM_CHARS = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };
    //当前验证码
    private String mCurrentCode;
    //验证码文字大小
    private int mCodeSize;
    //验证码颜色
    private int mCodeColor;
    //验证码背景色
    private int mCodeBackground;
    //验证码长度
    private int mCodeLength;
    //验证码的画笔
    private Paint mPaintCode;
    //验证码背景色的画笔
    private Paint mPaintBackground;
    //验证的边界
    private Rect mCodeBounds;

    public ValidateCodeView(Context context)
    {
        this(context,null);
    }

    public ValidateCodeView(Context context,@Nullable AttributeSet attrs)
    {
        this(context,attrs,0);
    }

    /**
     * 获得我自定义的样式属性
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public ValidateCodeView(Context context,AttributeSet attrs,int defStyle)
    {
        super(context,attrs,defStyle);
        //获取自定义属性
        obtainCustomAttribute(context,attrs,defStyle);

        initDatas();
        initEvent();
    }

    /**
     * 获取自定义属性
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    private void obtainCustomAttribute(Context context,AttributeSet attrs,int defStyle)
    {
        /**
         * 获得我们所定义的自定义样式属性
         */
        TypedArray typedArray = context.getTheme()
                .obtainStyledAttributes(attrs,R.styleable.ValidateCodeView,defStyle,0);
        //遍历获取自定义属性值
        for(int i = 0;i < typedArray.getIndexCount();i++)
        {
            //当前属性
            int attribute = typedArray.getIndex(i);
            switch(attribute)
            {
                //验证码颜色 默认黑色
                case R.styleable.ValidateCodeView_codeColor:
                    mCodeColor = typedArray.getColor(attribute,Color.parseColor(DEFAULT_CODE_COLOR));
                    break;
                //验证码背景色 默认白色
                case R.styleable.ValidateCodeView_codeBackground:
                    mCodeBackground = typedArray.getColor(attribute,Color.parseColor(DEFAULT_CODE_BACKGROUND));
                    break;
                //验证码字体大小,默认16sp
                case R.styleable.ValidateCodeView_codeSize:
                    mCodeSize = sp2px(typedArray,attribute);
                    break;
                //验证码长度,默认为6
                case R.styleable.ValidateCodeView_codeLength:
                    mCodeLength = typedArray.getInt(attribute,DEFAULT_CODE_LENGTH);
                    break;
            }

        }
        typedArray.recycle();
    }

    /**
     * 初始化数据
     */
    private void initDatas()
    {
        //获取验证码(初始化,计算边界用)
        mCurrentCode = getValidateCode();
        //初始化画笔类
        initPaint();
    }

    /**
     * 设置点击监听
     */
    private void initEvent()
    {
        this.setOnClickListener(mThisClickListener);
    }

    /**
     * 初始化画笔类
     */
    private void initPaint()
    {
        //绘制验证码的画笔
        mPaintCode = new Paint();
        mPaintCode.setTextSize(mCodeSize);
        mPaintCode.setColor(mCodeColor);
        //绘制背景色的画笔
        mPaintBackground = new Paint();
        mPaintBackground.setColor(mCodeBackground);
        //初始化边界
        mCodeBounds = new Rect();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec)
    {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = getActualWidth(widthMode,widthSize);
        int height = getActualHeight(heightMode,heightSize);

        //重新设置属性
        setMeasuredDimension(width,height);

    }

    /**
     * 绘制方法
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas)
    {

        //绘制背景
        canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaintBackground);
        //绘制验证码
        float x = getWidth() / 2 - mCodeBounds.width() / 2;
        float y = getHeight() / 2 + mCodeBounds.height() / 2;
        canvas.drawText(mCurrentCode,x,y,mPaintCode);
    }
    /**
     * 获取到实际的宽度
     *
     * @param widthMode
     * @param widthSize
     * @return
     */
    private int getActualWidth(int widthMode,int widthSize)
    {
        mPaintCode.getTextBounds(mCurrentCode,0,mCurrentCode.length(),mCodeBounds);
        //精确的宽度
        if(widthMode == MeasureSpec.EXACTLY)
            return widthSize;
        //wrap_content
        //测量出验证码的宽度
        float textWidth = mCodeBounds.width();
        return (int)(getPaddingLeft() + textWidth + getPaddingRight());
    }

    /**
     * 获取到实际的高度
     *
     * @param heightMode
     * @param heightSzie
     * @return
     */
    private int getActualHeight(int heightMode,int heightSzie)
    {
        mPaintCode.getTextBounds(mCurrentCode,0,mCurrentCode.length(),mCodeBounds);
        //精确的高度
        if(heightMode == MeasureSpec.EXACTLY)
            return heightSzie;
        //wrap_content
        //测量出验证码的高度
        float textHeight = mCodeBounds.height();
        return (int)(getPaddingTop() + textHeight + getPaddingBottom());
    }
    /**
     * sp转换成px
     *
     * @param typedArray
     * @return
     */
    private int sp2px(TypedArray typedArray,int index)
    {
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        //默认值
        int defaultValue = (int)TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP,DEFAULT_TEXT_SIZE,metrics);
        return typedArray.getDimensionPixelSize(index,defaultValue);
    }

    /**
     * 当前对象点击监听
     */
    private OnClickListener mThisClickListener = new OnClickListener()
    {

        @Override
        public void onClick(View v)
        {
            changeValidateCode();
        }
    };

    /**
     * 改变验证码
     */
    private void changeValidateCode()
    {
        mCurrentCode = getValidateCode();
        //请求重新测量(不同的验证码字符串大小也不一样)
        requestLayout();
        //刷新
        invalidate();
    }

    /**
     * 获取验证码
     *
     * @return
     */
    private String getValidateCode()
    {
        //验证码
        char[] result =new char[mCodeLength];
        Random random = new Random();
        //随机获取,并填充
        for(int i=0;i<mCodeLength;i++)
        {
            result[i] = RANDOM_CHARS[random.nextInt(RANDOM_CHARS.length)];
        }
        String code = new String(result);
        //回调
        if(mListener != null)
            mListener.changed(code);

        return code;
    }

    /********************************************************
     *
     *   生成随机数完成回调接口
     *
     ********************************************************
     */
    public interface ValidateCodeChangedListener
    {
        void changed(String validateCode);
    }

    private ValidateCodeChangedListener mListener;

    public void setValidateCodeChangedListener(ValidateCodeChangedListener listener)
    {
        mListener = listener;
    }

}

属性文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!--验证码颜色-->
    <attr name="codeColor" format="color" />
    <!--验证码背景色-->
    <attr name="codeBackground" format="color" />
    <!--验证码大小-->
    <attr name="codeSize" format="dimension" />
    <!--验证码长度-->
    <attr name="codeLength" format="integer" />

    <declare-styleable name="ValidateCodeView">
        <attr name="codeColor" />
        <attr name="codeBackground" />
        <attr name="codeSize" />
        <attr name="codeLength" />
    </declare-styleable>

</resources>

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginLeft="16dp"
    android:layout_marginRight="16dp"
    android:orientation="vertical">

    <jfsl.customview.view.ValidateCodeView
        android:id="@+id/id_vcv_my"
        android:layout_width="100dp"
        android:layout_height="50dp"
        app:codeColor="#FAFAFA"
        app:codeBackground="#009688"
        app:codeLength="4"
        app:codeSize="18sp"/>
    <EditText
        android:id="@+id/id_edittext_code"
        android:layout_width="match_parent"
        android:layout_height="56dp"/>
    <Button
        android:id="@+id/id_button_validate"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:text="验证"/>
</LinearLayout>

测试代码:

package jfsl.customview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import jfsl.customview.view.ValidateCodeView;

public class ActivityMain extends AppCompatActivity
{
    private ValidateCodeView mValidateCodeView;
    private EditText mEditText;
    private Button mButton;
    private String mCode;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
        initDatas();
        initEvent();
    }

    private void initViews()
    {
        mValidateCodeView = (ValidateCodeView)findViewById(R.id.id_vcv_my);
        mEditText = (EditText)findViewById(R.id.id_edittext_code);
        mButton = (Button)findViewById(R.id.id_button_validate);
    }

    private void initDatas()
    {

    }

    private void initEvent()
    {
        mValidateCodeView.setValidateCodeChangedListener(
                new ValidateCodeView.ValidateCodeChangedListener()
                {

                    @Override
                    public void changed(String validateCode)
                    {
                        //转成成大写
                        mCode = validateCode.toUpperCase();
                    }
                });
        mButton.setOnClickListener(new View.OnClickListener()
        {

            @Override
            public void onClick(View v)
            {
                validate();
            }
        });

    }

    private void validate()
    {
        String code = mEditText.getText().toString().trim();
        String upperCode = code.toUpperCase();

        if(upperCode.equals(mCode))
        {
            Toast.makeText(this,"验证码正确",Toast.LENGTH_SHORT).show();
            return;
        }
        Toast.makeText(this,"验证码错误",Toast.LENGTH_SHORT).show();
    }
}

参考代码 http://blog.csdn.net/lmj623565791/article/details/24252901/

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP