Android自定义View 做个简单的验证码控件

来源:互联网 发布:江苏省网络作家协会 编辑:程序博客网 时间:2024/06/05 20:58

首先感谢鸿洋大神,因为我看了他的文章后才写了这篇博文。
传送门:http://blog.csdn.net/lmj623565791/article/details/24252901

从15年工作到现在两年多了,从一个什么都不会的小菜鸟到现在会写点小程序的老菜鸟。两年来,做了十来个项目,有大有小,有团队开发也有单独开发,虽然比刚入职有了进步,但是总体来说总是差强人意,重复的用轮子,让自己的代码千篇一律毫无亮点。

两年来虽然自己不是完全没有写过自定义控件,但是也是极少写的,一直都感觉自定义控件十分复杂,写起来也比较费脑子(本人懒癌晚期患者),能不写就不写,随便百度一个相似控件,改改能用就行,不考虑是否与界面一致,色值是否协调,这也让我在开发中吃了不少亏,界面不协调改,色值相差大改,虽然在使用轮子中减少了工作量,但是在后续修改中也花费了不少工作时间,如果你是新人,千万不要学我。

虽然在开发界常说不要重复造轮子,但是呢这句话得这样看,不重复是对的,但是怎么造轮子这是门技术,这是需要我们学习的

第一步
自定义view的属性:
在你的项目/res/velues目录下新增attrs.xml,在这里你可以定义你需要定义的属性

<?xml version="1.0" encoding="utf-8"?><resources>    <!--设置参数 -->    <attr name="authCodeText" format="string"/><!--验证码显示值-->    <attr name="authCodeBackground" format="color"/><!--验证码背景-->    <attr name="authCodeTextSize" format="dimension"/><!--验证码位数-->    <!--声明-->    <declare-styleable name="AuthCodeUtils">        <attr name="authCodeText"/>        <attr name="authCodeBackground"/>        <attr name="authCodeTextSize"/>    </declare-styleable></resources>

第二步
获得自定义样式

写一个工具类并继承View(AuthCodeUtils),在构造方法中调用我们的样式

public class AuthCodeUtils extends View {    private String authCodeText;//文本    private int authCodeBackground;//背景颜色    private int authCodeSize;//验证码长度    private Rect mRect;//绘制矩形    private Paint mPaint;//    //构造方法    public AuthCodeUtils(Context context, AttributeSet attributeSet) {        super(context, attributeSet, 0);        initializeProperty(context, attributeSet);    }    /**     * 初始化属性     *     * @param context     * @param attributeSet     */    private void initializeProperty(Context context, AttributeSet attributeSet) {        TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.TestTextUtil);        //获取typedArray长度        int num = typedArray.getIndexCount();        for (int i = 0; i < num; i++) {            //获取属性ID            int id = typedArray.getIndex(i);            switch (id) {                case R.styleable.TestTextUtil_authCodeText:                    authCodeText = typedArray.getString(id);                    break;                case R.styleable.TestTextUtil_authCodeTextSize:                    authCodeSize = typedArray.getDimensionPixelSize(id, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));                    break;                case R.styleable.TestTextUtil_authCodeBackground:                    authCodeBackground = typedArray.getColor(id, Color.YELLOW);                    break;            }        }        typedArray.recycle();        //获取文本的长和宽        mPaint = new Paint();        mPaint.setTextSize(authCodeSize);        mRect = new Rect();        mPaint.getTextBounds(authCodeText, 0, authCodeText.length(), mRect);    }

然后重新onDraw方法

    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //添加背景色        mPaint.setColor(Color.YELLOW);        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);        //字体颜色        mPaint.setColor(authCodeBackground);        canvas.drawText(authCodeText, getWidth() / 2 - mRect.width() / 2, getHeight() / 2 + mRect.height() / 2, mPaint);    }

然后在布局文件中声明并调用

<?xml version="1.0" encoding="utf-8"?><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"><!--注意此句xmlns:custom="http://schemas.android.com/apk/res-auto",如果不写,则无法获得我们自定义的属性-->    <!--声明并调用-->    <cm.cui.publicbase.test.AuthCodeUtils        android:id="@+id/testText"        android:layout_width="70dp"        android:layout_height="30dp"        android:layout_centerInParent="true"        android:padding="10dp"        custom:authCodeBackground="@color/colorAccent"        custom:authCodeText="3712"        custom:authCodeTextSize="16sp"/></RelativeLayout>

运行程序,效果如图
这里写图片描述

第三步

重写onMeasure方法

这里是抄的鸿洋大神的代码,不过我加了注释和修改了一些地方,差不多意思就是这样

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        //获取控件宽高的显示模式        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        //获取宽高的尺寸值  固定值的宽度        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int width;        int height;//        MeasureSpec父布局传递给后代的布局要求 包含 确定大小和三种模式//        EXACTLY:一般是设置了明确的值或者是MATCH_PARENT//        AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT//        UNSPECIFIED:表示子布局想要多大就多大,很少使用        if (widthMode == MeasureSpec.EXACTLY) {            width = widthSize;        } else {            mPaint.setTextSize(authCodeSize);            mPaint.getTextBounds(authCodeText, 0, authCodeText.length(), mRect);            float textWidth = mRect.width();            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());            width = desired;        }        if (heightMode == MeasureSpec.EXACTLY) {            height = heightSize;        } else {            mPaint.setTextSize(authCodeSize);            mPaint.getTextBounds(authCodeText, 0, authCodeText.length(), mRect);            float textHeight = mRect.height();            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());            height = desired;        }        //设置测量的宽高        setMeasuredDimension(width, height);    }

编写噪点和划线方法,大意如下图

   //随机生成小圆点坐标    private int[] getPoint(int height, int width) {        int[] tempCheckNum = {0, 0};        tempCheckNum[0] = (int) (Math.random() * width);        tempCheckNum[1] = (int) (Math.random() * height);        return tempCheckNum;    }    //随机生成线线    public int[] getLine(int height, int width) {        int[] tempCheckNum = {0, 0, 0, 0};        for (int i = 0; i < 4; i += 2) {            tempCheckNum[i] = (int) (Math.random() * width);            tempCheckNum[i + 1] = (int) (Math.random() * height);        }        return tempCheckNum;    }

调用生成的噪点和划线方法

 final int height = getHeight();        final int width = getWidth();        // 绘制小圆点        int[] point;        for (int i = 0; i < 100; i++) {            point = getPoint(height, width);            /**             * drawCircle (float cx, float cy, float radius, Paint paint)             * float cx:圆心的x坐标。             * float cy:圆心的y坐标。             * float radius:圆的半径。             * Paint paint:绘制时所使用的画笔。             */            canvas.drawCircle(point[0], point[1], 1, mPaint);        }        for (int i = 0; i < 50; i++) {            point = getPoint(height, width);            canvas.drawCircle(point[0], point[1], 2, mPaint);        }        for (int i = 0; i < 30; i++) {            point = getPoint(height, width);            canvas.drawCircle(point[0], point[1], 3, mPaint);        }        int[] line;        for (int i = 0; i < 10; i++) {            line = getLine(height, width);            /**             * startX:起始端点的X坐标。             *startY:起始端点的Y坐标。             *stopX:终止端点的X坐标。             *stopY:终止端点的Y坐标。             *paint:绘制直线所使用的画笔。             */            canvas.drawLine(line[0], line[1], line[2], line[3], mPaint);        }

请自行添加点击事件……..

运行程序

这里写图片描述

Demo:代码地址,点我传送

原创粉丝点击