亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android自定義view制作絢麗的驗證碼

2020-01-02 07:02:08
字體:
來源:轉載
供稿:網友

廢話不多說了,先給大家展示下自定義view效果圖,如果大家覺得還不錯的話,請繼續往下閱讀。

這里寫圖片描述

怎么樣,這種驗證碼是不是很常見呢,下面我們就自己動手實現這種效果,自己動手,豐衣足食,哈哈~

一、 自定義view的步驟

自定義view一直被認為android進階通向高手的必經之路,其實自定義view好簡單,自定義view真正難的是如何繪制出高難度的圖形,這需要有好的數學功底(后悔沒有好好學數學了~),因為繪制圖形經常要計算坐標點及類似的幾何變換等等。自定義view通常只需要以下幾個步驟:

寫一個類繼承View類;

重新View的構造方法;

測量View的大小,也就是重寫onMeasure()方法;

重新onDraw()方法。

其中第三步不是必須的,只有當系統無法確定自定義的view的大小的時候需要我們自己重寫onMeasure()方法來完成自定義view大小的測量,因為如果用戶(程序員)在使用我們的自定義view的時候沒有指定其精確大?。▽挾然蚋叨龋?,如:布局文件中layout_width或layout_heigth屬性值為wrap_content而不是match_parent或某個精確的值,那么系統就不知道我們自定義view在onDraw()中繪制的圖形的大小,所以通常要讓我們自定義view支持wrap_content那么我們就必須重寫onMeasure方法來告訴系統我們要繪制的view的大?。▽挾群透叨龋?/p>

還有,如果我們自定義view需要一些特殊的屬性,那么我們還需要自定義屬性,這篇文章將會涉及到自定義屬性和上面的四個步驟的內容。

二、 自定義view的實現

要實現這種驗證碼控件,我們需要先分析一下它要怎么實現。通過看上面的效果圖,我們可以知道要實現這種效果,首先需要在繪制驗證碼字符串,即圖中的文本部分,然后繪制一些干擾點,再就是繪制干擾線了,分析完畢。下面我們根據分析結果一步步實現這種效果。

1. 繼承View,重寫構造方法

寫一個類繼承View,然后重新它的構造方法

