本系列到此,关于自定义view的大部分知识点已经讲述完了。本篇是此系列的最后一篇,以验证码 的实现来将前面的内容串在一起展现出来。
验证码是有随机生成的若干个数字 和 若干条干扰线组成,提供的功能主要有:
- 刷新验证吗
- 改变验证码个数
- 改变干扰线个数
- 改变验证码字体大小
- 改变验证码字体颜色
- 获取当前验证码
效果图如下:
CodeView需要添加4个自定义属性:验证码个数,干扰线条数,验证码字体大小,验证码字体颜色。
1,attrs.xml中添加属性如下:
<declare-styleable name="CodeView">
<attr name="count" format="integer" />
<attr name="line_count" format="integer"/>
<attr name="code_color" format="color"/>
<attr name="font_size" format="dimension"/>
</declare-styleable>
2,java代码读取设置的属性值
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CodeView);
count = a.getInteger(R.styleable.CodeView_count,DEFAULT_COUNT);
lineCount = a.getInteger(R.styleable.CodeView_line_count,DEFAULT_LINE_COUNT);
fontSize = a.getDimensionPixelSize(R.styleable.CodeView_font_size, (int)TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,DEFAULT_FONT_SIZE,getResources().getDisplayMetrics()
));
codeColor = a.getColor(R.styleable.CodeView_code_color,DEFAULT_COLOR);
a.recycle();
3,提供获取随机码的方法
private String getCode(){
String code = "";
for (int i = 0; i < count ; i++){
code = code + random.nextInt(10);
}
return code;
}
4,提供获取随机码宽高的方法(参照自定义TextView)
private Rect getTextRect (){
Rect rect = new Rect();
paint.getTextBounds(codeString,0,codeString.length(),rect);
return rect;
}
5,初始化画笔
private void initPaint(){
paint.reset();
paint.setAntiAlias(true);
paint.setColor(codeColor);
paint.setTextSize(fontSize);
}
6,onMeause测量CodeView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Rect rect = getTextRect();
int width = measureWidth(widthMeasureSpec,rect.width());
int height = measureHeight(heightMeasureSpec,rect.height());
setMeasuredDimension(width,height);
}
private int measureWidth(int widthMeasureSpec, int width){
int size = MeasureSpec.getSize(widthMeasureSpec);
int mode = MeasureSpec.getMode(widthMeasureSpec);
if(mode == MeasureSpec.EXACTLY){
return size;
}else if(mode == MeasureSpec.AT_MOST){
return getPaddingLeft() + width + getPaddingRight();//处理padding
}
return 0;
}
private int measureHeight(int heightMeasureSpec, int height){
int size = MeasureSpec.getSize(heightMeasureSpec);
int mode = MeasureSpec.getMode(heightMeasureSpec);
if(mode == MeasureSpec.EXACTLY){
return size;
}else if(mode == MeasureSpec.AT_MOST){
return getPaddingTop() + height + getPaddingBottom();//处理padding
}
return 0;
}
7,绘制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
Rect rect = new Rect(0,0,width,height);
//绘制外边框
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
Rect rect1 = new Rect(rect);
//rect1.inset(2,2);
canvas.drawRect(rect1,paint);
//绘制随机线
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GRAY);
for(int i = 0 ; i < lineCount ; i++){
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
canvas.drawLine(x1,y1,x2,y2,paint);
}
//绘制文字
paint.setColor(codeColor);
Rect textRect = getTextRect();
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
int x = (width - textRect.width()) / 2;
int y = (int)(height /2 + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent);
canvas.drawText(codeString,x,y,paint);
}
以上就是CodeView的主体代码了,现在我们就能根据xml中设置的默认值来绘制随机码了,但是我们的随机码还不能刷新,需要CodeView提供可供外界访问的修改 随机码内容、个数、颜色、字体大小、干扰线个数等接口。
//提供一些供外界调用了个刷新的方法
//刷新随机码
public void refreshCode(){
codeString = getCode();
invalidate();
}
//改变随机码颜色
public void changeCodeColor(int color) {
codeColor = color;
invalidate();
}
//改变随机码字体大小
public void changeCodeFontSize(int fontSizeSp){
fontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,fontSizeSp,getResources().getDisplayMetrics());
initPaint();
requestLayout();
}
//设置随机数个数
public void changeCodeCount(int codeCount){
count = codeCount;
codeString = getCode();
requestLayout();
}
//设置干扰线条数
public void changeLineCount(int lineCounts){
lineCount = lineCounts;
invalidate();
}
细心的同学可能发现了,在修改了CodeView的参数后,有的接口是调用invalide()来刷新,有的接口是调用requestLayout()来刷新,那么这两个方法有什么区别呢?
invalide()是重绘,即调用view的onDraw()方法来重新绘制view的内容,而view的宽高是不会重新计算的。
requestLayout()是先调用onMeasure(),在调用onDraw(),是会重新测量view的宽高的。
知道了这两个方法的区别,就可以理解上面的那些供外界调用了个刷新的方法了,只有在改变了随机码个数,随机码字体大小的时候,CodeView的大小才会发生变化,我们才需要重新Measure下CodeView的宽高。
CodeView的代码逻辑已经完成了,感兴趣的同学可以自己尝试做一个随机码出来哦。
DONE
至此,自定义view系列已经完成。