自定义控件 验证码【详解】

完整步骤



自定义View的步骤:
1、在res/values中自定义View的属性2、在构造方法中遍历我们自定义的属性,并根据这些属性值对成员变量初始化3、重写onMesure,测量view的宽高,view视图大小的将在这里最终确定,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并最终通过setMeasuredDimension(width, height)保存计算结果。4、重写onLayout(),确定view的位置。该函数主要是为ViewGroup类型布局子视图用的,自定义View时可以不重写此方法。5、重写onDraw,绘制view的内容,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。对于ViewGroup则不需要实现该函数,因为容器本身是“没有内容“的,其包含的子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是dispatchDraw()方法。6、添加事件监听、回调方法

自定义属性支持的类型



reference:参考某一资源ID,   = "@drawable/图片ID"
color:颜色值,                           = "#00FF00"(不能引用colors中定义的值,若要引用请使用reference)

boolean:布尔值,                     = "true"

dimension:尺寸值,                 = "42dp"

float:浮点值,                            = "0.7"

integer:整型值,                       = "100"

string:字符串,                         = "string"

fraction:百分数,                      = "200%"

enum:枚举值,    <attr name="orientation"> <enum name="vertical" value="1" />,        = "vertical"

flag:位"或",        <attr name="windowSoftInputMode"> <flag name = "stateUnspecified" value= "0" /> <flag name = "stateUnchanged" value= "1" />,        = "stateUnspecified | stateUnchanged">

属性定义时可以指定多种类型值,    <attr name = "background" format= "reference | color" />,        = "@drawable/图片ID 或 #00FF00"

代码中获取设置的值:   

重写onMeasure及三种测量模式


onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法

1、onMeasure()函数由包含这个View的具体的ViewGroup调用,因此两个参数widthMeasureSpec, heightMeasureSpec也是从这个ViewGroup中传入的。

2、这两个参数是由ViewGroup中的layout_width、layout_height、padding,以及View自身的layout_margin、layout_width、layout_height、padding等共同决定的。另外,权值weight也是需要考虑的因素。

3、这两个值的作用

        如对于heightMeasureSpec,这个值由高32位和低16位组成,高32位保存的值叫specMode,可以通过MeasureSpec.getMode()获取;低16位为specSize,可以由MeasureSpec.getSize()获取。

        那么specMode和specSize的作用又是什么呢?要想知道这一点,我们需要知道代码中的最后一行,所有的View的onMeasure()的最后一行都会调用setMeasureDimension()函数,它的的作用是:根据传进去的值确定View最终的视图大小。也就是说onMeasure()中之前所作的所有工作都是为了最后这一句话服务的。

MeasureSpec.specMode的三种类型: 在ViewGroup中,给View分配的空间大小并不是确定的,有可能随着具体的变化而变化,而这个变化的条件就是传到specMode中决定的 1、EXACTLY(精确):指设置了明确的值(如android:layout_width="100dp"),其中包括MATCH_PARENT(因为这时值也是确定的)。这种模式下,由于我们已明确设置了view的宽度和高度,所以系统帮我们测量的结果就是我们要设置的结果,所以我们可以直接使用系统测量的值。 2、AT_MOST(最大):表示子布局限制在一个最大值内,而在此范围内的具体大小并不确定,一般为WARP_CONTENT。在这种模式下,由于我们没有明确设置view的宽度和高度,具体的大小是可能变化的,所以系统帮我们测量的结果只是我们能设置的结果的最大允许值,所以我们不能直接使用系统测量的值,而要自己根据自己的需要手动设置大小。 3、UNSPECIFIED:表示子布局想要多大就多大,基本使用不到。
注意:RelativeLayout 是一个比较复杂的 ViewGroup,其中子 view 的大小不仅跟 layout_width、layout_height 属性相关,还和很多其他属性有关系(如align、toRight等),若自定义的view重写了onMeasure方法,但并没有处理这些情况下对View大小的影响,则在RelativeLayout中使用时可能会出现一些尺寸和我们预期的不一致的问题

【View】


public class MyTitleView extends View implements OnClickListener {

    /**初始文本内容,在定义时赋初值,当在布局中没有定义时就是用默认值。注意:由于字体大小可能不是相等的,而这里初始化的值决定了文本框的大小,所以这里的初始值应该设置占用空间比较大的字符。这里以后可以再优化*/

