android實(shí)現(xiàn)圖片驗(yàn)證碼方法解析(自繪控件)
自繪控件的內(nèi)容都是自己繪制出來(lái)的 大致流程如下:
1.定義一個(gè)類繼承view
1.使用TypedArray初始化屬性集合
在view的構(gòu)造方法中 有一個(gè)AttributeSet的參數(shù) 很明顯是用來(lái)保存控件屬性信息的 我們也的確可以通過(guò)循環(huán)然后用鍵值對(duì)的方式獲取信息 而TypedArray是用來(lái)簡(jiǎn)化我們的工作的
2.重寫(xiě)onMeasure 測(cè)量控件大小
3.重寫(xiě)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;//畫(huà)筆 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)建畫(huà)布 Canvas canvas=new Canvas(sourceBitmap); //畫(huà)上背景顏色 canvas.drawColor(mCodeBackground); //初始化文字畫(huà)筆 mPaint.setStrokeWidth(3f); mPaint.setTextSize(mCodeTextSize); //測(cè)量驗(yàn)證碼字符串顯示的寬度值 float textWidth=mPaint.measureText(mCodeText); //畫(huà)上驗(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; //用來(lái)保存Canvas的狀態(tài)。save之后,可以調(diào)用Canvas的平移、放縮、旋轉(zhuǎn)、錯(cuò)切、裁剪等操作。 canvas.save(); //設(shè)置旋轉(zhuǎn) canvas.rotate(offsetDegree, mWidth / 2, mHeight / 2); //給畫(huà)筆設(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); //用來(lái)恢復(fù)Canvas之前保存的狀態(tài)。防止save后對(duì)Canvas執(zhí)行的操作對(duì)后續(xù)的繪制有影響。 canvas.restore(); } //重新設(shè)置畫(huà)筆 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)) { //大寫(xiě)或小寫(xiě)字母 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)證碼是否一致 忽略大小寫(xiě) */ public Boolean isEqualsIgnoreCase(String CodeString) { return mCodeText.equalsIgnoreCase(CodeString); } /** * 判斷驗(yàn)證碼是否一致 不忽略大小寫(xiě) */ 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í)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
- Android利用CountDownTimer實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)效果實(shí)例
- Android實(shí)現(xiàn)常見(jiàn)的驗(yàn)證碼輸入框?qū)嵗a
- Android實(shí)現(xiàn)獲取短信驗(yàn)證碼并自動(dòng)填寫(xiě)功能
- Android用 Mob 實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼實(shí)例
- Android開(kāi)發(fā)中通過(guò)手機(jī)號(hào)+短信驗(yàn)證碼登錄的實(shí)例代碼
- Android 使用fast-verification實(shí)現(xiàn)驗(yàn)證碼填寫(xiě)功能的實(shí)例代碼
相關(guān)文章
Kotlin?Flow數(shù)據(jù)流的3種使用場(chǎng)景詳解
這篇文章主要為大家詳細(xì)介紹了Kotlin中Flow數(shù)據(jù)流的幾種使用場(chǎng)景,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2023-04-04android商品詳情頁(yè)面設(shè)計(jì)詳解
這篇文章主要為大家詳細(xì)介紹了android商品詳情頁(yè)面設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android 回調(diào)詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了Android 回調(diào)詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01

Android中WebView的使用與后退鍵處理詳細(xì)講解

修改Android Studio 的 Logcat 緩沖區(qū)大小操作

實(shí)現(xiàn)Android鍵盤(pán)的中英文適配

Android TabLayout設(shè)置指示器寬度的方法

Android調(diào)用系統(tǒng)裁剪的實(shí)現(xiàn)方法