接着第一篇内容,来完成一下中间部分,中文验证码的部分,先看一下要实现的效果:
一、分析功能
分析一下,我们要实现一个验证码功能,随机生成4个中文组合,文字随机,文字颜色随机,文字会出现不同程度的旋转,并且文字周围会出现一些干扰项,点击看不清时验证码内容会进行刷新重置.要解决的问题基本就这么多,下面分步来解决一下.
(1)文字和文字颜色随机,这个实现起来不难,只需要随机产生一个中文,并且画笔定义随机的颜色即可.
(2)文字旋转的话,可以通过旋转画布来完成该功能.
(3)对于干扰项,可以定义一个干扰项的数组,然后随机取出里边的内容,在画布上的随机位置进行绘制即可.
(4)刷新重置这个做过很多遍,只需要重置一些变量,再去调用view的重绘方法即可.
二、代码实现
通过上边的分析,我们自定义一个CodeView来完成该功能.
先来定义一些自定义属性,成员变量.代码如下:
自定义属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--CodeView相关-->
<!--扰乱项的个数-->
<attr name="disturbSize" format="integer"/>
<!--干扰项文字的大小-->
<attr name="disturbTextSize" format="dimension"/>
<!--干扰项文字颜色-->
<attr name="disturbTextColor" format="color"/>
<!--验证码文字大小-->
<attr name="codeTextSize" format="dimension"/>
<!--验证码文字颜色-->
<attr name="codeTextColor" format="color"/>
<!--画布旋转的度数-->
<attr name="rotate" format="integer"/>
<declare-styleable name="CodeView">
<attr name="disturbSize"/>
<attr name="disturbTextSize"/>
<attr name="disturbTextColor"/>
<attr name="codeTextSize"/>
<attr name="codeTextColor"/>
<attr name="rotate"/>
</resources>
变量:
/**
* 干扰项
*/
private final char[] CHARS = {
‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘,
‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘, ‘g‘, ‘h‘, ‘i‘, ‘j‘, ‘k‘, ‘l‘, ‘m‘,
‘n‘, ‘o‘, ‘p‘, ‘q‘, ‘r‘, ‘s‘, ‘t‘, ‘u‘, ‘v‘, ‘w‘, ‘x‘, ‘y‘, ‘z‘,
‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘, ‘F‘, ‘G‘, ‘H‘, ‘I‘, ‘J‘, ‘K‘, ‘L‘, ‘M‘,
‘N‘, ‘O‘, ‘P‘, ‘Q‘, ‘R‘, ‘S‘, ‘T‘, ‘U‘, ‘V‘, ‘W‘, ‘X‘, ‘Y‘, ‘Z‘
};
/**
* 汉字画笔
*/
private Paint hzPaint;
/**
* 干扰项画笔
*/
private Paint dbPaint;
/**
* 汉字画笔颜色
*/
private int hzColor;
/**
* 干扰项画笔颜色
*/
private int dbColor;
/**
* 干扰项的个数,默认30个
*/
private int DEFAULT_DBSIZE;
/**
* 默认画笔颜色
*/
private int DEFAULT_DBCOLOR, DEFAULT_HZCOLOR;
/**
* 干扰项随机生成的位置
*/
private float dbRandomX, dbRandomY;
/**
* 验证码文字
*/
private List<String> codeList;
/**
* 随机
*/
private Random random = new Random();
/**
* 矩形区域
*/
private Rect mBounds;
/**
* 干扰文字大小
*/
private int dbTextSize;
/**
* 验证码文字大小
*/
private int hzTextSize;
/**
* 画布旋转度数,默认为6
*/
private int rotate;
public CodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 获取自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CodeView);
DEFAULT_DBSIZE = ta.getInteger(R.styleable.CodeView_disturbSize, 30);
dbTextSize = DisplayUtil.sp2px(context, ta.getDimensionPixelSize(R.styleable.CodeView_disturbTextSize, 10));
hzTextSize = DisplayUtil.sp2px(context, ta.getDimensionPixelSize(R.styleable.CodeView_codeTextSize, 20));
DEFAULT_DBCOLOR = ta.getColor(R.styleable.CodeView_disturbTextColor, 0);
DEFAULT_HZCOLOR = ta.getColor(R.styleable.CodeView_codeTextColor, 0);
rotate = ta.getInteger(R.styleable.CodeView_rotate, 6);
init();
}
通过分析我们知道,我们需要随机生成一个中文,然后还要随机生成文字的颜色,所以先来写一个工具类Util,方便之后的使用.
工具类:
package com.example.junweiliu.hanzicode;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import android.widget.ListAdapter;
import java.io.UnsupportedEncodingException;
import java.util.Random;
/**
* Created by junweiliu on 16/5/9.
*/
public class Util {
/**
* 获取随机的颜色
*
* @return
*/
public static int randomColor() {
int red = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
return Color.argb(255, red, green, blue);
}
/**
* 获取随机的暗色
*
* @return
*/
public static int randomDarkColor() {
int red = (int) (Math.random() * 100 + 56);
int green = (int) (Math.random() * 100 + 56);
int blue = (int) (Math.random() * 100 + 56);
return Color.argb(255, red, green, blue);
}
/**
* 获取指定长度随机简体中文
*
* @param len int
* @return String
*/
public static String getRandomJianHan(int len) {
String ret = "";
for (int i = 0; i < len; i++) {
String str = null;
int hightPos, lowPos; // 定义高低位
Random random = new Random();
hightPos = (176 + Math.abs(random.nextInt(39))); //获取高位值
lowPos = (161 + Math.abs(random.nextInt(93))); //获取低位值
byte[] b = new byte[2];
b[0] = (new Integer(hightPos).byteValue());
b[1] = (new Integer(lowPos).byteValue());
try {
str = new String(b, "GBk"); //转成中文
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
ret += str;
}
return ret;
}
}
代码不是很复杂,这里定义了两种颜色的获取,使用的时候可以根据自己的需要来使用或者修改.
先来搞定干扰项的问题,干扰项已经定义过了,是一些数字和字母,随机颜色的方法也写在工具类了,之后就是去生成这些干扰项,生成之前,还需要随机生成位置,然后去绘制在画布上就可以了.
干扰项:
/**
* 创建干扰项
*
* @return
*/
private String createDBCode() {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < DEFAULT_DBSIZE; i++) {
buffer.append(CHARS[random.nextInt(CHARS.length)]);
}
return buffer.toString();
}
/**
* 随机生成干扰项画笔样式
*/
private void randomDBStyele() {
if (0 == DEFAULT_DBCOLOR) {
dbColor = Util.randomDarkColor();
} else {
dbColor = DEFAULT_DBCOLOR;
}
dbPaint = new Paint();
dbPaint.setColor(dbColor);
dbPaint.setTextSize(dbTextSize);
}
/**
* 随机生成干扰项的显示位置
*/
private void randomDBPosition() {
dbRandomX = (float) Math.random() * getWidth();
dbRandomY = (float) Math.random() * getHeight();
}
/**
* 绘制
*/
@Override
protected void onDraw(Canvas canvas) {
Bitmap bp = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bp);
String dbCode = createDBCode();
// 绘制干扰项
for (int i = 0; i < dbCode.length(); i++) {
randomDBStyele();
randomDBPosition();
c.drawText(dbCode.charAt(i) + "", dbRandomX, dbRandomY, dbPaint);
}
canvas.drawBitmap(bp, 0, 0, dbPaint);
}
然后来看一下验证文字,我们对外提供一个设置验证文字的方法,因为这些需要验证的文字肯定不是固定的,所以需要提供一个方法,来随时改变这些验证文字,获取到这些验证文字之后,就可以去绘制了.
验证文字:
/**
* 设置验证码文字
*
* @param codes
*/
public void setCode(List<String> codes) {
this.codeList = codes;
invalidate();
}
/**
* 随机生成汉字画笔样式
*/
private void randomHZStyle() {
hzPaint = new Paint();
hzPaint.setColor(hzColor);
hzPaint.setFakeBoldText(random.nextBoolean());
hzPaint.setTextSize(hzTextSize);
hzPaint.getTextBounds("一", 0, "一".length(), mBounds);
}
/**
* 绘制
*/
@Override
protected void onDraw(Canvas canvas) {
Bitmap bp = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bp);
String dbCode = createDBCode();
// 绘制干扰项
for (int i = 0; i < dbCode.length(); i++) {
randomDBStyele();
randomDBPosition();
c.drawText(dbCode.charAt(i) + "", dbRandomX, dbRandomY, dbPaint);
}
canvas.drawBitmap(bp, 0, 0, dbPaint);
// 绘制验证文字
if (null != codeList && codeList.size() > 0) {
for (int i = 0; i < codeList.size(); i++) {
randomHZStyle();
canvas.save();
canvas.rotate((int) (Math.floor(Math.random() * rotate + 1)
- Math.floor(Math.random() * rotate * 2 + 1)));
canvas.drawText(codeList.get(i), mBounds.width() * (i + 1), getHeight() / 2 + mBounds.height() / 2, hzPaint);
canvas.restore();
}
}
}
绘制验证文字的时候需要注意一下,这里只是对画布进行了一个旋转操作,如果需要设计不同的效果,可以去添加修改这部分,如果需要特殊字体,也可以在初始化的时候去加载一些特殊字体,这里就不去做这些内容了.
最后就是对外提供一个重置的方法,很简单
/**
* 重置
*/
public void reSet(List<String> codes) {
// 重置汉字画笔颜色
if (0 == DEFAULT_HZCOLOR) {
hzColor = Util.randomDarkColor();
} else {
hzColor = DEFAULT_HZCOLOR;
}
this.codeList.clear();
this.codeList = codes;
invalidate();
}
三、完整代码实及现
CodeView:
package com.example.junweiliu.hanzicode;
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.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Created by junweiliu on 16/5/9.
*/
public class CodeView extends ImageView {
/**
* 干扰项
*/
private final char[] CHARS = {
‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘,
‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘, ‘g‘, ‘h‘, ‘i‘, ‘j‘, ‘k‘, ‘l‘, ‘m‘,
‘n‘, ‘o‘, ‘p‘, ‘q‘, ‘r‘, ‘s‘, ‘t‘, ‘u‘, ‘v‘, ‘w‘, ‘x‘, ‘y‘, ‘z‘,
‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘, ‘F‘, ‘G‘, ‘H‘, ‘I‘, ‘J‘, ‘K‘, ‘L‘, ‘M‘,
‘N‘, ‘O‘, ‘P‘, ‘Q‘, ‘R‘, ‘S‘, ‘T‘, ‘U‘, ‘V‘, ‘W‘, ‘X‘, ‘Y‘, ‘Z‘
};
/**
* 汉字画笔
*/
private Paint hzPaint;
/**
* 干扰项画笔
*/
private Paint dbPaint;
/**
* 汉字画笔颜色
*/
private int hzColor;
/**
* 干扰项画笔颜色
*/
private int dbColor;
/**
* 干扰项的个数,默认30个
*/
private int DEFAULT_DBSIZE;
/**
* 默认画笔颜色
*/
private int DEFAULT_DBCOLOR, DEFAULT_HZCOLOR;
/**
* 干扰项随机生成的位置
*/
private float dbRandomX, dbRandomY;
/**
* 验证码文字
*/
private List<String> codeList;
/**
* 随机
*/
private Random random = new Random();
/**
* 矩形区域
*/
private Rect mBounds;
/**
* 干扰文字大小
*/
private int dbTextSize;
/**
* 验证码文字大小
*/
private int hzTextSize;
/**
* 画布旋转度数,默认为6
*/
private int rotate;
public CodeView(Context context) {
this(context, null);
}
public CodeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
;
public CodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 获取自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CodeView);
DEFAULT_DBSIZE = ta.getInteger(R.styleable.CodeView_disturbSize, 30);
dbTextSize = DisplayUtil.sp2px(context, ta.getDimensionPixelSize(R.styleable.CodeView_disturbTextSize, 10));
hzTextSize = DisplayUtil.sp2px(context, ta.getDimensionPixelSize(R.styleable.CodeView_codeTextSize, 20));
DEFAULT_DBCOLOR = ta.getColor(R.styleable.CodeView_disturbTextColor, 0);
DEFAULT_HZCOLOR = ta.getColor(R.styleable.CodeView_codeTextColor, 0);
rotate = ta.getInteger(R.styleable.CodeView_rotate, 6);
init();
}
/**
* 初始化
*/
private void init() {
// 验证的文字颜色一致,只需要生成一次
if (0 == DEFAULT_HZCOLOR) {
hzColor = Util.randomDarkColor();
} else {
hzColor = DEFAULT_HZCOLOR;
}
mBounds = new Rect();
codeList = new ArrayList<String>();
}
@Override
protected void onDraw(Canvas canvas) {
Bitmap bp = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bp);
String dbCode = createDBCode();
// 绘制干扰项
for (int i = 0; i < dbCode.length(); i++) {
randomDBStyele();
randomDBPosition();
c.drawText(dbCode.charAt(i) + "", dbRandomX, dbRandomY, dbPaint);
}
canvas.drawBitmap(bp, 0, 0, dbPaint);
// 绘制验证文字
if (null != codeList && codeList.size() > 0) {
for (int i = 0; i < codeList.size(); i++) {
randomHZStyle();
canvas.save();
canvas.rotate((int) (Math.floor(Math.random() * rotate + 1)
- Math.floor(Math.random() * rotate * 2 + 1)));
canvas.drawText(codeList.get(i), mBounds.width() * (i + 1), getHeight() / 2 + mBounds.height() / 2, hzPaint);
canvas.restore();
}
}
// super.onDraw(canvas);
}
/**
* 重置
*/
public void reSet(List<String> codes) {
// 重置汉字画笔颜色
if (0 == DEFAULT_HZCOLOR) {
hzColor = Util.randomDarkColor();
} else {
hzColor = DEFAULT_HZCOLOR;
}
this.codeList.clear();
this.codeList = codes;
invalidate();
}
/**
* 设置验证码文字
*
* @param codes
*/
public void setCode(List<String> codes) {
this.codeList = codes;
invalidate();
}
/**
* 随机生成汉字画笔样式
*/
private void randomHZStyle() {
hzPaint = new Paint();
// hzPaint.setAntiAlias(true);
hzPaint.setColor(hzColor);
hzPaint.setFakeBoldText(random.nextBoolean());
hzPaint.setTextSize(hzTextSize);
hzPaint.getTextBounds("一", 0, "一".length(), mBounds);
}
/**
* 随机生成干扰项画笔样式
*/
private void randomDBStyele() {
if (0 == DEFAULT_DBCOLOR) {
dbColor = Util.randomDarkColor();
} else {
dbColor = DEFAULT_DBCOLOR;
}
dbPaint = new Paint();
// dbPaint.setAntiAlias(true);
dbPaint.setColor(dbColor);
dbPaint.setTextSize(dbTextSize);
}
/**
* 随机生成干扰项的显示位置
*/
private void randomDBPosition() {
dbRandomX = (float) Math.random() * getWidth();
dbRandomY = (float) Math.random() * getHeight();
}
/**
* 创建干扰项
*
* @return
*/
private String createDBCode() {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < DEFAULT_DBSIZE; i++) {
buffer.append(CHARS[random.nextInt(CHARS.length)]);
}
return buffer.toString();
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:hz="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_all"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context="com.example.junweiliu.hanzicode.MainActivity">
<LinearLayout
android:layout_width="280dp"
android:layout_height="wrap_content"
android:background="@mipmap/hz_nor_ng"
android:orientation="vertical"
android:padding="20dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:orientation="horizontal"
android:visibility="visible"
>
<com.example.junweiliu.hanzicode.CodeView
android:id="@+id/cv_hz"
android:layout_width="120dp"
android:layout_height="40dp"
/>
<Button
android:id="@+id/btn_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@null"
android:text="看不清?"
android:textColor="#4791FF"
android:textSize="14sp"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
MainActivity:
package com.example.junweiliu.hanzicode;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.graphics.ColorUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ListAdapter;
import android.widget.Toast;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MainActivity extends Activity {
/**
* 验证码
*/
private CodeView mCodeView;
/**
* 重置按钮
*/
private Button mResetBtn;
/**
* 正确答案
*/
private List<String> mCorrectList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
/**
* 初始化数据
*/
private void initData() {
mCorrectList = new ArrayList<String>();
// 添加文字
for (int i = 0; i < 4; i++) {
mCorrectList.add(Util.getRandomJianHan(1));
}
}
/**
* 初始化控件
*/
private void initView() {
mCodeView = (CodeView) findViewById(R.id.cv_hz);
mCodeView.setCode(mCorrectList);
mResetBtn = (Button) findViewById(R.id.btn_reset);
mResetBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 重置文字
initData();
// 刷新验证码
mCodeView.reSet(mCorrectList);
}
});
}
}