    private String mTitleText = "8888";

    /**文本的颜色*/

    private int mTitleTextColor = Color.RED;

    /**文本字体大小*/

    private int mTitleTextSize;

    /**背景色*/

    private int mTitleBackgroundColor = Color.YELLOW;

    /**圆角大小*/

    private int mTitleRoundSize;

    /**绘制时文本绘制的范围*/

    private Rect mRect;

    private Paint mPaint;//画笔

    private Context mContext;

    //在【代码】里面创建对象的时候使用此构造方法

    public MyTitleView(Context context) {

        this(context, null);

    }

    //在【布局】文件中使用时,系统默认会调用两个参数的构造方法

    public MyTitleView(Context context, AttributeSet attrs) {

        this(context, attrs, 0);

    }

    public MyTitleView(Context context, AttributeSet attrs, int defStyle) {

        super(context, attrs, defStyle);

        mContext = context;

        initAttrs(context, attrs, defStyle);

        initRect();

        mTitleRoundSize = dp2px(5);

        setOnClickListener(this);

    }

    //初始化属性集

    private void initAttrs(Context context, AttributeSet attrs, int defStyle) {

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTitleView, defStyle, 0);//获取我们在【属性集】中【定义】的自定义属性的集合

        int count = typedArray.getIndexCount();//获取我们在【布局】文件中【设置】的自定义属性的个数,若没有设置任何自定义的属性,则此值为0

        for (int i = 0; i < count; i++) {

            int attrIndex = typedArray.getIndex(i);//获取此属性的编号,此值是由R文件自动生成的,代表【属性集】中定义的属性的位置,第一个属性对应的编号为0

            switch (attrIndex) {

            case R.styleable.MyTitleView_titleText://=0

                mTitleText = typedArray.getString(attrIndex);// 获取【布局】文件中设置的值

                break;

            case R.styleable.MyTitleView_titleTextColor://=1

                mTitleTextColor = typedArray.getColor(attrIndex, Color.RED);//defValue:Value to return if the attribute is not defined or not a resource 

                // 注意:布局中没设置此属性时代码根本执行不到这里,所以不能在此设置默认值,默认值建议直接在构造方法中初始化。

                break;

            case R.styleable.MyTitleView_titleBackground://=2

                mTitleBackgroundColor = typedArray.getColor(attrIndex, Color.YELLOW);

                break;

            case R.styleable.MyTitleView_titleTextSize://=3

                mTitleTextSize = typedArray.getDimensionPixelSize(attrIndex, 30);//能识别初代码中单位是sp(或dp)还是px

                break;

            }

        }