/*** Created by lt on 2016/3/2.*/public class ValidationCode extends View{/*** 在java代碼中創建view的時候調用,即new* @param context*/public ValidationCode(Context context) {this(context,null);}/*** 在xml布局文件中使用view但沒有指定style的時候調用* @param context* @param attrs*/public ValidationCode(Context context, AttributeSet attrs) {this(context, attrs, 0);}/*** 在xml布局文件中使用view并指定style的時候調用* @param context* @param attrs* @param defStyleAttr*/public ValidationCode(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);// 做一些初始化工作init();}}

View有三個構造方法,一般的做法都是讓一個參數和兩個參數的構造方法調用三個構造參數的方法,這三個構造方法的調用情況看方法上面的注釋。在這個構造方法里面我們先做一些初始化隨機驗證碼字符串,畫筆等工作:

/*** 初始化一些數據*/private void init() {// 生成隨機數字和字母組合mCodeString = getCharAndNumr(mCodeCount);// 初始化文字畫筆mTextPaint = new Paint();mTextPaint.setStrokeWidth(3); // 畫筆大小為3mTextPaint.setTextSize(mTextSize); // 設置文字大小// 初始化干擾點畫筆mPointPaint = new Paint();mPointPaint.setStrokeWidth(6);mPointPaint.setStrokeCap(Paint.Cap.ROUND); // 設置斷點處為圓形// 初始化干擾線畫筆mPathPaint = new Paint();mPathPaint.setStrokeWidth(5);mPathPaint.setColor(Color.GRAY);mPathPaint.setStyle(Paint.Style.STROKE); // 設置畫筆為空心mPathPaint.setStrokeCap(Paint.Cap.ROUND); // 設置斷點處為圓形// 取得驗證碼字符串顯示的寬度值mTextWidth = mTextPaint.measureText(mCodeString);}

到這里,我們就完成了自定義View步驟中的前面的兩小步了,接下來就是完成第三步,即重寫onMeasure()進行我們自定義view大?。▽捀撸┑臏y量了:

2. 重寫onMeasure(),完成View大小的測量

/*** 要像layout_width和layout_height屬性支持wrap_content就必須重新這個方法* @param widthMeasureSpec* @param heightMeasureSpec*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 分別測量控件的寬度和高度,基本為模板方法int measureWidth = measureWidth(widthMeasureSpec);int measureHeight = measureHeight(heightMeasureSpec);// 其實這個方法最終會調用setMeasuredDimension(int measureWidth,int measureHeight);// 將測量出來的寬高設置進去完成測量setMeasuredDimension(measureWidth, measureHeight);}

測量寬度的方法:

/*** 測量寬度* @param widthMeasureSpec*/private int measureWidth(int widthMeasureSpec) {int result = (int) (mTextWidth*1.8f);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);if(widthMode == MeasureSpec.EXACTLY){// 精確測量模式,即布局文件中layout_width或layout_height一般為精確的值或match_parentresult = widthSize; // 既然是精確模式,那么直接返回測量的寬度即可}else{if(widthMode == MeasureSpec.AT_MOST) {// 最大值模式,即布局文件中layout_width或layout_height一般為wrap_contentresult = Math.min(result,widthSize);}}return result;}

測量高度的方法:

/*** 測量高度* @param heightMeasureSpec*/private int measureHeight(int heightMeasureSpec) {int result = (int) (mTextWidth/1.6f);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);if(heightMode == MeasureSpec.EXACTLY){// 精確測量模式,即布局文件中layout_width或layout_height一般為精確的值或match_parentresult = heightSize; // 既然是精確模式,那么直接返回測量的寬度即可}else{if(heightMode == MeasureSpec.AT_MOST) {// 最大值模式,即布局文件中layout_width或layout_height一般為wrap_contentresult = Math.min(result,heightSize);}}return result;}

說明:其實onMeasure()方法最終會調用setMeasuredDimension(int measureWidth,int measureHeight);將測量出來的寬高設置進去完成測量,而我們要做的就是測量得到寬度和高度的值,測量寬度和高度的方法最重要的就是得到當用戶(程序員)沒有給我們的控件指定精確的值(具體數值或match_parent)時合適的寬度和高度,所以,以上測量寬度和高度的方法基本上是一個模板方法,要做的就是得到result的一個合適的值,這里我們無需關注給result的那個值,因為這個值根據控件算出來的一個合適的值(也許不是很合適)。

完成了控件的測量,那么接下來我們還要完成控件的繪制這一大步,也就是自定義view的核心的一步重寫onDraw()方法繪制圖形。

3. 重寫onDraw(),繪制圖形

根據我們上面的分析,我們需要繪制驗證碼文本字符串,干擾點,干擾線。由于干擾點和干擾線需要坐標和路徑來繪制, 所以在繪制之前先做一些初始化隨機干擾點坐標和干擾線路徑:

private void initData() {// 獲取控件的寬和高,此時已經測量完成mHeight = getHeight();mWidth = getWidth();mPoints.clear();// 生成干擾點坐標for(int i=0;i<150;i++){PointF pointF = new PointF(mRandom.nextInt(mWidth)+10,mRandom.nextInt(mHeight)+10);mPoints.add(pointF);}mPaths.clear();// 生成干擾線坐標for(int i=0;i<2;i++){Path path = new Path();int startX = mRandom.nextInt(mWidth/3)+10;int startY = mRandom.nextInt(mHeight/3)+10;int endX = mRandom.nextInt(mWidth/2)+mWidth/2-10;int endY = mRandom.nextInt(mHeight/2)+mHeight/2-10;path.moveTo(startX,startY);path.quadTo(Math.abs(endX-startX)/2,Math.abs(endY-startY)/2,endX,endY);mPaths.add(path);}}

有了這些數據之后,我們可以開始繪制圖形了。

(1)繪制驗證碼文本字符串

由于驗證碼文本字符串是隨機生成的,所以我們需要利用代碼來隨機生成這種隨機驗證碼:

/*** java生成隨機數字和字母組合* @param length[生成隨機數的長度]* @return*/public static String getCharAndNumr(int length) {String val = "";Random random = new Random();for (int i = 0; i < length; i++) {// 輸出字母還是數字String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";// 字符串if ("char".equalsIgnoreCase(charOrNum)) {// 取得大寫字母還是小寫字母int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;val += (char) (choice + random.nextInt(26));} else if ("num".equalsIgnoreCase(charOrNum)) { // 數字val += String.valueOf(random.nextInt(10));}}return val;}

這種代碼是java基礎,相信大家都看得懂,看不懂也沒關系,這種代碼網上隨便一搜就有,其實我也是直接從網上搜的,嘿嘿~。

android的2D圖形api canvas提供了drawXXX()方法來完成各種圖形的繪制,其中就有drawText()方法來繪制文本,同時還有drawPosText()在給定的坐標點上繪制文本,drawTextOnPath()在給定途徑上繪制圖形。仔細觀察上面的效果圖,發現文本有的不是水平的,即有的被傾斜了,這就可以給我們的驗證碼提升一定的識別難度,要實現文字傾斜效果,我們可以通過drawTextOnPath()在給定路徑繪制文本達到傾斜效果,然而這種方法實現比較困難(坐標點和路徑難以計算),所以,我們可以通過canvas提供的位置變換方法rorate()結合drawText()實現文本傾斜效果。

int length = mCodeString.length();float charLength = mTextWidth/length;for(int i=1;i<=length;i++){int offsetDegree = mRandom.nextInt(15);// 這里只會產生0和1,如果是1那么正旋轉正角度,否則旋轉負角度offsetDegree = mRandom.nextInt(2) == 1?offsetDegree:-offsetDegree;canvas.save();canvas.rotate(offsetDegree, mWidth / 2, mHeight / 2);// 給畫筆設置隨機顏色,+20是為了去除一些邊界值mTextPaint.setARGB(255, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20);canvas.drawText(String.valueOf(mCodeString.charAt(i - 1)), (i-1) * charLength * 1.6f+30, mHeight * 2 / 3f, mTextPaint);canvas.restore();}

這段代碼通過for循環分別繪制驗證碼字符串中的每個字符,每繪制一個字符都將畫布旋轉一個隨機的正負角度,然后通過drawText()方法繪制字符,每個字符的繪制起點坐標根據字符的長度和位置不同而不同,這個自己計算,這里也許也不是很合適。要注意的是,每次對畫布canvas進行位置變換的時候都要先調用canvas.save()方法保存好之前繪制的圖形,繪制結束后調用canvas.restore()恢復畫布的位置,以便下次繪制圖形的時候不會由于之前畫布的位置變化而受影響。

(2)繪制干擾點

// 產生干擾效果1 -- 干擾點for(PointF pointF : mPoints){mPointPaint.setARGB(255,mRandom.nextInt(200)+20,mRandom.nextInt(200)+20,mRandom.nextInt(200)+20);canvas.drawPoint(pointF.x,pointF.y,mPointPaint);}

給干擾點畫筆設置隨機顏色,然后根據隨機產生的點的坐標利用canvas.drawPoint()繪制點。

(3)繪制干擾線

// 產生干擾效果2 -- 干擾線for(Path path : mPaths){mPathPaint.setARGB(255, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20);canvas.drawPath(path, mPathPaint);}

給干擾線畫筆設置隨機顏色,然后根據隨機產生路徑利用canvas.drawPath()繪制貝塞爾曲線,從而繪制出干擾線。

4. 重寫onTouchEvent,定制View事件

這里做這一步是為了實現當我們點擊我們的自定義View的時候,完成一些操作,即定制View事件。這里,我們需要當用戶點擊驗證碼控件的時候,改變驗證碼的文本字符串。

@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:// 重新生成隨機數字和字母組合mCodeString = getCharAndNumr(mCodeCount);invalidate();break;default:break;}return super.onTouchEvent(event);}

OK,到這里我們的這個自定義View就基本完成了,可能大家會問,這個自定義View是不是擴展性太差了,定制性太低了,說好的自定義屬性呢?跑哪里去了。不要急,下面我們就來自定義我們自己View的屬性,自定義屬性。

5. 自定義屬性,提高自定義View的可定制性

(1)在資源文件attrs.xml文件中定義我們的屬性(集)

<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="IndentifyingCode"><attr name="codeCount" format="integer|reference"></attr><attr name="textSize" format="dimension"></attr></declare-styleable></resources>

說明:

在attrs.xml文件中的attr節點中定義我們的屬性,定義屬性需要name屬性表示我們的屬性值,同時需要format屬性表示屬性值的格式,其格式有很多種,如果屬性值可以使多種格式,那么格式間用”|”分開;

declare-styleable節點用來定義我們自定義屬性集,其name屬性指定了該屬性集的名稱,可以任意,但一般為自定義控件的名稱;

如果屬性已經定義了(如layout_width),那么可以直接引用該屬性,不要指定格式了。

(2)在布局文件中引用自定義屬性,注意需要引入命名空間

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:lt="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><com.lt.identifyingcode.ValidationCodeandroid:id="@+id/validationCode"android:layout_width="wrap_content"android:layout_centerInParent="true"lt:textSize="25sp"android:background="@android:color/darker_gray"android:layout_height="wrap_content"/></RelativeLayout>

引入命名空間在現在只需要添加xmlns:lt="http://schemas.android.com/apk/res-auto"即可(lt換成你自己的命名空間名稱),而在以前引入命名空間方式為xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01",res后面的包路徑指的是項目的package`

(3)在構造方法中獲取自定義屬性的值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IndentifyingCode);mCodeCount = typedArray.getInteger(R.styleable.IndentifyingCode_codeCount, 5); // 獲取布局中驗證碼位數屬性值,默認為5個// 獲取布局中驗證碼文字的大小,默認為20spmTextSize = typedArray.getDimension(R.styleable.IndentifyingCode_textSize, typedArray.getDimensionPixelSize(R.styleable.IndentifyingCode_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics())));// 一個好的習慣是用完資源要記得回收,就想打開數據庫和IO流用完后要記得關閉一樣typedArray.recycle();

OK,自定義屬性也完成了,值也獲取到了,那么我們只需要將定制的屬性值在我們onDraw()繪制的時候使用到就行了,自定義屬性就是這么簡單~,看到這里,也許有點混亂了,看一下完整代碼整理一下。

package com.lt.identifyingcode;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PointF;import android.util.AttributeSet;import android.util.TypedValue;import android.view.MotionEvent;import android.view.View;import java.util.ArrayList;import java.util.Random;/*** Created by lt on 2016/3/2.*/public class ValidationCode extends View{/*** 控件的寬度*/private int mWidth;/*** 控件的高度*/private int mHeight;/*** 驗證碼文本畫筆*/private Paint mTextPaint; // 文本畫筆/*** 干擾點坐標的集合*/private ArrayList<PointF> mPoints = new ArrayList<PointF>();private Random mRandom = new Random();;/*** 干擾點畫筆*/private Paint mPointPaint;/*** 繪制貝塞爾曲線的路徑集合*/private ArrayList<Path> mPaths = new ArrayList<Path>();/*** 干擾線畫筆*/private Paint mPathPaint;/*** 驗證碼字符串*/private String mCodeString;/*** 驗證碼的位數*/private int mCodeCount;/*** 驗證碼字符的大小*/private float mTextSize;/*** 驗證碼字符串的顯示寬度*/private float mTextWidth;/*** 在java代碼中創建view的時候調用,即new* @param context*/public ValidationCode(Context context) {this(context,null);}/*** 在xml布局文件中使用view但沒有指定style的時候調用* @param context* @param attrs*/public ValidationCode(Context context, AttributeSet attrs) {this(context, attrs, 0);}/*** 在xml布局文件中使用view并指定style的時候調用* @param context* @param attrs* @param defStyleAttr*/public ValidationCode(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);getAttrValues(context, attrs);// 做一些初始化工作init();}/*** 獲取布局文件中的值* @param context*/private void getAttrValues(Context context,AttributeSet attrs) {TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IndentifyingCode);mCodeCount = typedArray.getInteger(R.styleable.IndentifyingCode_codeCount, 5); // 獲取布局中驗證碼位數屬性值,默認為5個// 獲取布局中驗證碼文字的大小,默認為20spmTextSize = typedArray.getDimension(R.styleable.IndentifyingCode_textSize, typedArray.getDimensionPixelSize(R.styleable.IndentifyingCode_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics())));// 一個好的習慣是用完資源要記得回收,就想打開數據庫和IO流用完后要記得關閉一樣typedArray.recycle();}/*** 要像layout_width和layout_height屬性支持wrap_content就必須重新這個方法* @param widthMeasureSpec* @param heightMeasureSpec*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 分別測量控件的寬度和高度,基本為模板方法int measureWidth = measureWidth(widthMeasureSpec);int measureHeight = measureHeight(heightMeasureSpec);// 其實這個方法最終會調用setMeasuredDimension(int measureWidth,int measureHeight);// 將測量出來的寬高設置進去完成測量setMeasuredDimension(measureWidth, measureHeight);}@Overrideprotected void onDraw(Canvas canvas) {// 初始化數據initData();int length = mCodeString.length();float charLength = mTextWidth/length;for(int i=1;i<=length;i++){int offsetDegree = mRandom.nextInt(15);// 這里只會產生0和1,如果是1那么正旋轉正角度,否則旋轉負角度offsetDegree = mRandom.nextInt(2) == 1?offsetDegree:-offsetDegree;canvas.save();canvas.rotate(offsetDegree, mWidth / 2, mHeight / 2);// 給畫筆設置隨機顏色mTextPaint.setARGB(255, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20);canvas.drawText(String.valueOf(mCodeString.charAt(i - 1)), (i-1) * charLength * 1.6f+30, mHeight * 2 / 3f, mTextPaint);canvas.restore();}// 產生干擾效果1 -- 干擾點for(PointF pointF : mPoints){mPointPaint.setARGB(255,mRandom.nextInt(200)+20,mRandom.nextInt(200)+20,mRandom.nextInt(200)+20);canvas.drawPoint(pointF.x,pointF.y,mPointPaint);}// 產生干擾效果2 -- 干擾線for(Path path : mPaths){mPathPaint.setARGB(255, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20, mRandom.nextInt(200) + 20);canvas.drawPath(path, mPathPaint);}}private void initData() {// 獲取控件的寬和高,此時已經測量完成mHeight = getHeight();mWidth = getWidth();mPoints.clear();// 生成干擾點坐標for(int i=0;i<150;i++){PointF pointF = new PointF(mRandom.nextInt(mWidth)+10,mRandom.nextInt(mHeight)+10);mPoints.add(pointF);}mPaths.clear();// 生成干擾線坐標for(int i=0;i<2;i++){Path path = new Path();int startX = mRandom.nextInt(mWidth/3)+10;int startY = mRandom.nextInt(mHeight/3)+10;int endX = mRandom.nextInt(mWidth/2)+mWidth/2-10;int endY = mRandom.nextInt(mHeight/2)+mHeight/2-10;path.moveTo(startX,startY);path.quadTo(Math.abs(endX-startX)/2,Math.abs(endY-startY)/2,endX,endY);mPaths.add(path);}}/*** 初始化一些數據*/private void init() {// 生成隨機數字和字母組合mCodeString = getCharAndNumr(mCodeCount);// 初始化文字畫筆mTextPaint = new Paint();mTextPaint.setStrokeWidth(3); // 畫筆大小為3mTextPaint.setTextSize(mTextSize); // 設置文字大小// 初始化干擾點畫筆mPointPaint = new Paint();mPointPaint.setStrokeWidth(6);mPointPaint.setStrokeCap(Paint.Cap.ROUND); // 設置斷點處為圓形// 初始化干擾線畫筆mPathPaint = new Paint();mPathPaint.setStrokeWidth(5);mPathPaint.setColor(Color.GRAY);mPathPaint.setStyle(Paint.Style.STROKE); // 設置畫筆為空心mPathPaint.setStrokeCap(Paint.Cap.ROUND); // 設置斷點處為圓形// 取得驗證碼字符串顯示的寬度值mTextWidth = mTextPaint.measureText(mCodeString);}/*** java生成隨機數字和字母組合* @param length[生成隨機數的長度]* @return*/public static String getCharAndNumr(int length) {String val = "";Random random = new Random();for (int i = 0; i < length; i++) {// 輸出字母還是數字String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";// 字符串if ("char".equalsIgnoreCase(charOrNum)) {// 取得大寫字母還是小寫字母int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;val += (char) (choice + random.nextInt(26));} else if ("num".equalsIgnoreCase(charOrNum)) { // 數字val += String.valueOf(random.nextInt(10));}}return val;}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:// 重新生成隨機數字和字母組合mCodeString = getCharAndNumr(mCodeCount);invalidate();break;default:break;}return super.onTouchEvent(event);}/*** 測量寬度* @param widthMeasureSpec*/private int measureWidth(int widthMeasureSpec) {int result = (int) (mTextWidth*1.8f);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);if(widthMode == MeasureSpec.EXACTLY){// 精確測量模式,即布局文件中layout_width或layout_height一般為精確的值或match_parentresult = widthSize; // 既然是精確模式,那么直接返回測量的寬度即可}else{if(widthMode == MeasureSpec.AT_MOST) {// 最大值模式,即布局文件中layout_width或layout_height一般為wrap_contentresult = Math.min(result,widthSize);}}return result;}/*** 測量高度* @param heightMeasureSpec*/private int measureHeight(int heightMeasureSpec) {int result = (int) (mTextWidth/1.6f);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);if(heightMode == MeasureSpec.EXACTLY){// 精確測量模式,即布局文件中layout_width或layout_height一般為精確的值或match_parentresult = heightSize; // 既然是精確模式,那么直接返回測量的寬度即可}else{if(heightMode == MeasureSpec.AT_MOST) {// 最大值模式,即布局文件中layout_width或layout_height一般為wrap_contentresult = Math.min(result,heightSize);}}return result;}/*** 獲取驗證碼字符串,進行匹配的時候只需要字符串比較即可(具體比較規則自己決定)* @return 驗證碼字符串*/public String getCodeString() {return mCodeString;}}

總結:這里與其說自定義View到不如說是繪制圖形,關鍵在于坐標點的計算,這里在計算坐標上也許不太好,以上是給大家分享Android自定義view制作絢麗的驗證碼,希望對大家有所幫助!大家有什么好的思路或者建議希望可以留言告訴我,感激不盡~。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品va在线播放我和闺蜜| 亚洲午夜av久久乱码| 日韩在线视频二区| 亚洲精品美女久久久久| 国产日韩欧美91| 亚洲日本成人女熟在线观看| 97国产真实伦对白精彩视频8| 日韩电影免费在线观看| 92看片淫黄大片欧美看国产片| 欧美高跟鞋交xxxxxhd| 国产精品自在线| 久久久久久国产精品美女| 中文字幕精品在线视频| 欧美高清视频一区二区| 日韩一区二区三区在线播放| 欧美孕妇孕交黑巨大网站| 欧美成人免费视频| 欧美黑人巨大精品一区二区| 久久精品国产一区二区电影| 久久久久久久色| 国产一区二区日韩| 日韩精品极品在线观看播放免费视频| 色综合久久久久久中文网| 亚洲成人a级网| 国产福利精品在线| 美女视频黄免费的亚洲男人天堂| 欧美中文字幕在线观看| 国产精品极品美女粉嫩高清在线| 日本久久亚洲电影| 久热爱精品视频线路一| 国产精品三级美女白浆呻吟| 国产一区二区三区高清在线观看| 精品一区精品二区| 亚洲欧美一区二区三区在线| 91亚洲国产精品| 在线日韩日本国产亚洲| 亚洲欧美日韩成人| 成人免费看黄网站| 国产欧美精品va在线观看| 日韩高清电影免费观看完整版| 国产精品成人免费电影| 久久久久久这里只有精品| 欧亚精品中文字幕| 欧美激情精品久久久久久免费印度| 久久精品国产精品| 亚洲一区二区福利| 国产精品视频公开费视频| 亚洲在线观看视频网站| 91极品女神在线| 菠萝蜜影院一区二区免费| 日韩av色在线| 国模精品视频一区二区三区| 欧美成人亚洲成人| 日韩精品在线免费| 亚洲成人精品久久久| 亚洲国产欧美精品| 亚洲午夜未满十八勿入免费观看全集| 大胆人体色综合| 亚洲二区在线播放视频| 亚洲另类欧美自拍| 国产91精品久久久久久久| 中文字幕久热精品视频在线| 人体精品一二三区| 精品国产一区久久久| 国产精品视频免费观看www| 亚洲成人国产精品| 国产美女精彩久久| 日韩av影院在线观看| 97精品一区二区三区| 久久亚洲一区二区三区四区五区高| 欧美在线视频一二三| 日韩女优人人人人射在线视频| 国产综合在线看| 欧美视频中文字幕在线| 26uuu另类亚洲欧美日本老年| 97热在线精品视频在线观看| 热re99久久精品国产66热| 岛国av一区二区在线在线观看| 成人高h视频在线| 日韩在线观看高清| 操人视频在线观看欧美| 国产一区二区三区视频| 欧美在线视频在线播放完整版免费观看| 精品国产自在精品国产浪潮| 精品精品国产国产自在线| 精品福利视频导航| 国产激情视频一区| 国产精品视频一| 久久久国产精品视频| 国产97免费视| 亚洲欧美在线免费观看| 国产噜噜噜噜久久久久久久久| 国产精品久久综合av爱欲tv| 久热精品视频在线观看| 国产亚洲欧美日韩美女| 日韩av电影国产| 欧美激情奇米色| 国产亚洲成精品久久| 亚洲2020天天堂在线观看| 欧美在线视频免费播放| 69久久夜色精品国产7777| 97在线免费观看视频| 日本成人激情视频| 性欧美xxxx交| 欧美性猛交xxxxx水多| 色婷婷亚洲mv天堂mv在影片| 日本精品免费观看| 国产又爽又黄的激情精品视频| 三级精品视频久久久久| 国产日韩欧美综合| 在线观看视频99| 777精品视频| 久久久999精品免费| www欧美xxxx| 大量国产精品视频| 国产精品一区二区av影院萌芽| 国产欧美韩国高清| 亚洲精品一区二区三区婷婷月| 欧美国产精品va在线观看| 亚洲成色777777在线观看影院| 亚洲无限乱码一二三四麻| 亚洲国产精品成人一区二区| 91在线看www| 亚洲91精品在线观看| 欧美国产日本在线| 黄色91在线观看| 国产a∨精品一区二区三区不卡| 色婷婷成人综合| 美日韩丰满少妇在线观看| 亚洲第一精品久久忘忧草社区| 国产成人a亚洲精品| 91精品91久久久久久| 国产精品视频久久久久| 奇门遁甲1982国语版免费观看高清| 亚洲人成啪啪网站| 97久久久久久| 成人久久一区二区三区| www.亚洲男人天堂| 国产成人免费av电影| 亚洲自拍中文字幕| 欧美另类极品videosbest最新版本| 成人国产亚洲精品a区天堂华泰| 一本色道久久综合狠狠躁篇的优点| 久久精品99国产精品酒店日本| 日韩国产在线看| 97视频免费观看| 黄网站色欧美视频| 美日韩丰满少妇在线观看| 亚洲成人av片在线观看| 亚洲第一区第一页| 亚洲欧美在线第一页| 97超级碰碰碰久久久| 77777亚洲午夜久久多人| 欧美最顶级丰满的aⅴ艳星| 久久人人爽人人爽爽久久| 久久精品国产欧美亚洲人人爽| 永久555www成人免费| 91中文精品字幕在线视频| 日韩av在线免费| 欧美电影《睫毛膏》| 青青久久av北条麻妃海外网| 国产精品一区二区三区毛片淫片| 日韩av综合网| 国产成人综合一区二区三区|