自定义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 (一)

0 0
原创粉丝点击