android實(shí)現(xiàn)圖片驗(yàn)證碼方法解析(自繪控件)
自繪控件的內(nèi)容都是自己繪制出來的 大致流程如下:
1.定義一個(gè)類繼承view
1.使用TypedArray初始化屬性集合
在view的構(gòu)造方法中 有一個(gè)AttributeSet的參數(shù) 很明顯是用來保存控件屬性信息的 我們也的確可以通過循環(huán)然后用鍵值對(duì)的方式獲取信息 而TypedArray是用來簡(jiǎn)化我們的工作的
2.重寫onMeasure 測(cè)量控件大小
3.重寫onDraw 繪制控件
2.根據(jù)需求在attrs文件中自定義屬性
declare-styleable 聲明自定義屬性可以自定義一個(gè)新屬性也可以引用已經(jīng)存在的屬性兩者的區(qū)別就是新屬性需要添加format進(jìn)行類型的定義
3.在activity的布局文件使用
自定義圖片驗(yàn)證碼 演示效果

示例代碼
<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;
/**
* 類描述:自定義驗(yàn)證碼
* 創(chuàng)建者:lb
*/
public class VerifyCode extends View {
private String mCodeText;//文本內(nèi)容
private int mCodeTextSize;//文本大小
private int mCodeLength;//驗(yàn)證碼長(zhǎng)度
private int mCodeBackground;//背景色
private boolean isContainChar;//驗(yàn)證碼是否包含字母
private int mPointNum;//干擾點(diǎn)數(shù)
private int mLineNum;//干擾線數(shù)
private Paint mPaint;//畫筆
private Rect mBound;//繪制范圍
private Bitmap bitmap;//驗(yàn)證碼圖片
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的長(zhǎng)度
int count=typedArray.getIndexCount();
for (int i=0;i<count;i++){
//獲取此項(xiàng)屬性的ID
int index=typedArray.getIndex(i);
switch (index){
case R.styleable.VerifyCode_codeTextSize:
// 默認(rèn)設(shè)置為16sp,TypeValue類 px轉(zhuǎn)sp 一個(gè)轉(zhuǎn)換類
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();
}
/**
* 初始化數(shù)據(jù)
*/
private void initData(){
mCodeText=getValidationCode(mCodeLength,isContainChar);
mPaint=new Paint();
mPaint.setAntiAlias(true);
mBound=new Rect();
//計(jì)算文字所在矩形,可以得到寬高
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);
//設(shè)置寬高默認(rèn)為建議的最小寬高
int width= getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec) ;
int height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec);
// MeasureSpec父布局傳遞給后代的布局要求 包含 確定大小和三種模式
// EXACTLY:一般是設(shè)置了明確的值或者是MATCH_PARENT
// AT_MOST:表示子布局限制在一個(gè)最大值內(nèi),一般為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;
}
//設(shè)置測(cè)量的寬高
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);
}
/**
* 創(chuàng)建圖片驗(yàn)證碼
* @return
*/
private Bitmap createBitmapValidate(){
if(bitmap != null && !bitmap.isRecycled()){
//回收并且置為null
bitmap.recycle();
bitmap = null;
}
//創(chuàng)建圖片
Bitmap sourceBitmap=Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
//創(chuàng)建畫布
Canvas canvas=new Canvas(sourceBitmap);
//畫上背景顏色
canvas.drawColor(mCodeBackground);
//初始化文字畫筆
mPaint.setStrokeWidth(3f);
mPaint.setTextSize(mCodeTextSize);
//測(cè)量驗(yàn)證碼字符串顯示的寬度值
float textWidth=mPaint.measureText(mCodeText);
//畫上驗(yàn)證碼
int length = mCodeText.length();
//計(jì)算一個(gè)字符的所占位置
float charLength = textWidth / length;
for (int i = 1; i <= length; i++) {
int offsetDegree = mRandom.nextInt(15);
//這里只會(huì)產(chǎn)生0和1,如果是1那么正旋轉(zhuǎn)正角度,否則旋轉(zhuǎn)負(fù)角度
offsetDegree = mRandom.nextInt(2) == 1 ? offsetDegree : -offsetDegree;
//用來保存Canvas的狀態(tài)。save之后,可以調(diào)用Canvas的平移、放縮、旋轉(zhuǎn)、錯(cuò)切、裁剪等操作。
canvas.save();
//設(shè)置旋轉(zhuǎn)
canvas.rotate(offsetDegree, mWidth / 2, mHeight / 2);
//給畫筆設(shè)置隨機(jī)顏色
mPaint.setARGB(255, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20,
mRandom.nextInt(200) + 20);
//設(shè)置字體的繪制位置
canvas.drawText(String.valueOf(mCodeText.charAt(i - 1)), (i - 1) * charLength+5,
mHeight * 4 / 5f, mPaint);
//用來恢復(fù)Canvas之前保存的狀態(tài)。防止save后對(duì)Canvas執(zhí)行的操作對(duì)后續(xù)的繪制有影響。
canvas.restore();
}
//重新設(shè)置畫筆
mPaint.setARGB(255, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20,
mRandom.nextInt(200) + 20);
mPaint.setStrokeWidth(1);
//產(chǎn)生干擾效果1 -- 干擾點(diǎn)
for (int i = 0; i < mPointNum; i++) {
drawPoint(canvas, mPaint);
}
//生成干擾效果2 -- 干擾線
for (int i = 0; i < mLineNum; i++) {
drawLine(canvas, mPaint);
}
return sourceBitmap;
}
/**
* 生成干擾點(diǎn)
*/
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);
}
/**
* 獲取驗(yàn)證碼
*
* @param length 生成隨機(jī)數(shù)的長(zhǎng)度
* @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){
//字母或數(shù)字
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;
}
/**
*判斷驗(yàn)證碼是否一致 忽略大小寫
*/
public Boolean isEqualsIgnoreCase(String CodeString) {
return mCodeText.equalsIgnoreCase(CodeString);
}
/**
* 判斷驗(yàn)證碼是否一致 不忽略大小寫
*/
public Boolean isEquals(String CodeString) {
return mCodeText.equals(CodeString);
}
/**
* 提供外部調(diào)用的刷新方法
*/
public void refresh(){
mCodeText= getValidationCode(mCodeLength,isContainChar);
bitmap = createBitmapValidate();
invalidate();
}
}
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持腳本之家!
- Android利用CountDownTimer實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)效果實(shí)例
- Android實(shí)現(xiàn)常見的驗(yàn)證碼輸入框?qū)嵗a
- Android實(shí)現(xiàn)獲取短信驗(yàn)證碼并自動(dòng)填寫功能
- Android用 Mob 實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼實(shí)例
- Android開發(fā)中通過手機(jī)號(hào)+短信驗(yàn)證碼登錄的實(shí)例代碼
- Android 使用fast-verification實(shí)現(xiàn)驗(yàn)證碼填寫功能的實(shí)例代碼
相關(guān)文章
Kotlin?Flow數(shù)據(jù)流的3種使用場(chǎng)景詳解
這篇文章主要為大家詳細(xì)介紹了Kotlin中Flow數(shù)據(jù)流的幾種使用場(chǎng)景,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2023-04-04
Android 回調(diào)詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了Android 回調(diào)詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01
Android中WebView的使用與后退鍵處理詳細(xì)講解
修改Android Studio 的 Logcat 緩沖區(qū)大小操作
Android TabLayout設(shè)置指示器寬度的方法
Android調(diào)用系統(tǒng)裁剪的實(shí)現(xiàn)方法