        typedArray.recycle();//Give back a previously retrieved array, for later re-use.

    }

    //获取文本需要占用的空间大小

    private void initRect() {

        mPaint = new Paint();

        //如果有自定义大小,就用自定义的,否则设置一个默认的。因为定义成员时Context还不存在,所以放在这里赋初值。

        if (mTitleTextSize == 0) mTitleTextSize = dp2px(30);

        mPaint.setTextSize(mTitleTextSize);//单位为px

        mRect = new Rect();

        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mRect);//将初始文本的边界值封装到矩形mRect中

    }

    @Override

    public void onClick(View v) {

        mTitleText = getRandomText();

        invalidate();//调用此方法主动刷新UI。If the view is visible, onDraw will be called at some point in the future。

    }

    @Override

    //测量view尺寸时的回调方法

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //设置宽度

        int width = 0;

        int width_size = MeasureSpec.getSize(widthMeasureSpec);//测量的值

        int width_mode = MeasureSpec.getMode(widthMeasureSpec);//测量模式

        switch (width_mode) {

        case MeasureSpec.EXACTLY:// 设置了具体的值或者是【MATCH_PARENT】时,可以直接使用测量的值

            width = getPaddingLeft() + getPaddingRight() + width_size;//Padding值+测量值

            break;

        case MeasureSpec.AT_MOST:// 当我们设置为【WARP_CONTENT】时,测量的值实际为【MATCH_PARENT】,不能使用,所以我们重新计算

            width = getPaddingLeft() + getPaddingRight() + mRect.width();//Padding值+文本实际占用空间

            break;

        }

        //设置高度

        int height = 0;

        int height_size = MeasureSpec.getSize(heightMeasureSpec);

        int height_mode = MeasureSpec.getMode(heightMeasureSpec);

        switch (height_mode) {

        case MeasureSpec.EXACTLY:

            height = getPaddingTop() + getPaddingBottom() + height_size;

            break;

        case MeasureSpec.AT_MOST:

            height = getPaddingTop() + getPaddingBottom() + mRect.height();

            break;

        }

        //设置控件实际大小

        setMeasuredDimension(width, height);

    }

    @Override

    //在onDraw中绘制【圆角矩形】的背景和随机生成的文字。只要图形稍有改变,此方法在就会调用

    protected void onDraw(Canvas canvas) {

        Log.i("bqt", "getMeasuredWidth()=" + getMeasuredWidth() + ",getMeasuredHeight()=" + getMeasuredHeight());//点击一次调用一次

        mPaint.setColor(mTitleBackgroundColor);

        canvas.drawRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), mTitleRoundSize, mTitleRoundSize, mPaint);//绘制背景

        mPaint.setColor(mTitleTextColor);

        canvas.drawText(mTitleText, getWidth() / 2 - mRect.width() / 2, getHeight() / 2 + mRect.height() / 2, mPaint);//居中绘制文字

    }

    /** 

      * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 

      */

    public int dp2px(float dpValue) {

        final float scale = mContext.getResources().getDisplayMetrics().density;

        return (int) (dpValue * scale + 0.5f);

    }

    /**

     * 获取一个四位数字的随机数

     */

    public String getRandomText() {

        Random random = new Random();

        Set<Integer> set = new HashSet<Integer>();

        while (set.size() < 4) {

            int randomInt = random.nextInt(10);

            set.add(randomInt);

        }

        StringBuffer sb = new StringBuffer();

        for (Integer i : set) {

            sb.append("" + i);

        }

        return sb.toString();

    }

}

使用

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:bqt="http://schemas.android.com/apk/res/com.bqt.myview"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:gravity="center_horizontal"

    android:orientation="vertical" >

    <com.bqt.myview.MyTitleView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_marginTop="10dp" />

    <com.bqt.myview.MyTitleView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_marginTop="10dp"

        android:paddingBottom="5dp"

        android:paddingLeft="10dp"

        android:paddingRight="10dp"

        android:paddingTop="5dp"

        bqt:titleBackground="#3f00"

        bqt:titleText="8888"

        bqt:titleTextColor="#0f0"

        bqt:titleTextSize="25sp" />

    <com.bqt.myview.MyTitleView

        android:layout_width="150dp"

        android:layout_height="50dp"

        android:layout_marginTop="10dp"

        bqt:titleBackground="#30f0"

        bqt:titleText="8888"

        bqt:titleTextColor="#00f"

        bqt:titleTextSize="35sp" />

    <com.bqt.myview.MyTitleView

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginTop="10dp"

        bqt:titleBackground="#300f"

        bqt:titleText="8888"

        bqt:titleTextColor="#f0f"

        bqt:titleTextSize="50sp" />

</LinearLayout>

自定义属性


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

<resources>

<!-- 注意,自定义属性定义好后顺序若变了必须clean一下工程好让R文件同步更新,否则运行时就会挂掉 -->

    <declare-styleable name="MyTitleView">

        <attr name="titleText" format="string" />

        <attr name="titleTextColor" format="color" />

        <attr name="titleBackground" format="color" />

        <attr name="titleTextSize" format="dimension" />

    </declare-styleable>

</resources>

来自为知笔记(Wiz)

时间: 2024-08-02 02:46:32

自定义控件 验证码【详解】的相关文章

Java 验证码详解

1 使用Servlet实现验证码,涉及的知识点主要为java 绘图技术与session保存数据. HTML页面 1 <html> 2 <image src='images/logo1.jpg' /><hr/> 3 <head><br/><title>登录</title> <br/><h1> 欢迎登录</h1></head> 4 <body> 5 <form

Android 自定义控件之第三讲:obtainStyledAttributes 系列函数详解

