验证码在网页应用中可以说应用十分广泛,但是我们在Android开发中有时候也是需要用到验证码的,其实验证码是一个最基础的自定义控件,原理非常的简单,接下来直接上代码
首先需要一些自定义的属性
<!--验证码控件自定义属性--> <declare-styleable name="custom_Verification_Code"> <!--验证码文字大小--> <attr name="codeTextSize" format="dimension|reference"/> <!--验证码长度--> <attr name="codeLength" format="integer|reference"/> <!--验证码类型--> <attr name="codeType"> <!--纯数字--> <enum name="num" value="0"/> <!--字母--> <enum name="litter" value="1"/> <!--中文--> <enum name="chinese" value="2"/> <!--字母和数字混合--> <enum name="litterAndNum" value="3"/> </attr> </declare-styleable>这里要说一下codeType这个属性,是个枚举,也就是我们经常在其他View中使用的常量,这里如果你给codeType没有定义format属性的话,那么最好使用int类型,如果定义了其他属性,要在Java文件中做接收处理。
接下来看一个各个类型的验证码生成方法
/** * 生成纯字母 * @return */ public String getLetter(){ String val = ""; Random random = new Random(); int temp = random.nextInt(2) % 2 == 0 ? 65 : 97; val += (char)(random.nextInt(26) + temp); return val; }
/** * 生成数字和字母 * @return */ public String getLetterAndNum() { String val = ""; Random random = new Random(); int charOrNum = random.nextInt(2) % 2; //输出字母还是数字 if( charOrNum==0 ) { //输出是大写字母还是小写字母 int temp = random.nextInt(2) % 2 == 0 ? 65 : 97; val += (char)(random.nextInt(26) + temp); } { val += String.valueOf(random.nextInt(10)); } return val; }
/** * 生成随机汉字 * @return */ private char getChineseChar() { String str = ""; int hightPos; int lowPos; hightPos = (176 + Math.abs(random.nextInt(39))); lowPos = (161 + Math.abs(random.nextInt(93))); byte[] b = new byte[2]; b[0] = (Integer.valueOf(hightPos)).byteValue(); b[1] = (Integer.valueOf(lowPos)).byteValue(); try { str = new String(b, "GBK"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return str.charAt(0); }生成0到9数字就不贴出来了,这个应该很简单。
做好这些尊卑工作,那么首先我们要接收自定义属性,以及初始化画笔之类的
/** * 画笔 */ private Paint mPaint; /** * code的size */ private float mCodeTextSize; /** * code的字符串 */ private String mCodeStr; /** * 字符串的长度 */ private int mCodeLength; /** * 文本矩形 */ private Rect mRect; /** * 验证码的类型 */ private int mCodeType; /** * 纯数字 */ public final static int NUM_CODE = 0; /** * 纯字母 */ public final static int LETTER_CODE = 1; /** * 汉子 */ public final static int CHAINESE_CODE = 2; /** * 数字与字母 */ public final static int LETTER_NUM_CODE = 3; /** * 点的数量,可以自己定义 */ private int mSortNum = 100; /** * 线的数量,可以自己定义 */ private int mLineNum = 5; private Random random = new Random(); public VerificationCodeView(Context context) { this(context,null); } public VerificationCodeView(Context context, AttributeSet attrs) { this(context, attrs,0); } public VerificationCodeView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); /** * 获取自定义属性 */ TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.custom_Verification_Code); mCodeTextSize = typedArray.getDimension(R.styleable.custom_Verification_Code_codeTextSize,14f); mCodeLength = typedArray.getInteger(R.styleable.custom_Verification_Code_codeLength,4); mCodeType = typedArray.getInt(R.styleable.custom_Verification_Code_codeType,0); typedArray.recycle(); mCodeStr = randomText(); initPaint(); Log.d("custom_view",mCodeStr); setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { /** * 点击重新生成字符串并刷新控件 */ mCodeStr = randomText(); postInvalidate(); } }); } /** * 返回生成的字符串 * @return */ public String getCodeStr() { return mCodeStr; } /** * 初始化画笔,并且把字符串的宽高赋值给一个矩形 */ private void initPaint(){ mPaint = new Paint(); mPaint.setTextSize(mCodeTextSize); mRect = new Rect(); mPaint.getTextBounds(mCodeStr,0,mCodeStr.length(),mRect); } /** * 随机生成不同类型的验证码 * @return */ private String randomText() { StringBuilder codeSb = new StringBuilder(); for (int i = 0; i < mCodeLength; i++) { switch (mCodeType){ case NUM_CODE: int randomInt = (int) Math.floor(Math.random()*10);//随机数取整 codeSb.append(randomInt); break; case LETTER_CODE: codeSb.append(getLetter()); break; case CHAINESE_CODE: codeSb.append(getChineseChar()); break; case LETTER_NUM_CODE: codeSb.append(getLetterAndNum()); break; } } return codeSb.toString(); }接着就是View大小的计算,需要重写onMeasure
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { /** * 设置控件的宽高 */ Log.d("custom_view","2"); int width = 0; int height = 0; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); if (widthMode==MeasureSpec.EXACTLY){ /** * 给了具体的宽高 */ width = getPaddingLeft()+getPaddingRight()+widthSize; }else { /** * 自适应宽高 */ width = getPaddingLeft()+getPaddingRight()+mRect.width(); } int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (heightMode==MeasureSpec.EXACTLY){ height = getPaddingTop()+getPaddingBottom()+heightSize; }else { height = getPaddingTop()+getPaddingBottom()+mRect.height(); } setMeasuredDimension(width,height); }接下来是一些坐标点的计算
/** * 文字y轴 * @param height * @return */ private int getPositon(int height) { int strY = (int) (Math.random() * height); if (strY < 20) { strY += 20; } return strY; } /** * 随机产生原点 * @param height * @param width * @return */ public static int[] getSortXY(int height, int width) { int[] SortXY = { 0, 0}; SortXY[0] = (int) (Math.random() * width); SortXY[1] = (int) (Math.random() * height); return SortXY; } /** * 生成线条的起点和重点坐标 * @param height * @param width * @return */ public static int[] getLineXY(int height, int width) { int[] lineSortXY = { 0, 0, 0, 0 }; for (int i = 0; i < 4; i += 2) { lineSortXY[i] = (int) (Math.random() * width); lineSortXY[i + 1] = (int) (Math.random() * height); } return lineSortXY; }接着就是画图了,ondraw
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /** * 画背景 */ mPaint.setAntiAlias(true); // 消除锯齿 Resources resources = getResources(); Drawable backDrawable = resources.getDrawable(R.drawable.code_back); if (backDrawable!=null){ backDrawable.setBounds(0,0,getMeasuredWidth(),getMeasuredHeight()); backDrawable.draw(canvas); } /** * 画点 */ for(int i = 0; i < mSortNum; i ++) { mPaint.setColor(Color.argb(100,random.nextInt(255),random.nextInt(255),random.nextInt(255))); int randomInt = random.nextInt(5); int [] SortXY = getSortXY(getMeasuredHeight(), getMeasuredWidth()); canvas.drawCircle(SortXY[0], SortXY[1], randomInt, mPaint); } /** * 画线 */ for (int i = 0;i < mLineNum;i++){ mPaint.setColor(Color.argb(100,random.nextInt(255),random.nextInt(255),random.nextInt(255))); int [] lineXY = getLineXY(getMeasuredHeight(),getMeasuredWidth()); mPaint.setStrokeWidth(5); canvas.drawLine(lineXY[0],lineXY[1],lineXY[2],lineXY[3],mPaint); } /** * 画文字 */ for(int i = 0; i < mCodeStr.length(); i ++){ mPaint.setColor(Color.argb(100,random.nextInt(255),random.nextInt(255),random.nextInt(255))); int tx = (getMeasuredWidth()-getPaddingLeft()-getPaddingRight())/(mCodeLength+1)*(i+1); //canvas.rotate((float) (Math.random()*10)-5); canvas.drawText(mCodeStr.charAt(i)+"",tx, getHeight() / 2 + getPositon(mRect.height() / 2), mPaint); // canvas.restore(); // canvas.save(); } }好了,到这里这个View就算完成了,接着只需要在布局文件中加入控件就可以了
<zjt.com.customviewdemo.VerificationCodeView android:layout_width="wrap_content" android:layout_height="wrap_content" app:codeTextSize="20sp" app:codeLength="6" app:codeType="litterAndNum" android:paddingLeft="20dp" android:paddingRight="20dp" android:paddingTop="15dp" android:paddingBottom="15dp" />
接下来是个效果图
到这里这个控件就算完成了
资源链接: