Android 自定义View之随机生成图片验证码

本篇文章讲的是Android自定义View之随机生成图片验证码,开发中我们会经常需要随机生成图片验证码,但是这个是其次,主要还是想总结一些自定义View的开发过程以及一些需要注意的地方。

按照惯例先看看效果图:

一、先总结下自定义View的步骤:

1、自定义View的属性

2、在View的构造方法中获得我们自定义的属性

3、重写onMesure

4、重写onDraw

其中onMesure方法不一定要重写,但大部分情况下还是需要重写的

二、View 的几个构造函数

1、public CustomView(Context context)

—>java代码直接new一个CustomView实例的时候,会调用这个只有一个参数的构造函数;

2、public CustomView(Context context, AttributeSet attrs)

—>在默认的XML布局文件中创建的时候调用这个有两个参数的构造函数。AttributeSet类型的参数负责把XML布局文件中所自定义的属性通过AttributeSet带入到View内;

3、public CustomView(Context context, AttributeSet attrs, int defStyleAttr)

—>构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或者Activity所用的Theme中的默认Style,且只有在明确调用的时候才会调用

4、public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

—>该构造函数是在API21的时候才添加上的

三、下面我们就开始来看看代码啦

1、自定义View的属性,首先在res/values/ 下建立一个attr.xml , 在里面定义我们的需要用到的属性以及声明相对应属性的取值类型

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="text" format="string" />
    <attr name="textColor" format="color" />
    <attr name="textSize" format="dimension" />
    <attr name="bgColor" format="color" />

    <declare-styleable name="CustomView">
        <attr name="text" />
        <attr name="textColor" />
        <attr name="textSize" />
        <attr name="bgColor" />
    </declare-styleable>

</resources>

我们定义了字体,字体颜色,字体大小以及字体的背景颜色4个属性,format是值该属性的取值类型,format取值类型总共有10种,包括:string,color,demension,integer,enum,reference,float,boolean,fraction和flag。

2、然后在XML布局中声明我们的自定义View

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.per.customview01.view.CustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:padding="10dp"
        custom:bgColor="#FF27FF28"
        custom:text="J2RdWQG"
        custom:textColor="#ff0000"
        custom:textSize="36dp" />

</RelativeLayout>

一定要引入xmlns:custom=”http://schemas.android.com/apk/res-auto”,Android Studio中我们可以使用res-atuo命名空间,就不用在添加自定义View全类名。

3、在View的构造方法中,获得我们的自定义的样式

