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

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

android图片验证码--自绘控件的更多相关文章

  1. Android 自定义View之自绘控件

    首先要提前声明一下,我对于自定义View的理解并不是很深,最近啃了几天guolin博主写的关于自定义View的博客,讲的非常棒,只不过涉及到源码和底层的一些东西,我自己就懵逼了,目前只是会了关于自定义 ...

  2. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  3. Android 开源组件 ----- Android LoopView无限自动轮转控件

    Android 开源组件 ----- Android LoopView无限自动轮转控件 2015-12-28 15:26 by 杰瑞教育, 32 阅读, 0 评论, 收藏, 编辑 一.组件介绍 App ...

  4. android学习日记03--常用控件button/imagebutton

    常用控件 控件是对数据和方法的封装.控件可以有自己的属性和方法.属性是控件数据的简单访问者.方法则是控件的一些简单而可见的功能.所有控件都是继承View类 介绍android原生提供几种常用的控件bu ...

  5. android - 自定义(组合)控件 + 自定义控件外观

    转载:http://www.cnblogs.com/bill-joy/archive/2012/04/26/2471831.html android - 自定义(组合)控件 + 自定义控件外观   A ...

  6. Android高效率编码-细节,控件,架包,功能,工具,开源汇总,你想要的这里都有

    Android高效率编码-细节,控件,架包,功能,工具,开源汇总 其实写博客的初衷也并不是说什么分享技术,毕竟咱还只是个小程序员,最大的目的就是对自我的知识积累,以后万一编码的时候断片了,也可以翻出来 ...

  7. Android开发工程师文集-相关控件的讲解,五大布局

    前言 大家好,给大家带来Android开发工程师文集-相关控件的讲解,五大布局的概述,希望你们喜欢 TextView控件 TextView控件有哪些属性: android:id->控件的id a ...

  8. Android 拖动条/滑动条控件、星级评分控件

    ProgressBar有2个子控件: SeekBar   拖动条控件 RatingBar   星级评分控件 1.拖动条控件 <SeekBar android:layout_width=" ...

  9. Android布局属性与常用控件

    一.Android常用布局属性 1. LinearLayout的特有属性 android:orientation:设置布局排列方式   android:layout_weight:设置所占布局的权重  ...

随机推荐

  1. 2DToolkit官方文档中文版打地鼠教程(一):初始设置

    这是2DToolkit官方文档中 Whack a Mole 打地鼠教程的译文,为了减少文中过多重复操作的翻译,以及一些无必要的句子,这里我假设你有Unity的基础知识(例如了解如何新建Sprite等) ...

  2. 微信小程序新单位rpx与自适应布局

    rpx是微信小程序新推出的一个单位,按官方的定义,rpx可以根据屏幕宽度进行自适应,在rpx出现之前,web页面的自适应布局已经有了多种解决方案,为什么微信还捣鼓出新的rpx单位?在解释这个单位前,我 ...

  3. Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇

    目前业界流行的分布式消息队列系统(或者可以叫做消息中间件)种类繁多,比如,基于Erlang的RabbitMQ.基于Java的ActiveMQ/Apache Kafka.基于C/C++的ZeroMQ等等 ...

  4. 2000条你应知的WPF小姿势 基础篇<63-68 Triggers和WPF类逻辑结构>

    在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000ThingsYou Should Know About C# 和 2,00 ...

  5. Javascript 相关文章 —— 性能

    在 IBM Bluemix 云平台上开发并部署您的下一个应用. 开始您的试用 概览 无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成.JavaS ...

  6. Atitit Data Matrix dm码的原理与特点

    Atitit Data Matrix dm码的原理与特点 Datamatrix原名Datacode,由美国国际资料公司(International Data Matrix, 简称ID Matrix)于 ...

  7. MongoDB高级查询详细

    前言 前几篇,老玩家绕道即可,新手晚上闲着也是蛋疼,不如把命令敲一边,这样你就会对MongoDB有一定的掌握啦.如果没有安装MongoDB去看我的上一篇博客  MongoDB下载安装与简单增删改查 前 ...

  8. 【Win 10 应用开发】获取本机的IP地址

    按照老规矩,也是朋友的建议,老周今天在吹牛之前,先讲一个小故事. 有朋友问我,老周,你现在还发短信吗,你每个月用多少电话费?唉,实话说,现在真的发短信不多了,套餐送的130条短信,每月都发不了一条.至 ...

  9. Android消息传递之Handler消息机制

    前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...

  10. Hibernate中的锁机制

    锁机制:是数据库为了保证数据的一致性<一个事务的各种操作不相互影响>而使各种共享资源在被并发访问访问变得有序所设计的一种规则,用来保证在当前用户进行操作数据的时候其他的用户不能对同一数据进 ...