在项目中开发自定义控件时,或多或少都会用到 obtainStyledAttributes(AttributeSet, int[], int, int) 或者 obtainAttributes(AttributeSet, int[]) 函数,它们的主要作用是:根据传入的参数,返回一个对应的 TypedArray ,如果小伙伴还没有看过 LZ 的第二讲,那么请自行移步 Android 自定义控件之第二讲:TypedArray 详解,好了,就先扯到这里,下面开始今天内容讲解: 获取 TypedArra

【转】declare-styleable的使用(自定义控件) 以及declare-styleable中format详解

原文网址:http://www.cnblogs.com/622698abc/p/3348692.html declare-styleable是给自定义控件添加自定义属性用的 1.首先,先写attrs.xml <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="TestAttr"> <attr name=&q

attrs.xml中declare-styleable 详解(用于自定义控件的属性)

1. 框架定义: <declare-styleable name = "名称"> <attr name = "……" format = "……" /> </declare-styleable> 2. color:颜色值,指定这个属性必须输入的是颜色值 <attr name = "textColor" format = "color" /> 3. boolean

Android自定义控件系列八:详解onMeasure()(二)--利用onMeasure测量来实现图片拉伸永不变形,解决屏幕适配问题

上一篇文章详细讲解了一下onMeasure/measure方法在Android自定义控件时的原理和作用,参看博文:Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一),今天就来真正实践一下,让这两个方法大显神威来帮我们搞定图片的屏幕适配问题. 请尊重原创劳动成果,转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45038329,非允许请勿用于商业或盈利用途,违者必究. 使用ImageView会遇到

WebView使用详解(三)——WebChromeClient与LoadData补充

前言: 我不会忘了我 忘了我曾说过一定会得到的梦想 --<老大>小柯 相关文章 1.<WebView使用详解(一)--Native与JS相互调用(附JadX反编译)> 2.<WebView使用详解(二)--WebViewClient与常用事件监听> 一.WebChromeClient 1.概述 (1). 与WebViewClient的区别 很多同学一看到这里有Chrome,立马就会想到google 的Chrome浏览器:这里并不是指Chrome浏览器的意思,而是泛指浏览

jQuery 事件用法详解

jQuery 事件用法详解 目录 简介 实现原理 事件操作 绑定事件 解除事件 触发事件 事件委托 事件操作进阶 阻止默认事件 阻止事件传播 阻止事件向后执行 命名空间 自定义事件 事件队列 jquery中文文档 简介 jquery 之所以成为最受欢迎的前端库,很大一部分是得益于它的事件具有良好的语义,优秀的兼容性,并且便于管理和扩展. 在这里我会介绍 jquery 事件的一些比较基础的用法. 实现原理 jquery 事件脱胎于浏览器的 addEventListener (W3) 和 attac

Android基础入门教程——8.3.16 Canvas API详解(Part 1)

Android基础入门教程--8.3.16 Canvas API详解(Part 1) 标签(空格分隔): Android基础入门教程 本节引言: 前面我们花了13小节详细地讲解了Android中Paint类大部分常用的API,本节开始我们来讲解 Canvas(画板)的一些常用API,我们在Android基础入门教程--8.3.1 三个绘图工具类详解 中已经列出了我们可供调用的一些方法,我们分下类: drawXxx方法族:以一定的坐标值在当前画图区域画图,另外图层会叠加, 即后面绘画的图层会覆盖前

ASP.NET ViewState详解

ASP.NET ViewState详解[转载] asp.net存储textboxserializationstring服务器 作者:Infinities Loop 概述 ViewState是一个被误解很深的动物了.我希望通过此文章来澄清人们对ViewState的一些错误认识.为了达到这个目的,我决定从头到尾详细的描述一下整个ViewState的工作机制,其中我会同时用一些例子说明我文章中的观点,结论.比如我会用静态控件(declared controls)和动态控件(dynamic contro

Android-- Android事件机制之二:onTouch详解

Android事件机制之二:onTouch详解 在其中对OntouchEvent中的总结中,不是很具体.本文将主要对onTouch进行总结. onTouch是Android系统中整个事件机制的基础.Android中的其他事件,如onClick.onLongClick等都是以onTouch为基础的. onTouch包括从手指按下到离开手机屏幕的整个过程,在微观形式上,具体表现为action_down.action_move和action_up等过程. onTouch两种主要定义形式如下: (1)在