自定义view之一:自定义验证码控件
来源:互联网 发布:linux 用户安全设置 编辑:程序博客网 时间:2024/06/01 07:20
转载请表明出处:
http://blog.csdn.net/bxl049/article/details/50939712
一、概述
最近学习了一些关于自定义View控件的博客,积累了一些理论知识(自我感觉良好ing,哈哈)。现在想通过一个自定义验证码
控件来检验下自己的学习成果。先来讲下自定一view步骤:
1、自定义view属性;
2、在构造方法中获得我们自定义的属性;
3、重写onMeasure方法,设置控件大小;
4、重写ondraw方法。
步骤3不一定是必须的,当layout_widht和layout_height值为wrap_content时,则需要重写。
二、自定义验证码控件
2、1 自定view属性
首先在res/values下新建文件attrs.xml,在文件中定义属性以及声明我们的属性样式。
<?xml version=1.0 encoding="utf-8"?><resources> <attr name="codeLength" format="integer"/> <attr name="codeSize" format="dimension"/><declare-styleable name="valideCode> <attr name="codeLength"/> <attr name="codeSize"/></ resources>
这个我们定义了验证码字符串长度、字体大小两个属性,format类型是该属性的的取值类型,
string,color,integer,enum,float,boolean,flag,fraction,dimension,reference,具体含义大家可以谷歌下,然后我们在布局文
件中声明:
<?html version=1.0 encoding="uft-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com.apk/res_auto android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <com.vann.validecodetextview.ValideCodeTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:paddingleft="10dp" app:codeLength="4" app:codeSize="16sp"/></LinearLayout>
注意布局文件中要引入xmlns:app=”http://schemas.android.com/apk/res-auto”,不然我们是无法获取到我们的自定属性的。
2、2构造函数初始化
新建类ValideCodeTextView继承View,重写ValideCodeTextView的构造函数,并使最后运行含有三个参数的构造函数,在含有
是三个参数的构造函数中进行自定义属性获取以及部分变量初始化
//最大随机字符间隔private static final int MAX_PADDING = 16;//字体大小private float mCodeSize;// 验证码长度private int mCodeLength;//验证码private String mCode;//控件文本画笔、范围private Paint mPaint;private Rect mBound;//每个字符左边距距离private int paddingleft;private int paddingTop;private int mWidthMode;// 噪点画笔private Paint mPointPaint;// 干扰线画笔private Paint mPathPaint;private Context mContext;private Random mRandom = new Random();public ValideCodeTextView(Context context) { this(context, null);}public ValideCodeTextView(Context context, AttributeSet attrs) { this(context, attrs, 0);}public ValideCodeTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; //获得自定义属性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.valideCode); mCodeLength = ta.getInt(R.styleable.valideCode_codeLength, 1); mCodeSize = ta.getDimension(R.styleable.valideCode_codeSize, 0); ta.recycle(); mCode = getRandomText(); //文本画笔初始化 mPaint = new Paint(); mPaint.setTextSize(mCodeSize); mBound = new Rect(); mPaint.getTextBounds(mCode, 0, mCodeLength, mBound); mPathPaint = new Paint(); mPointPaint = new Paint();}
在三个参数的构造函数中获取到我们前面自定义的属性codeLength,codeSize,并对验证码文本画笔进行初始化。根据获取到的
自定义验证码长度获取随机验证码,验证码随机获取方法
如下:
/** * 获取随机位数的数字与符号的组合字符串 * * @return */private String getRandomText() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < mCodeLength; i++) { String ch = mRandom.nextInt(2) % 2 == 0 ? "char" : "num"; if ("char".equals(ch)) { int lowerOrUp = mRandom.nextInt(2) % 2 == 0 ? 65 : 97; char val = (char) (lowerOrUp + mRandom.nextInt(26)); sb.append(val); } else if ("num".equals(ch)) { sb.append(String.valueOf(mRandom.nextInt(10))); } } return sb.toString();}
2、3 重写onMeasure方法
当验证码控件定义的不是一个精确值时,我们需要重写该方法获得他的测量值、测量模式来确定控件的大小。重写之前先了解
MeasureSpec的
specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
下面是我们重写onMeasure代码:
/** * 计算测量值、模式,设置控件大小 * * @param widthMeasureSpec * @param heightMeasureSpec */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); mWidthMode = widthMode; int width, height; if (MeasureSpec.EXACTLY == widthMode) { width = widthSize; } else { mPaint.setTextSize(mCodeSize); randomPaintStyle(mPaint); int tWidth = 0; Rect child; for (int i = 0; i < mCodeLength; i++) { child = new Rect(); char c = mCode.charAt(i); mPaint.getTextBounds(c + "", 0, 1, child); float w = child.width(); int dw = (int) (w + MAX_PADDING); tWidth += dw; } width = tWidth + getPaddingLeft() + getPaddingRight(); } if (MeasureSpec.EXACTLY == heightMode) { height = heightSize; } else { mPaint.setTextSize(mCodeSize); randomPaintStyle(mPaint); mPaint.getTextBounds(mCode, 0, mCodeLength, mBound); float tHeight = mBound.height(); int h = (int) (tHeight + getPaddingTop() + getPaddingBottom()); height = h + MAX_PADDING; } setMeasuredDimension(width, height);}
在该方法中我们先获得了验证码控件的测量值和测量模式,当测量模式为MeasureSpec.EXACTLY 时,父控件用width,height,
进行setMeasureDimension大小设置,否则,对画笔样式进行随机设置,此时width等于左边距+右边距+每个字符的宽+字符
间距的最大随机数,取最大随机数是为了保证验证码文本不超出控件范围之外,文本画笔样式随机设置如下:
/** * 设置文本画笔的随机样式 * * @param paint */private void randomPaintStyle(Paint paint) { paint.setTextSize(mCodeSize); paint.setColor(getRandomColor(1)); paint.setFakeBoldText(mRandom.nextBoolean());//是否粗体 float skewx = mRandom.nextInt(11) / 10; skewx = mRandom.nextBoolean() ? skewx : -skewx; paint.setTextSkewX(skewx);// float类型参数,负数右倾,正数左倾}
2、4 重写onDraw方法
通过onMeasure方法我们已经确定了控件的大小,现在通过onDraw方法来画出验证码文本,先定义画板的默认背景色为白色,
然后获取每个随机验证码的字符宽度以及左边距,并记录下左边距,在画板上通过drawText方法分别画出字符。代码如下:
@Overrideprotected void onDraw(Canvas canvas) { mPaint.setColor(Color.WHITE); canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); Rect rec; int lastWidth = 0; paddingleft = getPaddingLeft(); for (int i = 0; i < mCodeLength; i++) { paddingTop = getHeight() / 2 + mRandom.nextInt(MAX_PADDING); randomPaintStyle(mPaint); char ch = mCode.charAt(i); rec = new Rect(); mPaint.getTextBounds(ch + "", 0, 1, rec); lastWidth = rec.width(); paddingleft += lastWidth + mRandom.nextInt(MAX_PADDING); canvas.drawText(mCode.charAt(i) + "", paddingleft, paddingTop, mPaint); } int dw = getMeasuredWidth(); int w = paddingleft + lastWidth;// Toast.makeText(mContext,"控件长度:"+dw+"验证码长度:"+w,Toast.LENGTH_SHORT).show(); // 当模式不为EXACTLY且验证码长度大于控件长度时,重新获取验证码并绘制 if (MeasureSpec.EXACTLY != mWidthMode && paddingleft + lastWidth > dw) { mCode = getRandomText(); postInvalidate(); return; } for (int i = 0; i < 2; i++) { randomPathPaintStyle(mPathPaint); drawLine(canvas, mPathPaint); } for (int i = 0; i < 50; i++) { randomPointPaintStyle(mPointPaint); drawPoint(canvas, mPointPaint); } canvas.save(Canvas.ALL_SAVE_FLAG); canvas.restore();}
画出文本后,判断后一个字符的左边距是否大于控件的实际距离,如果大于则重新获取随机验证码字符串,如果没有,则进行噪
点和干扰线的生成操作,噪点我们固定生成150各,干扰线为2条,噪点生成的代码如下:
/** * 绘制噪点 * * @param canvas * @param mPointPaint */private void drawPoint(Canvas canvas, Paint mPointPaint) { int x = mRandom.nextInt(getWidth()); int y = mRandom.nextInt(getHeight()); canvas.drawPoint(x, y, mPointPaint);}
每次画噪点前先随机获取噪点画笔的样式,以便获取到不同颜色的噪点。画干扰线的方法如下:
/** * 绘制干扰线 * * @param canvas * @param mPaint */private void drawLine(Canvas canvas, Paint mPaint) { int startX = mRandom.nextInt(getWidth()); int startY = mRandom.nextInt(getHeight()); int endX = mRandom.nextInt(getWidth()); int endY = mRandom.nextInt(getHeight()); canvas.drawLine(startX, startY, endX, endY, mPaint);}
至此验证码已画出,当我们点击控件时需要刷新验证码文本,因此,需要给控件添加一个监听事件,在构造函数中添加添加事
件,并在添加事件中重新获取随机验证码,重绘图像,监听代码如下:
this.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mCode = getRandomText(); postInvalidate(); } });
好了,现在看下运行结果:
参考博客:
洪洋大神的文章—Android 自定义View (一)
- 自定义view之一:自定义验证码控件
- Android自定义View--验证码控件
- 自定义“验证码”控件
- 自定义“验证码”控件
- 自定义“验证码”控件
- 自定义验证码控件
- Android自定义View入门之简单验证码控件
- Android自定义View 做个简单的验证码控件
- 自定义控件,自定义View
- 自定义View-输入验证码
- Android 自定义View (验证码)
- Android自定义View之一
- Android自定义View之一
- 自定义View之一五子棋
- android 自定义控件以及自定义view学习(随机验证码生成)
- Android自定义控件(特效三) 自定义View实现图片验证码
- android 自定义控件以及自定义view学习(随机验证码生成)
- 自定义控件之一:自定义属性
- Android使用xml旋转图片,个人记录
- Apache commons (Java常用工具包)简介
- 控制台五子棋游戏类记录
- 导入v4库源码
- leetcode012 Integer to Roman
- 自定义view之一:自定义验证码控件
- QQ也可以和微信一样只能共同好友见评论
- matlab图像数据转换函数
- 问题 A: 输入字符串以及输出
- 123
- lintcode: Balanced Binary Tree
- ACM——GCD算法
- mybatis 中#与$的区别
- SwingWeb