Android文字验证码

xiaoxiao2021-02-28  35

今天来写最后一部分,九宫格部分,先来看一下最终的效果图:


一、分析功能

直接看下边的九宫格,九宫格里边的文字随机,文字颜色随机,并且每个文字都进行了不同程度的扭曲变形,点击看不清的时候,可以对九宫格的内容进行重置,要解决的问题差不多就这么多.

(1)对于九宫格来说,安卓原生提供了GridView,使用它可以很轻松的完成该功能.  (2)对于文字处理,随机生成文字和文字颜色,这个很简单,在上一篇文章中我们封装了一个工具类,这个不用再去处理,直接拿来用就可以了,对于文字扭曲,找了一下画布的方法,好像并没有什么绘制文字的方法可以实现想要的效果,所以换种思路,对图片进行处理有没有方法达到想要的效果呢?查阅了一些资料,找到了一个方法drawBitmapMesh,可以实现对图片进行扭曲.好了,有了解决方案,就可以来愉快的coding了.


二、代码实现

这次先来解决一下图片扭曲的问题,然后再向下进行.  drawBitmapMesh这个方法大家可以参考一下大神的博客:  自定义控件其实很简单5/12  只需要看到大波妹(嘿嘿嘿~~)那部分就可以,咳咳,回归正题哈,这里我们把代码copy出来改动一下,代码如下:

