android圖片驗證碼--自繪控件

自繪控件的內容都是本身繪製出來的 大體流程以下:java

1.定義一個類繼承view
  1. 使用TypedArray初始化屬性集合
    在view的構造方法中 有一個AttributeSet的參數 很明顯是用來保存控件屬性信息的 咱們也的確能夠經過循環而後用鍵值對的方式獲取信息 而TypedArray是用來簡化咱們的工做的
  2. 重寫onMeasure 測量控件大小
  3. 重寫onDraw 繪製控件
2.根據需求在attrs文件中自定義屬性

declare-styleable 聲明自定義屬性 能夠自定義一個新屬性 也能夠引用已經存在的屬性 二者的區別就是 新屬性須要添加format 進行類型的定義android

3.在activity的佈局文件使用

自定義圖片驗證碼 演示效果canvas

示例代碼app

<declare-styleable name="VerifyCode">
        <attr name="codeTextSize" format="dimension"/>
        <attr name="codeBackground" format="color"/>
        <attr name="codeLength" format="integer"/>
        <attr name="isContainChar" format="boolean"/>
        <attr name="pointNum" format="integer"/>
        <attr name="linNum" format="integer"/>
    </declare-styleable>
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import java.util.Random;

/**
 * 類描述:自定義驗證碼
 * 建立者:lb
 */

public class VerifyCode extends View {

    private String mCodeText;//文本內容
    private int mCodeTextSize;//文本大小
    private int mCodeLength;//驗證碼長度
    private int mCodeBackground;//背景色
    private boolean isContainChar;//驗證碼是否包含字母
    private int mPointNum;//干擾點數
    private int mLineNum;//干擾線數

    private Paint mPaint;//畫筆
    private Rect mBound;//繪製範圍
    private Bitmap bitmap;//驗證碼圖片

    private static Random mRandom = new Random();
    private static int mWidth;//控件的寬度
    private static int mHeight;//控件的高度


    public VerifyCode(Context context) {
        super(context);
    }

    public VerifyCode(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttrValues(context,attrs);
        initData();
    }

    /**
     * 初始化屬性集合
     * @param context
     * @param attrs
     */
    private void initAttrValues(Context context, AttributeSet attrs){
        // //獲取在AttributeSet中定義的 VerifyCode 中聲明的屬性的集合
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VerifyCode);
        //獲取TypeArray的長度
        int count=typedArray.getIndexCount();
        for (int i=0;i<count;i++){
            //獲取此項屬性的ID
            int index=typedArray.getIndex(i);
            switch (index){
                case R.styleable.VerifyCode_codeTextSize:
                    // 默認設置爲16sp,TypeValue類 px轉sp 一個轉換類
                    mCodeTextSize =typedArray.getDimensionPixelSize(index,(int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.VerifyCode_codeBackground:
                    mCodeBackground=typedArray.getColor(index,Color.WHITE);
                    break;
                case R.styleable.VerifyCode_codeLength:
                    mCodeLength=typedArray.getInteger(index,4);
                    break;
                case R.styleable.VerifyCode_isContainChar:
                    isContainChar=typedArray.getBoolean(index,false);
                    break;
                case R.styleable.VerifyCode_pointNum:
                    mPointNum=typedArray.getInteger(index,100);
                    break;
                case R.styleable.VerifyCode_linNum:
                    mLineNum=typedArray.getInteger(index,3);
                    break;
            }
        }
        //Recycles the TypedArray, to be re-used by a later caller
        //官方解釋:回收TypedArray 以便後面的使用者重用
        typedArray.recycle();
    }

    /**
     * 初始化數據
     */
    private void initData(){
        mCodeText=getValidationCode(mCodeLength,isContainChar);
        mPaint=new Paint();
        mPaint.setAntiAlias(true);
        mBound=new Rect();
        //計算文字所在矩形,能夠獲得寬高
        mPaint.getTextBounds(mCodeText,0, mCodeText.length(),mBound);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //獲取控件寬高的顯示模式
        int widthMode=MeasureSpec.getMode(widthMeasureSpec);
        int heightMode=MeasureSpec.getMode(heightMeasureSpec);
        //獲取寬高的尺寸值  固定值的寬度
        int widthSize=MeasureSpec.getSize(widthMeasureSpec);
        int heightSize=MeasureSpec.getSize(heightMeasureSpec);
        //設置寬高默認爲建議的最小寬高
        int width= getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec) ;
        int height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec);

//        MeasureSpec父佈局傳遞給後代的佈局要求 包含 肯定大小和三種模式
//        EXACTLY:通常是設置了明確的值或者是MATCH_PARENT
//        AT_MOST:表示子佈局限制在一個最大值內,通常爲WARP_CONTENT
//        UNSPECIFIED:表示子佈局想要多大就多大,不多使用
        if (widthMode==MeasureSpec.EXACTLY){
            width=widthSize;
        }else{
            mPaint.setTextSize(mCodeTextSize);
            mPaint.getTextBounds(mCodeText,0,mCodeText.length(),mBound);
            float textWidth=mBound.width();
            int tempWidth=(int)(getPaddingLeft()+textWidth+getPaddingRight());
            width=tempWidth;
        }
        if (heightMode == MeasureSpec.EXACTLY)
        {
            height = heightSize;
        } else
        {
            mPaint.setTextSize(mCodeTextSize);
            mPaint.getTextBounds(mCodeText, 0, mCodeText.length(), mBound);
            float textHeight = mBound.height();
            int tempHeight = (int) (getPaddingTop() + textHeight + getPaddingBottom());
            height = tempHeight;
        }
        //設置測量的寬高
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mWidth=getWidth();
        mHeight=getHeight();

