Android自定義View繪制隨機(jī)生成圖片驗(yàn)證碼
本篇文章講的是Android自定義View之隨機(jī)生成圖片驗(yàn)證碼,開(kāi)發(fā)中我們會(huì)經(jīng)常需要隨機(jī)生成圖片驗(yàn)證碼,但是這個(gè)是其次,主要還是想總結(jié)一些自定義View的開(kāi)發(fā)過(guò)程以及一些需要注意的地方。
按照慣例先看看效果圖:
一、先總結(jié)下自定義View的步驟:
1、自定義View的屬性
2、在View的構(gòu)造方法中獲得我們自定義的屬性
3、重寫(xiě)onMesure
4、重寫(xiě)onDraw
其中onMesure方法不一定要重寫(xiě),但大部分情況下還是需要重寫(xiě)的
二、View 的幾個(gè)構(gòu)造函數(shù)
1、public CustomView(Context context)
—>java代碼直接new一個(gè)CustomView實(shí)例的時(shí)候,會(huì)調(diào)用這個(gè)只有一個(gè)參數(shù)的構(gòu)造函數(shù);
2、public CustomView(Context context, AttributeSet attrs)
—>在默認(rèn)的XML布局文件中創(chuàng)建的時(shí)候調(diào)用這個(gè)有兩個(gè)參數(shù)的構(gòu)造函數(shù)。AttributeSet類(lèi)型的參數(shù)負(fù)責(zé)把XML布局文件中所自定義的屬性通過(guò)AttributeSet帶入到View內(nèi);
3、public CustomView(Context context, AttributeSet attrs, int defStyleAttr)
—>構(gòu)造函數(shù)中第三個(gè)參數(shù)是默認(rèn)的Style,這里的默認(rèn)的Style是指它在當(dāng)前Application或者Activity所用的Theme中的默認(rèn)Style,且只有在明確調(diào)用的時(shí)候才會(huì)調(diào)用
4、public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
—>該構(gòu)造函數(shù)是在API21的時(shí)候才添加上的
三、下面我們就開(kāi)始來(lái)看看代碼
1、自定義View的屬性,首先在res/values/ 下建立一個(gè)attr.xml , 在里面定義我們的需要用到的屬性以及聲明相對(duì)應(yīng)屬性的取值類(lèi)型
<?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個(gè)屬性,format是值該屬性的取值類(lèi)型,format取值類(lèi)型總共有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全類(lèi)名。
3、在View的構(gòu)造方法中,獲得我們的自定義的樣式
/** * 文本 */ 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: // 默認(rèn)文本顏色設(shè)置為黑色 mTextColor = a.getColor(R.styleable.CustomView_textColor, Color.BLACK); break; case R.styleable.CustomView_bgColor: // 默認(rèn)文本背景顏色設(shè)置為藍(lán)色 mBgCplor = a.getColor(R.styleable.CustomView_bgColor, Color.BLUE); break; case R.styleable.CustomView_textSize: // 默認(rèn)設(shè)置為16sp,TypeValue也可以把sp轉(zhuǎn)化為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); }
我們重寫(xiě)了3個(gè)構(gòu)造方法,在上面的構(gòu)造方法中說(shuō)過(guò)默認(rèn)的布局文件調(diào)用的是兩個(gè)參數(shù)的構(gòu)造方法,所以記得讓所有的構(gòu)造方法調(diào)用三個(gè)參數(shù)的構(gòu)造方法,然后在三個(gè)參數(shù)的構(gòu)造方法中獲得自定義屬性。
一開(kāi)始一個(gè)參數(shù)的構(gòu)造方法和兩個(gè)參數(shù)的構(gòu)造方法是這樣的:
public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); }
有一點(diǎn)要注意的是super應(yīng)該改成this,然后讓一個(gè)參數(shù)的構(gòu)造方法引用兩個(gè)參數(shù)的構(gòu)造方法,兩個(gè)參數(shù)的構(gòu)造方法引用三個(gè)參數(shù)的構(gòu)造方法,代碼如下:
public CustomView(Context context) { this(context, null); } public CustomView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
4、重寫(xiě)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方法開(kāi)始的,經(jīng)過(guò)measure、layout和draw三個(gè)過(guò)程才能最終將一個(gè)View繪制出來(lái),其中:
•測(cè)量——onMeasure():用來(lái)測(cè)量View的寬和高來(lái)決定View的大小
•布局——onLayout():用來(lái)確定View在父容器ViewGroup中的放置位置
•繪制——onDraw():負(fù)責(zé)將View繪制在屏幕上
來(lái)看下此時(shí)的效果圖
細(xì)心的朋友會(huì)發(fā)現(xiàn),在上面的布局文件中,我們是把寬和高設(shè)置為wrap_content的,可是這個(gè)效果圖怎么看都不是我們想要的,這是因?yàn)橄到y(tǒng)幫我們測(cè)量的高度和寬度默認(rèn)是MATCH_PARNET,當(dāng)我們?cè)O(shè)置明確的寬度和高度時(shí),系統(tǒng)幫我們測(cè)量的結(jié)果就是我們?cè)O(shè)置的結(jié)果,這個(gè)是對(duì)的。但是除了設(shè)置明確的寬度和高度,不管我們?cè)O(shè)置為WRAP_CONTENT還是MATCH_PARENT,系統(tǒng)幫我們測(cè)量的結(jié)果就是MATCH_PARENT,所以,當(dāng)我們?cè)O(shè)置了WRAP_CONTENT時(shí),我們需要自己進(jìn)行測(cè)量,也就是說(shuō)我們需要重寫(xiě)onMesure方法
下面是我們重寫(xiě)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(完全):一般是設(shè)置了明確的值或者是MATCH_PARENT,父元素決定了子元素的大小,子元素將被限定在給定的范圍里而忽略它本身大??;
(2)AT_MOST(至多):表示子元素至多達(dá)到給定的一個(gè)最大值,一般為WARP_CONTENT;
我們?cè)倏纯葱Ч麍D:
現(xiàn)在這個(gè)是我們想要的結(jié)果了吧,回歸到主題,今天講的是自定義View之隨機(jī)生成圖片驗(yàn)證碼,現(xiàn)在把自定義View的部分完成了,我把完整的代碼貼出來(lái)
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'}; /** * 初始化生成隨機(jī)數(shù)的類(lèi) */ 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: // 默認(rèn)文本顏色設(shè)置為黑色 mTextColor = a.getColor(R.styleable.CustomView_textColor, Color.BLACK); break; case R.styleable.CustomView_bgColor: // 默認(rèn)文本背景顏色設(shè)置為藍(lán)色 mBgCplor = a.getColor(R.styleable.CustomView_bgColor, Color.BLUE); break; case R.styleable.CustomView_textSize: // 默認(rèn)設(shè)置為16sp,TypeValue也可以把sp轉(zhuǎn)化為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重新調(diào)用一次draw過(guò)程,以起到界面刷新的作用 postInvalidate(); } }); } /** * 生成驗(yàn)證碼 */ public String createCode() { sb.delete(0, sb.length()); // 使用之前首先清空內(nèi)容 for (int i = 0; i < 6; i++) { sb.append(CHARS[mRandom.nextInt(CHARS.length)]); } Log.e("生成驗(yàn)證碼", sb.toString()); return sb.toString(); } /** * 隨機(jī)顏色 */ private int randomColor() { sb.delete(0, sb.length()); // 使用之前首先清空內(nèi)容 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("隨機(jī)顏色", "#" + 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); } }
我們添加了一個(gè)點(diǎn)擊事件,每一次點(diǎn)擊View我都讓它把生成的驗(yàn)證碼和字體顏色以及字體背景顏色打印出來(lái),如下所示:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android響應(yīng)事件onClick方法的五種實(shí)現(xiàn)方式小結(jié)
本篇文章主要介紹了Android響應(yīng)onClick方法的五種實(shí)現(xiàn)方式小結(jié),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03Android 啟動(dòng)第三方程序的方法總結(jié)
這篇文章主要介紹了Android 啟動(dòng)第三方程序的方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-04-04Android編程判斷手機(jī)上是否安裝了某個(gè)程序的方法
這篇文章主要介紹了Android編程判斷手機(jī)上是否安裝了某個(gè)程序的方法,涉及Android針對(duì)程序包的操作及進(jìn)程判斷的相關(guān)技巧,需要的朋友可以參考下2015-11-11Android編程中出現(xiàn)The connection to adb is down問(wèn)題的解決方法
這篇文章主要介紹了Android編程中出現(xiàn)The connection to adb is down問(wèn)題的解決方法,涉及Android進(jìn)程與服務(wù)的相關(guān)操作技巧,需要的朋友可以參考下2015-12-12直接可用的Android studio學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了直接可用的Android studio學(xué)生信息管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Android開(kāi)發(fā)實(shí)現(xiàn)AlertDialog中View的控件設(shè)置監(jiān)聽(tīng)功能分析
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)AlertDialog中View的控件設(shè)置監(jiān)聽(tīng)功能,結(jié)合實(shí)例形式分析了Android針對(duì)AlertDialog中的控件使用View進(jìn)行監(jiān)聽(tīng)的相關(guān)操作技巧,需要的朋友可以參考下2017-11-11詳解Android Studio正式簽名進(jìn)行調(diào)試的實(shí)現(xiàn)步驟
這篇文章主要介紹了詳解Android Studio正式簽名進(jìn)行調(diào)試的實(shí)現(xiàn)步驟的相關(guān)資料,需要的朋友可以參考下2017-07-07詳解android特性之CoordinatorLayout用法探析實(shí)例
本篇文章主要介紹了android特性之CoordinatorLayout用法探析實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02Android開(kāi)發(fā)之獲取單選與復(fù)選框的值操作示例
這篇文章主要介紹了Android開(kāi)發(fā)之獲取單選與復(fù)選框的值操作,結(jié)合實(shí)例形式分析了Android針對(duì)單選按鈕、復(fù)選框的事件響應(yīng)、數(shù)值獲取等相關(guān)操作技巧,需要的朋友可以參考下2019-04-04