/**
     * 文本
     */
    private String mText;
    /**
     * 文本的颜色
     */
    private int mTextColor;
    /**
     * 文本的大小
     */
    private int mTextSize;
    /**
     * 文本的背景颜色
     */
    private int mBgCplor;
    private Rect mBound;
    private Paint mPaint;

    public CustomView(Context context) {
        this(context, null);
    }

    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        /**
         * 获得我们所定义的自定义样式属性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);
        for (int i = 0; i < a.getIndexCount(); i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.CustomView_text:
                    mText = a.getString(attr);
                    break;
                case R.styleable.CustomView_textColor:
                    // 默认文本颜色设置为黑色
                    mTextColor = a.getColor(R.styleable.CustomView_textColor, Color.BLACK);
                    break;
                case R.styleable.CustomView_bgColor:
                    // 默认文本背景颜色设置为蓝色
                    mBgCplor = a.getColor(R.styleable.CustomView_bgColor, Color.BLUE);
                    break;
                case R.styleable.CustomView_textSize:
                    // 默认设置为16sp,TypeValue也可以把sp转化为px
                    mTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
            }
        }
        a.recycle();
        // 获得绘制文本的宽和高
        mPaint = new Paint();
        mPaint.setTextSize(mTextSize);

        mBound = new Rect();
        mPaint.getTextBounds(mText, 0, mText.length(), mBound);
    }

我们重写了3个构造方法,在上面的构造方法中说过默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造方法调用三个参数的构造方法,然后在三个参数的构造方法中获得自定义属性。

一开始一个参数的构造方法和两个参数的构造方法是这样的:

 public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

有一点要注意的是super应该改成this,然后让一个参数的构造方法引用两个参数的构造方法,两个参数的构造方法引用三个参数的构造方法,代码如下:

 public CustomView(Context context) {
        this(context, null);
    }

    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

4、重写onDraw,onMesure方法

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(mBgCplor);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);

        mPaint.setColor(mTextColor);
        canvas.drawText(mText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
    }

View的绘制流程是从ViewRoot的performTravarsals方法开始的,经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中:

  • 测量——onMeasure():用来测量View的宽和高来决定View的大小
  • 布局——onLayout():用来确定View在父容器ViewGroup中的放置位置
  • 绘制——onDraw():负责将View绘制在屏幕上

    来看下此时的效果图

    细心的朋友会发现,在上面的布局文件中,我们是把宽和高设置为wrap_content的,可是这个效果图怎么看都不是我们想要的,这是因为系统帮我们测量的高度和宽度默认是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,这个是对的。但是除了设置明确的宽度和高度,不管我们设置为WRAP_CONTENT还是MATCH_PARENT,系统帮我们测量的结果就是MATCH_PARENT,所以,当我们设置了WRAP_CONTENT时,我们需要自己进行测量,也就是说我们需要重写onMesure方法

    下面是我们重写onMeasure代码:

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heighMode = MeasureSpec.getMode(heightMeasureSpec);
        int heighSize = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : getPaddingLeft() + getPaddingRight() + mBound.width(), heighMode == MeasureSpec.EXACTLY ? heighSize : getPaddingTop() + getPaddingBottom() + mBound.height());
    }

MeasureSpec封装了父布局传递给子布局的布局要求,MeasureSpec的specMode一共有三种模式:

(1)EXACTLY(完全):一般是设置了明确的值或者是MATCH_PARENT,父元素决定了子元素的大小,子元素将被限定在给定的范围里而忽略它本身大小;

(2)AT_MOST(至多):表示子元素至多达到给定的一个最大值,一般为WARP_CONTENT;

我们再看看效果图

现在这个是我们想要的结果了吧,回归到主题,今天讲的是自定义View之随机生成图片验证码,现在把自定义View的部分完成了,我把完整的代码贴出来

package com.per.customview01.view;

import android.content.Context;
import android.content.res.TypedArray;
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.util.TypedValue;
import android.view.View;

import com.per.customview01.R;

import java.util.Random;

/**
 * @author: adan
 * @description:
 * @projectName: CustomView01
 * @date: 2016-06-12
 * @time: 10:26
 */
public class CustomView extends View {
    private static 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 Random mRandom = new Random();
    /**
     * 初始化可变字符串
     */
    private StringBuffer sb = new StringBuffer();
    /**
     * 文本
     */
    private String mText;
    /**
     * 文本的颜色
     */
    private int mTextColor;
    /**
     * 文本的大小
     */
    private int mTextSize;
    /**
     * 文本的背景颜色
     */
    private int mBgCplor;
    private Rect mBound;
    private Paint mPaint;

    public CustomView(Context context) {
        this(context, null);
    }

    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        /**
         * 获得我们所定义的自定义样式属性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);
        for (int i = 0; i < a.getIndexCount(); i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.CustomView_text:
                    mText = a.getString(attr);
                    break;
                case R.styleable.CustomView_textColor:
                    // 默认文本颜色设置为黑色
                    mTextColor = a.getColor(R.styleable.CustomView_textColor, Color.BLACK);
                    break;
                case R.styleable.CustomView_bgColor:
                    // 默认文本背景颜色设置为蓝色
                    mBgCplor = a.getColor(R.styleable.CustomView_bgColor, Color.BLUE);
                    break;
                case R.styleable.CustomView_textSize:
                    // 默认设置为16sp,TypeValue也可以把sp转化为px
                    mTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
            }
        }
        a.recycle();
        // 获得绘制文本的宽和高
        mPaint = new Paint();
        mPaint.setTextSize(mTextSize);

        mBound = new Rect();
        mPaint.getTextBounds(mText, 0, mText.length(), mBound);

        this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mText = createCode();
                mTextColor = randomColor();
                mBgCplor = randomColor();
                //View重新调用一次draw过程,以起到界面刷新的作用
                postInvalidate();
            }
        });
    }

    /**
     * 生成验证码
     */
    public String createCode() {
        sb.delete(0, sb.length()); // 使用之前首先清空内容
        for (int i = 0; i < 6; i++) {
            sb.append(CHARS[mRandom.nextInt(CHARS.length)]);
        }
        Log.e("生成验证码", sb.toString());
        return sb.toString();
    }