        if (bitmap==null){
            bitmap=createBitmapValidate();
        }
        canvas.drawBitmap(bitmap,0,0,mPaint);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                refresh();
                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 建立圖片驗證碼
     * @return
     */
    private Bitmap createBitmapValidate(){
        if(bitmap != null && !bitmap.isRecycled()){
            //回收而且置爲null
            bitmap.recycle();
            bitmap = null;
        }
        //建立圖片
        Bitmap sourceBitmap=Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
        //建立畫布
        Canvas canvas=new Canvas(sourceBitmap);
        //畫上背景顏色
        canvas.drawColor(mCodeBackground);
        //初始化文字畫筆
        mPaint.setStrokeWidth(3f);
        mPaint.setTextSize(mCodeTextSize);
        //測量驗證碼字符串顯示的寬度值
        float textWidth=mPaint.measureText(mCodeText);
        //畫上驗證碼
        int length = mCodeText.length();
        //計算一個字符的所佔位置
        float charLength = textWidth / length;
        for (int i = 1; i <= length; i++) {
            int offsetDegree = mRandom.nextInt(15);
            //這裏只會產生0和1,若是是1那麼正旋轉正角度,不然旋轉負角度
            offsetDegree = mRandom.nextInt(2) == 1 ? offsetDegree : -offsetDegree;
            //用來保存Canvas的狀態。save以後,能夠調用Canvas的平移、放縮、旋轉、錯切、裁剪等操做。
            canvas.save();
            //設置旋轉
            canvas.rotate(offsetDegree, mWidth / 2, mHeight / 2);
            //給畫筆設置隨機顏色
            mPaint.setARGB(255, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20,
                    mRandom.nextInt(200) + 20);
            //設置字體的繪製位置
            canvas.drawText(String.valueOf(mCodeText.charAt(i - 1)), (i - 1) * charLength+5,
                    mHeight * 4 / 5f, mPaint);
            //用來恢復Canvas以前保存的狀態。防止save後對Canvas執行的操做對後續的繪製有影響。
            canvas.restore();
        }

        //從新設置畫筆
        mPaint.setARGB(255, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20,
                mRandom.nextInt(200) + 20);
        mPaint.setStrokeWidth(1);
        //產生干擾效果1 -- 干擾點
        for (int i = 0; i < mPointNum; i++) {
            drawPoint(canvas, mPaint);
        }
        //生成干擾效果2 -- 干擾線
        for (int i = 0; i < mLineNum; i++) {
            drawLine(canvas, mPaint);
        }
        return sourceBitmap;
    }

    /**
     * 生成干擾點
     */
    private static void drawPoint(Canvas canvas, Paint paint) {
        PointF pointF = new PointF(mRandom.nextInt(mWidth) + 10, mRandom.nextInt(mHeight) + 10);
        canvas.drawPoint(pointF.x, pointF.y, paint);
    }

    /**
     * 生成干擾線
     */
    private static void drawLine(Canvas canvas, Paint paint) {
        int startX = mRandom.nextInt(mWidth);
        int startY = mRandom.nextInt(mHeight);
        int endX = mRandom.nextInt(mWidth);
        int endY = mRandom.nextInt(mHeight);
        canvas.drawLine(startX, startY, endX, endY, paint);
    }

    /**
     * 獲取驗證碼
     *
     * @param length 生成隨機數的長度
     * @param contains 是否包含字符串
     * @return
     */
    public String getValidationCode(int length,boolean contains) {
        String val = "";
        Random random = new Random();

        for (int i = 0; i < length; i++) {
            if (contains){
                //字母或數字
                String code = random.nextInt(2) % 2 == 0 ? "char" : "num";
                //字符串
                if ("char".equalsIgnoreCase(code)) {
                    //大寫或小寫字母
                    int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;
                    val += (char) (choice + random.nextInt(26));
                } else if ("num".equalsIgnoreCase(code)) {
                    val += String.valueOf(random.nextInt(10));
                }
            }else{
                val += String.valueOf(random.nextInt(10));
            }

        }
        return val;
    }

    /**
     *判斷驗證碼是否一致 忽略大小寫
     */
    public Boolean isEqualsIgnoreCase(String CodeString) {
        return mCodeText.equalsIgnoreCase(CodeString);
    }

    /**
     * 判斷驗證碼是否一致 不忽略大小寫
     */
    public Boolean isEquals(String CodeString) {
        return mCodeText.equals(CodeString);
    }

    /**
     * 提供外部調用的刷新方法
     */
    public void refresh(){
        mCodeText= getValidationCode(mCodeLength,isContainChar);
        bitmap = createBitmapValidate();
        invalidate();
    }
}
相關文章
相關標籤/搜索