android实现图片验证码方法解析(自绘控件)

自绘控件的内容都是自己绘制出来的 大致流程如下:

1.定义一个类继承view

1.使用TypedArray初始化属性集合
    在view的构造方法中 有一个AttributeSet的参数 很明显是用来保存控件属性信息的 我们也的确可以通过循环然后用键值对的方式获取信息 而TypedArray是用来简化我们的工作的

2.重写onMeasure 测量控件大小

3.重写onDraw 绘制控件

2.根据需求在attrs文件中自定义属性

declare-styleable 声明自定义属性可以自定义一个新属性也可以引用已经存在的属性两者的区别就是新属性需要添加format进行类型的定义

3.在activity的布局文件使用

自定义图片验证码 演示效果

示例代码

<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(); } }

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

时间: 2024-09-20 10:35:00

android实现图片验证码方法解析(自绘控件)的相关文章

Android UI开发专题(四) View自绘控件

  很多时候想要设计漂亮的Android UI,使用Android自带的控件无法满足我们的需要就要考虑自绘控件,在Android界面显示类View,可以通过继承扩展重写相关方法来实现我们的图形绘制. 首先我们需要了解下View类的底层实现,在SDK中我们可以看到View直接继承于Java的基类Object,实现了图形绘制和按键事件Drawable.Callback KeyEvent.Callback的相关方法,我们自绘时主要实现其内部的onDraw方法,相关的界面计算可以重写onMeasure方

Android学习自定义View(三)——自绘控件和组合控件

MainActivity如下: package cc.testviewstudy3; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.app.Activity; /** * Demo描述: * 关于自定义View的学习(三) * * 自定义View的实现方式大概可以分为三种: * 自绘控件.组合控件.以及继承控件 * 在此Dem

dit ext控件-android开发中EditText中动态添加别的控件(图片,按钮,视频)

问题描述 android开发中EditText中动态添加别的控件(图片,按钮,视频) 我想在EditText输入框中不仅仅输入文字,还想输入图片,图片的功能我已经实现了,怎么往里面加入一个按钮,或者加入一个VideoView控件用来播放视频?哪位大牛会啊,给个思路也行啊 解决方案 Android Edittext 添加按钮android开发EditText中添加图片 解决方案二: 组织成html字符串,textview可以解析 解决方案三: 用ImageSpan和SpannableString

Android UI开发 View自绘控件 分享_Android

很多时候想要设计漂亮的Android UI,使用Android自带的控件无法满足我们的需要就要考虑自绘控件,在Android界面显示类View,可以通过继承扩展重写相关方法来实现我们的图形绘制. 首先我们需要了解下View类的底层实现,在SDK中我们可以看到View直接继承于Java的基类Object,实现了图形绘制和按键事件 Drawable.Callback KeyEvent.Callback的相关方法,我们自绘时主要实现其内部的onDraw方法,相关的界面计算可以重写onMeasure方法

Android UI开发 View自绘控件 分享

很多时候想要设计漂亮的Android UI,使用Android自带的控件无法满足我们的需要就要考虑自绘控件,在Android界面显示类View,可以通过继承扩展重写相关方法来实现我们的图形绘制. 首先我们需要了解下View类的底层实现,在SDK中我们可以看到View直接继承于Java的基类Object,实现了图形绘制和按键事件 Drawable.Callback KeyEvent.Callback的相关方法,我们自绘时主要实现其内部的onDraw方法,相关的界面计算可以重写onMeasure方法

Android仿英语流利说取词放大控件的实现方法(附demo源码下载)_Android

本文实例讲述了Android仿英语流利说取词放大控件的实现方法.分享给大家供大家参考,具体如下: 1 取词放大控件 英语流利说是一款非常帮的口语学习app,在app的修炼页面长按屏幕,会弹出一个放大镜,当手指移到某个单词的附近,可以看到该英文单词会被选中,效果如下图所示: 2 代码示例 该控件挺有意思,于是我写了个简单的demo,完整实例代码点击此处本站下载.,程序运行后的效果如下: 3 实现原理 该控件的实现原理比较简单,下面介绍几个比较重要的类 ① WordView 在实习该控件的过程中,我

Android编程获取屏幕宽高与获取控件宽高的方法_Android

本文实例讲述了Android编程获取屏幕宽高与获取控件宽高的方法.分享给大家供大家参考,具体如下: 获取屏幕宽高 // 获取屏幕宽高(方法1) int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); // 屏幕宽(像素,如:480px) int screenHeight = getWindowManager().getDefaultDisplay().getHeight(); // 屏幕高(像素,如:800p) L

自绘控件的子类化方法

  1.       新建一个基于对话框的MFC程序. 2.       在工程中添加一个新类CMyButton,基类选择CButton. 3.       然后转至新生成的MyButton.h头文件中,将鼠标放在类名CMyButton上点击一下,打开"属性"窗口,在"消息"按钮下为此类添加 WM_LBUTTONDOWN,WM_LBUTTONUP消息,并重写DrawItem虚函数(这个函数是重绘按钮时要调用的,在这里面可以自定义一些绘制按钮的操作,必须设置控件属性为

Android 自绘控件

由于公司项目的需求,需要绘制一条竖直的间断线作为分割线.这个可坑了爹了,以前只搞过水平的间断线,只要通过shape也可以简单的画出来,但是千万记得把hardwareAccelerated给关了,不然水平的虚线还是会是实线,这个以前也是我趟过的一个坑.那咋办呢,找不到原生的解决方案,不就是一条简单的间断线嘛,还能难住我等程序猿?这个时候,首先联想到的就是自绘控件了. 自绘控件,顾名思义就是控件所展示的内容都是我们自己绘制上去的.所有的绘制操作就是在onDraw()方法里面进行的,当然我们的这个自定