    /**
     * 随机颜色
     */
    private int randomColor() {
        sb.delete(0, sb.length()); // 使用之前首先清空内容
        String haxString;
        for (int i = 0; i < 3; i++) {
            haxString = Integer.toHexString(mRandom.nextInt(0xFF));
            if (haxString.length() == 1) {
                haxString = "0" + haxString;
            }
            sb.append(haxString);
        }
        Log.e("随机颜色", "#" + sb.toString());
        return Color.parseColor("#" + sb.toString());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heighMode = MeasureSpec.getMode(heightMeasureSpec);
        int heighSize = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : getPaddingLeft() + getPaddingRight() + mBound.width(), heighMode == MeasureSpec.EXACTLY ? heighSize : getPaddingTop() + getPaddingBottom() + mBound.height());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(mBgCplor);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);

        mPaint.setColor(mTextColor);
        canvas.drawText(mText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
    }
}

我们添加了一个点击事件,每一次点击View我都让它把生成的验证码和字体颜色以及字体背景颜色打印出来,如下所示:

有什么疑问的,请在下面留言,有不足的还望指导,感谢各位^_^

时间: 2024-10-07 19:56:32

Android 自定义View之随机生成图片验证码的相关文章

Android自定义View探索(一)—生命周期

Activity代码: public class FiveActivity extends AppCompatActivity { private MyView myView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e("log", "Activity生命周期:onCreate"); setConte

Android 自定义View视图

创建全新的视图将满足我们独特的UI需求. 本文介绍在指南针开发中会用到的罗盘的界面UI,通过继承View类实现的自定义视图,以此来深刻了解自定义视图. 实现效果图: 源代码: 布局文件activity_main(其中CompassView继承View类): <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.

android自定义View (一)MeasureSpec

A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement for either the width or the height. A MeasureSpec is comprised of a size and a mode. There are three possible modes: UNSPECIFIED

(转)[原] Android 自定义View 密码框 例子

遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 样子 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="border

Android自定义View——圆形进度条式按钮

介绍 今天上班的时候有个哥们问我怎么去实现一个按钮式的进度条,先来看看他需要实现的效果图. 和普通的圆形进度条类似,只是中间的地方有两个状态表示,未开始,暂停状态.而且他说圆形进度的功能已经实现了.那么我们只需要对中间的两个状态做处理就行了. 先来看看实现的效果图: 上面说了我们只需要处理中间状态的变化就可以了,对于进度的处理直接使用了弘洋文章中实现: http://blog.csdn.net/lmj623565791/article/details/43371299 下面开始具体实现. 具体实

android 自定义View【2】对话框取色&amp;色盘取色的实现

android 自定义View[2]对话框取色&色盘取色的实现    上一篇文章基本介绍了android自定义view的流程:继承view,复写view的一些方法.实现简单的自定义view.这篇文章主要介绍的是系统对话框取色功能,然后顺便介绍升级版,色盘取色[类似于ps中的吸管,对图片点击相应位置,获取那个位置的颜色]. 一.概述:通过该例子了解以下内容: 1.进一步了解android 自定义view. 2.知道如何获取图片上的颜色值. 3.监听屏幕touch,实现移动的时候自动取色.[onDr

Android自定义View(二、深入解析自定义属性)

转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51468648 本文出自:[openXu的博客] 目录: 为什么要自定义属性 怎样自定义属性 属性值的类型format 类中获取属性值 Attributeset和TypedArray以及declare-styleable ??在上一篇博客<Android自定义View(一.初体验)>中我们体验了自定义控件的基本流程: 继承View,覆盖构造方法 自定义属性 重写onMeasure方法测量宽

Android 自定义 view(三)&mdash;&mdash; onDraw

前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自定义view的两个比较重要的方法 onDraw(Canvas canvas) ,在探究 onDraw方法之前,我们必须先深入了解两个类Paint和Canvas .   第一:认识Paint 在探究onDraw之前首先必须要认识两个类,这里给出非常不错的两个资料参考网站,我也是从这里得到想要知道的东西,简单的说

[原] Android 自定义View 密码框 例子

遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 样子 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="border