public class HanZiCode extends ImageView { /** * 分割数 */ private static final int WIDTH = 9, HEIGHT = 9; /** * 焦点数 */ private static final int COUNT = (WIDTH + 1) * (HEIGHT + 1); /** * 位图对象 */ private Bitmap mBitmap; /** * 基准点坐标数组 */ private float[] matrixOriganal = new float[COUNT * 2]; /** * 变换后点坐标数组 */ private float[] matrixMoved = new float[COUNT * 2]; /** * 触摸屏幕时手指的xy坐标 */ private float clickX, clickY; /** * 基准点、变换点和线段的绘制Paint */ private Paint origPaint, movePaint, linePaint; /** * 画笔 */ private Paint mPaint; /** * 矩形区域 */ private Rect mBounds; /** * 文字 */ private String mText = "逗"; public HanZiView(Context context, AttributeSet set) { super(context, set); setFocusable(true); // 实例画笔并设置颜色 origPaint = new Paint(Paint.ANTI_ALIAS_FLAG); origPaint.setColor(0x660000FF); movePaint = new Paint(Paint.ANTI_ALIAS_FLAG); movePaint.setColor(0x99FF0000); linePaint = new Paint(Paint.ANTI_ALIAS_FLAG); linePaint.setColor(0xFFFFFB00); // 初始化坐标数组 int index = 0; for (int y = 0; y <= HEIGHT; y++) { float fy = mBitmap.getHeight() * y / HEIGHT; for (int x = 0; x <= WIDTH; x++) { float fx = mBitmap.getWidth() * x / WIDTH; setXY(matrixMoved, index, fx, fy); setXY(matrixOriganal, index, fx, fy); index += 1; } } mPaint = new Paint(); mPaint.setColor(Color.Red); mPaint.setTextSize(80); // 随机使用粗体 mPaint.setFakeBoldText(r.nextBoolean()); mBounds = new Rect(); mPaint.getTextBounds(mText, 0, mText.length(), mBounds); } /** * 设置坐标数组 * * @param array * 坐标数组 * @param index * 标识值 * @param x * x坐标 * @param y * y坐标 */ private void setXY(float[] array, int index, float x, float y) { array[index * 2 + 0] = x; array[index * 2 + 1] = y; } @Override protected void onDraw(Canvas canvas) { // 创建带有文字的图片 mBitmap = Bitmap.createBitmap(getWidth(), getHeight(),Bitmap.Config.ARGB_8888); Canvas c = new Canvas(mBitmap); c.drawText(mText, getWidth() / 2 - mBounds.width() / 2, mBounds.height() / 2 + getHeight() / 2, mPaint); // 绘制网格位图 canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, matrixMoved, 0, null, 0, null); // 绘制参考元素 drawGuide(canvas); } /** * 绘制参考元素 * * @param canvas * 画布 */ private void drawGuide(Canvas canvas) { for (int i = 0; i < COUNT * 2; i += 2) { float x = matrixOriganal[i + 0]; float y = matrixOriganal[i + 1]; canvas.drawCircle(x, y, 4, origPaint); float x1 = matrixOriganal[i + 0]; float y1 = matrixOriganal[i + 1]; float x2 = matrixMoved[i + 0]; float y2 = matrixMoved[i + 1]; canvas.drawLine(x1, y1, x2, y2, origPaint); } for (int i = 0; i < COUNT * 2; i += 2) { float x = matrixMoved[i + 0]; float y = matrixMoved[i + 1]; canvas.drawCircle(x, y, 4, movePaint); } canvas.drawCircle(clickX, clickY, 6, linePaint); } /** * 计算变换数组坐标 */ private void smudge() { for (int i = 0; i < COUNT * 2; i += 2) { float xOriginal = matrixOriganal[i + 0]; float yOriginal = matrixOriganal[i + 1]; float dist_click_to_origin_x = clickX - xOriginal; float dist_click_to_origin_y = clickY - yOriginal; float kv_kat = dist_click_to_origin_x * dist_click_to_origin_x + dist_click_to_origin_y * dist_click_to_origin_y; float pull = (float) (1000000 / kv_kat / Math.sqrt(kv_kat)); if (pull >= 1) { matrixMoved[i + 0] = clickX; matrixMoved[i + 1] = clickY; } else { matrixMoved[i + 0] = xOriginal + dist_click_to_origin_x * pull; matrixMoved[i + 1] = yOriginal + dist_click_to_origin_y * pull; } } } @Override public boolean onTouchEvent(MotionEvent event) { clickX = event.getX(); clickY = event.getY(); smudge(); invalidate(); return true; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167

然后在xml中使用一下:

xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:hz="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/ll_all" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" tools:context="com.example.junweiliu.hanzicode.MainActivity"> <com.example.junweiliu.hanzicode.HanZiView android:layout_width="100dp" android:layout_height="100dp"/> </LinearLayout> 1234567891011121314

看一下效果图:

很不错,达到了想要的效果,简单分析一下这段代码,这段代码其实就是通过点击的位置,去改变原始图片的各个原始点的坐标,然后使用drawBitmapMesh方法去实现对新的坐标的图像绘制,从而实现了图片的扭曲,这里重点看一下变换坐标的方法:

/** * 计算变换数组坐标 */ private void smudge() { for (int i = 0; i < COUNT * 2; i += 2) { float xOriginal = matrixOriganal[i + 0]; float yOriginal = matrixOriganal[i + 1]; float dist_click_to_origin_x = clickX - xOriginal; float dist_click_to_origin_y = clickY - yOriginal; float kv_kat = dist_click_to_origin_x * dist_click_to_origin_x + dist_click_to_origin_y * dist_click_to_origin_y; // 扭曲大小 float pull = (float) (1000000 / kv_kat / Math.sqrt(kv_kat)); if (pull >= 1) { matrixMoved[i + 0] = clickX; matrixMoved[i + 1] = clickY; } else { matrixMoved[i + 0] = xOriginal + dist_click_to_origin_x * pull; matrixMoved[i + 1] = yOriginal + dist_click_to_origin_y * pull; } } } 12345678910111213141516171819202122232425

可能算法会有很多,可以实现不同的效果,有兴趣的同学可以去研究一下,这里只看一下这部分,其实不难理解,这部分就是去生成新的扭曲坐标,我们找到一个关键变量pull,这个变量的大小决定了扭曲度的大小.所以可以适当修改pull的值,来调节扭曲度.最关键的问题解决了,接下来就很简单了.完整代码如下:

自定义属性attr:

<?xml version="1.0" encoding="utf-8"?> <resources> <!--HanZiView相关--> <!--左右宽度留白--> <attr name="borderWidthSize" format="dimension"/> <!--上下高度留白--> <attr name="borderHeightSize" format="dimension"/> <!--旋转的度数--> <attr name="rotateSize" format="integer"/> <!--扭曲的系数--> <attr name="smudgeCoefficieng" format="float"/> <!--文字的颜色--> <attr name="textColor" format="color"/> <!--文字大小--> <attr name="textSize" format="dimension"/> <declare-styleable name="HanZiView"> <attr name="borderWidthSize"/> <attr name="borderHeightSize"/> <attr name="rotateSize"/> <attr name="smudgeCoefficieng"/> <attr name="textColor"/> <attr name="textSize"/> </declare-styleable> </resources> 123456789101112131415161718192021222324

HanZiView:

package com.example.junweiliu.hanzicode; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.ImageView; import java.util.Random; /** * Created by junweiliu on 16/5/3. */ public class HanZiView extends ImageView { /** * 分割数 */ private static final int WIDTH = 9, HEIGHT = 9; /** * 焦点数 */ private static final int COUNT = (WIDTH + 1) * (HEIGHT + 1); /** * 位图对象 */ private Bitmap mBitmap; /** * 基准点坐标数组 */ private float[] matrixOriganal = new float[COUNT * 2]; /** * 变换后点坐标数组 */ private float[] matrixMoved = new float[COUNT * 2]; /** * 避免过多的重绘 */ private boolean needDraw = true; /** * 画笔 */ private Paint mPaint; /** * 文字大小 */ private int mTextSize; /** * 文字 */ private String mText = "逗"; /** * 文字颜色 */ private int mTextColor; /** * 矩形区域 */ private Rect mBounds; /** * 四周的留白宽度值 */ private int mBorderWidthSize; /** * 四周的留白高度值 */ private int mBorderHeightSize; /** * 获取扭曲点的随机位置 */ private float mRandomX, mRandomY; /** * 字体旋转的角度 */ private int DEFAULT_ROTATE_SIZE; /** * 扭曲的系数 */ private float SMUDGE_COEFFICIENT; /** * 旋转的角度 */ private int rotate; /** * 随机 */ private Random r = new Random(); public HanZiView(Context context) { this(context, null); } public HanZiView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public HanZiView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 实例画笔并设置颜色 origPaint = new Paint(Paint.ANTI_ALIAS_FLAG); origPaint.setColor(0x660000FF); movePaint = new Paint(Paint.ANTI_ALIAS_FLAG); movePaint.setColor(0x99FF0000); linePaint = new Paint(Paint.ANTI_ALIAS_FLAG); linePaint.setColor(0xFFFFFB00); // 获取自定义属性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HanZiView); mBorderWidthSize = ta.getDimensionPixelSize(R.styleable.HanZiView_borderWidthSize, DisplayUtil.dip2px(context, 30)); mBorderHeightSize = ta.getDimensionPixelSize(R.styleable.HanZiView_borderHeightSize, DisplayUtil.dip2px(context, 20)); DEFAULT_ROTATE_SIZE = ta.getInteger(R.styleable.HanZiView_rotateSize, 40); SMUDGE_COEFFICIENT = ta.getFloat(R.styleable.HanZiView_smudgeCoefficieng, 1.0f); mTextSize = ta.getDimensionPixelSize(R.styleable.HanZiView_textSize, DisplayUtil.sp2px(context, 20)); mTextColor = ta.getColor(R.styleable.HanZiView_textColor, 0); ta.recycle(); init(); } /** * 初始化数据 */ private void init() { mPaint = new Paint(); mPaint.setTextSize(mTextSize); if (0 != mTextColor) { mPaint.setColor(mTextColor); } else { mPaint.setColor(Util.randomDarkColor()); } // 随机使用粗体 mPaint.setFakeBoldText(r.nextBoolean()); mBounds = new Rect(); mPaint.getTextBounds(mText, 0, mText.length(), mBounds); } /** * 随机生成扭曲位置的XY坐标 */ private void initRandomXY() { mRandomX = (float) Math.random() * getWidth(); mRandomY = (float) Math.random() * getHeight(); } /** * 设置显示的文字 * * @param text */ public void setText(String text) { this.mText = text; needDraw = true; invalidate(); } /** * 设置显示的文字的颜色 * * @param color */ public void setTextColor(int color) { mPaint.setColor(color); needDraw = true; invalidate(); } /** * 设置显示文字的颜色的ARGB * * @param A * @param R * @param G * @param B */ public void setTextColor(int A, int R, int G, int B) { mPaint.setARGB(A, R, G, B); needDraw = true; invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = 0; int height = 0; int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); switch (specMode) { case MeasureSpec.EXACTLY: width = getPaddingLeft() + getPaddingRight() + specSize + mBorderWidthSize; break; case MeasureSpec.AT_MOST: width = getPaddingLeft() + getPaddingRight() + mBounds.width() + mBorderWidthSize; break; case MeasureSpec.UNSPECIFIED: width = getPaddingLeft() + getPaddingRight() + mBounds.width() + mBorderWidthSize; break; } specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); switch (specMode) { case MeasureSpec.EXACTLY: height = getPaddingTop() + getPaddingBottom() + specSize + mBorderHeightSize; break; case MeasureSpec.AT_MOST: height = getPaddingTop() + getPaddingBottom() + mBounds.height() + mBorderHeightSize; break; case MeasureSpec.UNSPECIFIED: height = getPaddingTop() + getPaddingBottom() + mBounds.height() + mBorderHeightSize; break; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (needDraw) { // 创建带有文字的图片 mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(mBitmap); c.drawText(mText, getWidth() / 2 - mBounds.width() / 2, mBounds.height() / 2 + getHeight() / 2, mPaint); // 初始化坐标数组 int index = 0; for (int y = 0; y <= HEIGHT; y++) { float fy = mBitmap.getHeight() * y / HEIGHT; for (int x = 0; x <= WIDTH; x++) { float fx = mBitmap.getWidth() * x / WIDTH; setXY(matrixMoved, index, fx, fy); setXY(matrixOriganal, index, fx, fy); index += 1; } } initRandomXY(); smudgeMatrix(); // 随机旋转角度 rotate = (int) (Math.floor(Math.random() * DEFAULT_ROTATE_SIZE + 1) - Math.floor(Math.random() * DEFAULT_ROTATE_SIZE * 2 + 1)); } needDraw = false; // 绘制网格位图 canvas.drawBitmapMesh(rotateBitmap(rotate, mBitmap), WIDTH, HEIGHT, matrixMoved, 0, null, 0, null); } /** * 设置坐标数组 * * @param array 坐标数组 * @param index 标识值 * @param x x坐标 * @param y y坐标 */ private void setXY(float[] array, int index, float x, float y) { array[index * 2 + 0] = x; array[index * 2 + 1] = y; } /** * 计算变换数组坐标 */ private void smudgeMatrix() { for (int i = 0; i < COUNT * 2; i += 2) { float xOriginal = matrixOriganal[i + 0]; float yOriginal = matrixOriganal[i + 1]; float dist_random_to_origin_x = mRandomX - xOriginal; float dist_random_to_origin_y = mRandomY - yOriginal; float kv_kat = dist_random_to_origin_x * dist_random_to_origin_x + dist_random_to_origin_y * dist_random_to_origin_y; float pull = (float) (1000 * SMUDGE_COEFFICIENT / kv_kat / Math.sqrt(kv_kat)); if (pull >= 1) { matrixMoved[i + 0] = mRandomX; matrixMoved[i + 1] = mRandomY; } else { matrixMoved[i + 0] = xOriginal + dist_random_to_origin_x * pull; matrixMoved[i + 1] = yOriginal + dist_random_to_origin_y * pull; } } } /** * 旋转图片 * * @param degree * @param bitmap * @return */ public Bitmap rotateBitmap(int degree, Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.postRotate(degree); Bitmap bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return bm; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310

这里用随机的XY坐标来代替点击的坐标,把pull进行处理,可以自由的去改变扭曲度,加入了一个旋转图像方法,当然如果需要,还可以去添加和修改里边的内容.


三、完整代码实及现

GridView的内容就不再写了.唯一注意的一点就是,需要重新测量一下GridView的高度,并适当添加一些高度,不然会出现GridView有滚动条的现象.  最后结合前两篇文章,贴一下完整的代码实现:  Android仿斗鱼领取鱼丸文字验证(一)  Android仿斗鱼领取鱼丸文字验证(二)

attr:

<?xml version="1.0" encoding="utf-8"?> <resources> <!--AnswerLayout相关--> <!--删除按钮图像--> <attr name="deleteBtnSrc" format="reference"/> <!--汉字格边框颜色--> <attr name="hanziBorderColor" format="color"/> <!--汉字格边框的粗度--> <attr name="hanziBorderStrokeWidth" format="float"/> <!--汉字格的个数--> <attr name="hanziTestNum" format="integer"/> <!--文字的颜色--> <attr name="hanziTextColor" format="color"/> <!--文字大小--> <attr name="hanziTextSize" format="dimension"/> <declare-styleable name="AnswerLayout"> <attr name="deleteBtnSrc"/> <attr name="hanziBorderColor"/> <attr name="hanziBorderStrokeWidth"/> <attr name="hanziTestNum"/> <attr name="hanziTextColor"/> <attr name="hanziTextSize"/> </declare-styleable> <!--HanZiView相关--> <!--左右宽度留白--> <attr name="borderWidthSize" format="dimension"/> <!--上下高度留白--> <attr name="borderHeightSize" format="dimension"/> <!--旋转的度数--> <attr name="rotateSize" format="integer"/> <!--扭曲的系数--> <attr name="smudgeCoefficieng" format="float"/> <!--文字的颜色--> <attr name="textColor" format="color"/> <!--文字大小--> <attr name="textSize" format="dimension"/> <declare-styleable name="HanZiView"> <attr name="borderWidthSize"/> <attr name="borderHeightSize"/> <attr name="rotateSize"/> <attr name="smudgeCoefficieng"/> <attr name="textColor"/> <attr name="textSize"/> </declare-styleable> <!--CodeView相关--> <!--扰乱项的个数--> <attr name="disturbSize" format="integer"/> <!--干扰项文字的大小--> <attr name="disturbTextSize" format="dimension"/> <!--干扰项文字颜色--> <attr name="disturbTextColor" format="color"/> <!--验证码文字大小--> <attr name="codeTextSize" format="dimension"/> <!--验证码文字颜色--> <attr name="codeTextColor" format="color"/> <!--画布旋转的度数--> <attr name="rotate" format="integer"/> <declare-styleable name="CodeView"> <attr name="disturbSize"/> <attr name="disturbTextSize"/> <attr name="disturbTextColor"/> <attr name="codeTextSize"/> <attr name="codeTextColor"/> <attr name="rotate"/> </declare-styleable> </resources> 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667

xml:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:hz="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/ll_all" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" tools:context="com.example.junweiliu.hanzicode.MainActivity"> <LinearLayout android:layout_width="280dp" android:layout_height="wrap_content" android:background="@mipmap/hz_nor_ng" android:orientation="vertical" android:padding="20dp" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:text="验证码:" android:textSize="14sp" /> <com.example.junweiliu.hanzicode.AnswerLayout android:id="@+id/al_mal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:gravity="center" android:orientation="horizontal" > <com.example.junweiliu.hanzicode.CodeView android:id="@+id/cv_hz" android:layout_width="120dp" android:layout_height="42dp" hz:rotate="4" /> <Button android:id="@+id/btn_reset" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:background="@null" android:text="看不清?" android:textColor="#4791FF" android:textSize="14sp" /> </LinearLayout> <GridView android:id="@+id/gv_hz" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" android:gravity="center" android:horizontalSpacing="10dp" android:listSelector="@android:color/transparent" android:numColumns="3" android:scrollbars="none" android:stretchMode="columnWidth" android:verticalSpacing="10dp" /> </LinearLayout> </LinearLayout> 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687

HanZiView:

package com.example.junweiliu.hanzicode; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.ImageView; import java.util.Random; /** * Created by junweiliu on 16/5/3. */ public class HanZiView extends ImageView { /** * 分割数 */ private static final int WIDTH = 9, HEIGHT = 9; /** * 焦点数 */ private static final int COUNT = (WIDTH + 1) * (HEIGHT + 1); /** * 位图对象 */ private Bitmap mBitmap; /** * 基准点坐标数组 */ private float[] matrixOriganal = new float[COUNT * 2]; /** * 变换后点坐标数组 */ private float[] matrixMoved = new float[COUNT * 2]; /** * 避免过多的重绘 */ private boolean needDraw = true; /** * 画笔 */ private Paint mPaint; /** * 文字大小 */ private int mTextSize; /** * 文字 */ private String mText = "逗"; /** * 文字颜色 */ private int mTextColor; /** * 矩形区域 */ private Rect mBounds; /** * 四周的留白宽度值 */ private int mBorderWidthSize; /** * 四周的留白高度值 */ private int mBorderHeightSize; /** * 获取扭曲点的随机位置 */ private float mRandomX, mRandomY; /** * 字体旋转的角度 */ private int DEFAULT_ROTATE_SIZE; /** * 扭曲的系数 */ private float SMUDGE_COEFFICIENT; /** * 旋转的角度 */ private int rotate; /** * 随机 */ private Random r = new Random(); public HanZiView(Context context) { this(context, null); } public HanZiView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public HanZiView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 获取自定义属性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HanZiView); mBorderWidthSize = ta.getDimensionPixelSize(R.styleable.HanZiView_borderWidthSize, DisplayUtil.dip2px(context, 30)); mBorderHeightSize = ta.getDimensionPixelSize(R.styleable.HanZiView_borderHeightSize, DisplayUtil.dip2px(context, 20)); DEFAULT_ROTATE_SIZE = ta.getInteger(R.styleable.HanZiView_rotateSize, 40); SMUDGE_COEFFICIENT = ta.getFloat(R.styleable.HanZiView_smudgeCoefficieng, 1.0f); mTextSize = ta.getDimensionPixelSize(R.styleable.HanZiView_textSize, DisplayUtil.sp2px(context, 20)); mTextColor = ta.getColor(R.styleable.HanZiView_textColor, 0); ta.recycle(); init(); } /** * 初始化数据 */ private void init() { mPaint = new Paint(); // mPaint.setAntiAlias(true); mPaint.setTextSize(mTextSize); if (0 != mTextColor) { mPaint.setColor(mTextColor); } else { mPaint.setColor(Util.randomDarkColor()); } // 随机使用粗体 mPaint.setFakeBoldText(r.nextBoolean()); mBounds = new Rect(); mPaint.getTextBounds(mText, 0, mText.length(), mBounds); } /** * 随机生成扭曲位置的XY坐标 */ private void initRandomXY() { mRandomX = (float) Math.random() * getWidth(); mRandomY = (float) Math.random() * getHeight(); } /** * 设置显示的文字 * * @param text */ public void setText(String text) { this.mText = text; needDraw = true; invalidate(); } /** * 设置显示的文字的颜色 * * @param color */ public void setTextColor(int color) { mPaint.setColor(color); needDraw = true; invalidate(); } /** * 设置显示文字的颜色的ARGB * * @param A * @param R * @param G * @param B */ public void setTextColor(int A, int R, int G, int B) { mPaint.setARGB(A, R, G, B); needDraw = true; invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = 0; int height = 0; int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); switch (specMode) { case MeasureSpec.EXACTLY: width = getPaddingLeft() + getPaddingRight() + specSize + mBorderWidthSize; break; case MeasureSpec.AT_MOST: width = getPaddingLeft() + getPaddingRight() + mBounds.width() + mBorderWidthSize; break; case MeasureSpec.UNSPECIFIED: width = getPaddingLeft() + getPaddingRight() + mBounds.width() + mBorderWidthSize; break; } specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); switch (specMode) { case MeasureSpec.EXACTLY: height = getPaddingTop() + getPaddingBottom() + specSize + mBorderHeightSize; break; case MeasureSpec.AT_MOST: height = getPaddingTop() + getPaddingBottom() + mBounds.height() + mBorderHeightSize; break; case MeasureSpec.UNSPECIFIED: height = getPaddingTop() + getPaddingBottom() + mBounds.height() + mBorderHeightSize; break; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (needDraw) { // 创建带有文字的图片 mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(mBitmap); c.drawText(mText, getWidth() / 2 - mBounds.width() / 2, mBounds.height() / 2 + getHeight() / 2, mPaint); // 初始化坐标数组 int index = 0; for (int y = 0; y <= HEIGHT; y++) { float fy = mBitmap.getHeight() * y / HEIGHT; for (int x = 0; x <= WIDTH; x++) { float fx = mBitmap.getWidth() * x / WIDTH; setXY(matrixMoved, index, fx, fy); setXY(matrixOriganal, index, fx, fy); index += 1; } } initRandomXY(); smudgeMatrix(); // 随机旋转角度 rotate = (int) (Math.floor(Math.random() * DEFAULT_ROTATE_SIZE + 1) - Math.floor(Math.random() * DEFAULT_ROTATE_SIZE * 2 + 1)); } needDraw = false; // 绘制网格位图 canvas.drawBitmapMesh(rotateBitmap(rotate, mBitmap), WIDTH, HEIGHT, matrixMoved, 0, null, 0, null); } /** * 设置坐标数组 * * @param array 坐标数组 * @param index 标识值 * @param x x坐标 * @param y y坐标 */ private void setXY(float[] array, int index, float x, float y) { array[index * 2 + 0] = x; array[index * 2 + 1] = y; } /** * 计算变换数组坐标 */ private void smudgeMatrix() { for (int i = 0; i < COUNT * 2; i += 2) { float xOriginal = matrixOriganal[i + 0]; float yOriginal = matrixOriganal[i + 1]; float dist_random_to_origin_x = mRandomX - xOriginal; float dist_random_to_origin_y = mRandomY - yOriginal; float kv_kat = dist_random_to_origin_x * dist_random_to_origin_x + dist_random_to_origin_y * dist_random_to_origin_y; float pull = (float) (1000 * SMUDGE_COEFFICIENT / kv_kat / Math.sqrt(kv_kat)); if (pull >= 1) { matrixMoved[i + 0] = mRandomX; matrixMoved[i + 1] = mRandomY; } else { matrixMoved[i + 0] = xOriginal + dist_random_to_origin_x * pull; matrixMoved[i + 1] = yOriginal + dist_random_to_origin_y * pull; } } } /** * 旋转图片 * * @param degree * @param bitmap * @return */ public Bitmap rotateBitmap(int degree, Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.postRotate(degree); Bitmap bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return bm; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306

BorderTextView:

package com.example.junweiliu.hanzicode; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.widget.TextView; /** * Created by junweiliu on 16/5/4. */ public class BorderTextView extends TextView { /** * 画笔 */ private Paint mPaint; public BorderTextView(Context context) { this(context, null); } public BorderTextView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { mPaint = new Paint(); // 将边框设为灰色 mPaint.setColor(Color.GRAY); mPaint.setAntiAlias(true); mPaint.setStrokeWidth((float) 3.0); } /** * 设置边框颜色 * * @param color */ public void setBorderColor(int color) { mPaint.setColor(color); invalidate(); } /** * 设置边框宽度 * * @param size */ public void setBorderStrokeWidth(float size) { mPaint.setStrokeWidth(size); invalidate(); } @Override protected void onDraw(Canvas canvas) { // 画TextView的4个边 canvas.drawLine(0, 0, this.getWidth(), 0, mPaint); canvas.drawLine(0, 0, 0, this.getHeight(), mPaint); // 右边线 // canvas.drawLine(this.getWidth(), 0, this.getWidth(), this.getHeight(), mPaint); canvas.drawLine(0, this.getHeight(), this.getWidth(), this.getHeight(), mPaint); super.onDraw(canvas); } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768

AnswerLayout:

package com.example.junweiliu.hanzicode; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Created by junweiliu on 16/5/4. */ public class AnswerLayout extends LinearLayout { /** * 整体的宽度 */ private int pWidth; /** * 需要验证字的个数(多少个框,默认为4个) */ private int HANZI_TEST_SIZE; /** * 需要创建的子View个数 */ private int childViewCount; /** * 回答的答案列表 */ private List<String> mAnswers; /** * 正确答案 */ private List<String> mCorrectAnswer; /** * 删除按钮资源 */ private int deleteBtnSrc; /** * 汉子格边框颜色 */ private int hanziBorderColor; /** * 汉子格边框宽度 */ private float hanziBorderStrokeWidth; /** * 汉字格文字的颜色 */ private int hanziTextColor; /** * 汉字格文字的大小 */ private int hanziTextSize; /** * 回调接口 */ interface CheckAnswerListener { // 成功回调 public void onSuccess(); // 失败回调 public void onFail(); } /** * 回调 */ private CheckAnswerListener mListener; /** * 设置回调 * * @param listener */ public void setCheckAnswerListener(CheckAnswerListener listener) { this.mListener = listener; } public AnswerLayout(Context context) { this(context, null); } public AnswerLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public AnswerLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 获取自定义属性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.AnswerLayout); deleteBtnSrc = ta.getResourceId(R.styleable.AnswerLayout_deleteBtnSrc, R.drawable.delete_btn_selector); hanziBorderColor = ta.getColor(R.styleable.AnswerLayout_hanziBorderColor, Color.GRAY); hanziBorderStrokeWidth = ta.getFloat(R.styleable.AnswerLayout_hanziBorderStrokeWidth, 3.0f); HANZI_TEST_SIZE = ta.getInt(R.styleable.AnswerLayout_hanziTestNum, 4); hanziTextColor = ta.getColor(R.styleable.AnswerLayout_hanziTextColor, Color.BLACK); hanziTextSize = ta.getDimensionPixelSize(R.styleable.AnswerLayout_hanziTextSize, 16); ta.recycle(); init(); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mAnswers.size() > HANZI_TEST_SIZE) { return; } if (mAnswers.size() == 0) { for (int i = 0; i < HANZI_TEST_SIZE; i++) { TextView tv = (TextView) getChildAt(i); tv.setText(""); } } else { for (int i = 0; i < mAnswers.size(); i++) { TextView tv = (TextView) getChildAt(i); tv.setText(mAnswers.get(i)); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); pWidth = MeasureSpec.getSize(widthMeasureSpec); if (HANZI_TEST_SIZE <= 0) { return; } // 需要加入一个删除按钮,所以子view的总数需要加1 childViewCount = HANZI_TEST_SIZE + 1; for (int i = 0; i < HANZI_TEST_SIZE; i++) { addView(makeItemView(i)); } addView(makeDeleteView()); } /** * 初始化数据 */ public void init() { mAnswers = new ArrayList<String>(); mCorrectAnswer = new ArrayList<String>(); } /** * 填写的答案 * * @param answers */ public void setAnswers(List<String> answers) { this.mAnswers = answers; invalidate(); if (compare(mAnswers, mCorrectAnswer) && mAnswers.size() == HANZI_TEST_SIZE) { if (null != mListener) mListener.onSuccess(); } else if (mAnswers.size() == HANZI_TEST_SIZE) { if (null != mListener) mListener.onFail(); } } /** * 判断两个list是否完全相同 * * @param a * @param b * @param <T> * @return */ private <T extends Comparable<T>> boolean compare(List<T> a, List<T> b) { if (a.size() != b.size()) return false; for (int i = 0; i < a.size(); i++) { if (!a.get(i).equals(b.get(i))) return false; } return true; } /** * 正确答案 * * @param correctAnswers */ public void setCorrectAnswers(List<String> correctAnswers) { this.mCorrectAnswer = correctAnswers; } /** * 重置 */ public void reSet(List<String> correctAnswers) { mAnswers.clear(); mCorrectAnswer = correctAnswers; invalidate(); } /** * 创建删除按钮 * * @return */ private View makeDeleteView() { ImageView deleteView = new ImageView(getContext()); LinearLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); lp.width = pWidth / childViewCount; lp.height = pWidth / childViewCount; deleteView.setImageResource(deleteBtnSrc); deleteView.setScaleType(ImageView.ScaleType.FIT_XY); deleteView.setLayoutParams(lp); deleteView.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { for (int i = HANZI_TEST_SIZE; i > 0; i--) { TextView tv = (TextView) getChildAt(i - 1); if (!"".equals(tv.getText())) { mAnswers.remove(i - 1); tv.setText(""); break; } } } }); return deleteView; } /** * 创建汉子验证格 * * @return */ private View makeItemView(final int i) { final BorderTextView bv = new BorderTextView(getContext()); LinearLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); lp.width = pWidth / childViewCount; lp.height = pWidth / childViewCount; bv.setBorderColor(hanziBorderColor); bv.setTextSize(hanziTextSize); bv.setGravity(Gravity.CENTER); bv.setText(""); bv.setLayoutParams(lp); bv.setTextColor(hanziTextColor); bv.setBorderStrokeWidth(hanziBorderStrokeWidth); return bv; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263

CodeView:

package com.example.junweiliu.hanzicode; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; 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.widget.ImageView; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * Created by junweiliu on 16/5/9. */ public class CodeView extends ImageView { /** * 干扰项 */ private 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 Paint hzPaint; /** * 干扰项画笔 */ private Paint dbPaint; /** * 汉字画笔颜色 */ private int hzColor; /** * 干扰项画笔颜色 */ private int dbColor; /** * 干扰项的个数,默认30个 */ private int DEFAULT_DBSIZE; /** * 默认画笔颜色 */ private int DEFAULT_DBCOLOR, DEFAULT_HZCOLOR; /** * 干扰项随机生成的位置 */ private float dbRandomX, dbRandomY; /** * 验证码文字 */ private List<String> codeList; /** * 随机 */ private Random random = new Random(); /** * 矩形区域 */ private Rect mBounds; /** * 干扰文字大小 */ private int dbTextSize; /** * 验证码文字大小 */ private int hzTextSize; /** * 画布旋转度数,默认为6 */ private int rotate; public CodeView(Context context) { this(context, null); } public CodeView(Context context, AttributeSet attrs) { this(context, attrs, 0); } ; public CodeView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 获取自定义属性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CodeView); DEFAULT_DBSIZE = ta.getInteger(R.styleable.CodeView_disturbSize, 30); dbTextSize = DisplayUtil.sp2px(context, ta.getDimensionPixelSize(R.styleable.CodeView_disturbTextSize, 10)); hzTextSize = DisplayUtil.sp2px(context, ta.getDimensionPixelSize(R.styleable.CodeView_codeTextSize, 20)); DEFAULT_DBCOLOR = ta.getColor(R.styleable.CodeView_disturbTextColor, 0); DEFAULT_HZCOLOR = ta.getColor(R.styleable.CodeView_codeTextColor, 0); rotate = ta.getInteger(R.styleable.CodeView_rotate, 6); init(); } /** * 初始化 */ private void init() { // 验证的文字颜色一致,只需要生成一次 if (0 == DEFAULT_HZCOLOR) { hzColor = Util.randomDarkColor(); } else { hzColor = DEFAULT_HZCOLOR; } mBounds = new Rect(); codeList = new ArrayList<String>(); } @Override protected void onDraw(Canvas canvas) { Bitmap bp = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bp); String dbCode = createDBCode(); // 绘制干扰项 for (int i = 0; i < dbCode.length(); i++) { randomDBStyele(); randomDBPosition(); c.drawText(dbCode.charAt(i) + "", dbRandomX, dbRandomY, dbPaint); } canvas.drawBitmap(bp, 0, 0, dbPaint); // 绘制验证文字 if (null != codeList && codeList.size() > 0) { for (int i = 0; i < codeList.size(); i++) { randomHZStyle(); canvas.save(); canvas.rotate((int) (Math.floor(Math.random() * rotate + 1) - Math.floor(Math.random() * rotate * 2 + 1))); canvas.drawText(codeList.get(i), mBounds.width() * (i + 1), getHeight() / 2 + mBounds.height() / 2 + rotate, hzPaint); canvas.restore(); } } // super.onDraw(canvas); } /** * 重置 */ public void reSet(List<String> codes) { // 重置汉字画笔颜色 if (0 == DEFAULT_HZCOLOR) { hzColor = Util.randomDarkColor(); } else { hzColor = DEFAULT_HZCOLOR; } this.codeList.clear(); this.codeList = codes; invalidate(); } /** * 设置验证码文字 * * @param codes */ public void setCode(List<String> codes) { this.codeList = codes; invalidate(); } /** * 随机生成汉字画笔样式 */ private void randomHZStyle() { hzPaint = new Paint(); // hzPaint.setAntiAlias(true); hzPaint.setColor(hzColor); hzPaint.setFakeBoldText(random.nextBoolean()); hzPaint.setTextSize(hzTextSize); hzPaint.getTextBounds("一", 0, "一".length(), mBounds); } /** * 随机生成干扰项画笔样式 */ private void randomDBStyele() { if (0 == DEFAULT_DBCOLOR) { dbColor = Util.randomDarkColor(); } else { dbColor = DEFAULT_DBCOLOR; } dbPaint = new Paint(); // dbPaint.setAntiAlias(true); dbPaint.setColor(dbColor); dbPaint.setTextSize(dbTextSize); } /** * 随机生成干扰项的显示位置 */ private void randomDBPosition() { dbRandomX = (float) Math.random() * getWidth(); dbRandomY = (float) Math.random() * getHeight(); } /** * 创建干扰项 * * @return */ private String createDBCode() { StringBuilder buffer = new StringBuilder(); for (int i = 0; i < DEFAULT_DBSIZE; i++) { buffer.append(CHARS[random.nextInt(CHARS.length)]); } return buffer.toString(); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227

Util:

package com.example.junweiliu.hanzicode; import android.graphics.Color; import android.view.View; import android.view.ViewGroup; import android.widget.GridView; import android.widget.ListAdapter; import java.io.UnsupportedEncodingException; import java.util.Random; /** * Created by junweiliu on 16/5/9. */ public class Util { /** * 获取随机的颜色 * * @return */ public static int randomColor() { int red = (int) (Math.random() * 256); int green = (int) (Math.random() * 256); int blue = (int) (Math.random() * 256); return Color.argb(255, red, green, blue); } /** * 获取随机的暗色 * * @return */ public static int randomDarkColor() { int red = (int) (Math.random() * 100 + 56); int green = (int) (Math.random() * 100 + 56); int blue = (int) (Math.random() * 100 + 56); return Color.argb(255, red, green, blue); } /** * 获取指定长度随机简体中文 * * @param len int * @return String */ public static String getRandomJianHan(int len) { String ret = ""; for (int i = 0; i < len; i++) { String str = null; int hightPos, lowPos; // 定义高低位 Random random = new Random(); hightPos = (176 + Math.abs(random.nextInt(39))); //获取高位值 lowPos = (161 + Math.abs(random.nextInt(93))); //获取低位值 byte[] b = new byte[2]; b[0] = (new Integer(hightPos).byteValue()); b[1] = (new Integer(lowPos).byteValue()); try { str = new String(b, "GBk"); //转成中文 } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); } ret += str; } return ret; } /** * 计算GridView宽高 * * @param gridView */ public static void calGridViewWidthAndHeigh(int numColumns, GridView gridView) { // 获取GridView对应的Adapter ListAdapter listAdapter = gridView.getAdapter(); if (listAdapter == null) { return; } int totalHeight = 0; for (int i = 0, len = listAdapter.getCount(); i < len; i++) { // listAdapter.getCount()返回数据项的数目 View listItem = listAdapter.getView(i, null, gridView); // 计算子项View 的宽高 listItem.measure(0, 0); if ((i + 1) % numColumns == 0) { // 统计所有子项的总高度 totalHeight += listItem.getMeasuredHeight(); } if ((i + 1) == len && (i + 1) % numColumns != 0) { // 统计所有子项的总高度 totalHeight += listItem.getMeasuredHeight(); } } // 适当添加一些高度,防止gridview高度不够,出现滚动条 totalHeight += 60; ViewGroup.LayoutParams params = gridView.getLayoutParams(); params.height = totalHeight; // params.height最后得到整个ListView完整显示需要的高度 gridView.setLayoutParams(params); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108

DisplayUtil:

package com.example.junweiliu.hanzicode; import android.content.Context; /** * dp、sp 转换为 px 的工具类 */ public class DisplayUtil { /** * 将px值转换为dip或dp值,保证尺寸大小不变 * * @param pxValue * @return */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } /** * 将dip或dp值转换为px值,保证尺寸大小不变 * * @param dipValue * @return */ public static int dip2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } /** * 将px值转换为sp值,保证文字大小不变 * * @param pxValue * @return */ public static int px2sp(Context context, float pxValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (pxValue / fontScale + 0.5f); } /** * 将sp值转换为px值,保证文字大小不变 * * @param spValue * @return */ public static int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152

MainActivity:

package com.example.junweiliu.hanzicode; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.support.v4.graphics.ColorUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.GridView; import android.widget.ListAdapter; import android.widget.Toast; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.Random; public class MainActivity extends Activity { /** * 验证答案框 */ private AnswerLayout mAnswerLayout; /** * 验证文字框 */ private GridView mGridView; /** * 验证码 */ private CodeView mCodeView; /** * 重置按钮 */ private Button mResetBtn; /** * 适配器 */ private HanZiCodeAdapter hzAdapter; /** * 获取到的文字备份 */ private List<String> cHanZiList; /** * 获取到的文字 */ private List<String> mHanZiList; /** * 选择的文字 */ private List<String> mChooseHZList; /** * 正确答案 */ private List<String> mCorrectList; /** * 验证框汉字颜色 */ private int mColor; /** * 测试计数 */ private int num = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); initGridView(); } /** * 初始化gridview */ private void initGridView() { mGridView = (GridView) findViewById(R.id.gv_hz); hzAdapter = new HanZiCodeAdapter(); mGridView.setAdapter(hzAdapter); Util.calGridViewWidthAndHeigh(3, mGridView); } /** * 初始化数据 */ private void initData() { mHanZiList = new ArrayList<String>(); cHanZiList = new ArrayList<String>(); mChooseHZList = new ArrayList<String>(); mCorrectList = new ArrayList<String>(); for (int i = 0; i < 9; i++) { mHanZiList.add(Util.getRandomJianHan(1)); } cHanZiList.addAll(mHanZiList); // 添加不重复的文字 for (int i = 0; i < 4; i++) { String hz = cHanZiList.get((int) (Math.random() * cHanZiList.size())); cHanZiList.remove(hz); mCorrectList.add(hz); } mColor = Util.randomColor(); // Log.e("main", "颜色值为" + mColor + "\n获取到的文字为\n" + mCorrectList.get(0) + "\n" + mCorrectList.get(1) + "\n" + mCorrectList.get(2) + "\n" + mCorrectList.get(3)); } /** * 初始化控件 */ private void initView() { mAnswerLayout = (AnswerLayout) findViewById(R.id.al_mal); mAnswerLayout.setCorrectAnswers(mCorrectList); mAnswerLayout.setCheckAnswerListener(new AnswerLayout.CheckAnswerListener() { @Override public void onSuccess() { Toast.makeText(MainActivity.this, "成功", Toast.LENGTH_SHORT).show(); } @Override public void onFail() { Toast.makeText(MainActivity.this, "失败", Toast.LENGTH_SHORT).show(); } }); mCodeView = (CodeView) findViewById(R.id.cv_hz); mCodeView.setCode(mCorrectList); mResetBtn = (Button) findViewById(R.id.btn_reset); mResetBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { initData(); mAnswerLayout.reSet(mCorrectList); mCodeView.reSet(mCorrectList); hzAdapter.notifyDataSetChanged(); } }); } /** * 汉子展示框适配器 */ class HanZiCodeAdapter extends BaseAdapter { @Override public int getCount() { return mHanZiList.size(); } @Override public Object getItem(int i) { return mHanZiList.get(i); } @Override public long getItemId(int i) { return 0; } @Override public View getView(final int i, View view, ViewGroup viewGroup) { ViewHolder holder = new ViewHolder(); if (null == view) { view = LayoutInflater.from(MainActivity.this).inflate(R.layout.hanzi_item, null, false); holder.hzImg = (HanZiView) view.findViewById(R.id.hz_item); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } //设置holder holder.hzImg.setText(mHanZiList.get(i)); holder.hzImg.setTextColor(mColor); holder.hzImg.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mChooseHZList.size() < 4) { mChooseHZList.add(mHanZiList.get(i)); mAnswerLayout.setAnswers(mChooseHZList); } // Toast.makeText(MainActivity.this, "点的是" + mHanZiList.get(i), Toast.LENGTH_SHORT).show(); } }); return view; } class ViewHolder { HanZiView hzImg; } } }源代码云盘自取:链接:http://pan.baidu.com/s/1hr7fp4o 密码:folt
转载请注明原文地址: https://www.6miu.com/read-1700162.html

最新回复(0)