Android仿斗鱼领取鱼丸文字验证(二)

接着第一篇内容,来完成一下中间部分,中文验证码的部分,先看一下要实现的效果:

效果图


一、分析功能

分析一下,我们要实现一个验证码功能,随机生成4个中文组合,文字随机,文字颜色随机,文字会出现不同程度的旋转,并且文字周围会出现一些干扰项,点击看不清时验证码内容会进行刷新重置.要解决的问题基本就这么多,下面分步来解决一下.

(1)文字和文字颜色随机,这个实现起来不难,只需要随机产生一个中文,并且画笔定义随机的颜色即可.
(2)文字旋转的话,可以通过旋转画布来完成该功能.
(3)对于干扰项,可以定义一个干扰项的数组,然后随机取出里边的内容,在画布上的随机位置进行绘制即可.
(4)刷新重置这个做过很多遍,只需要重置一些变量,再去调用view的重绘方法即可.


二、代码实现

通过上边的分析,我们自定义一个CodeView来完成该功能.

先来定义一些自定义属性,成员变量.代码如下:

自定义属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--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"/>
</resources>        

变量:

    /**
     * 干扰项
     */
    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, 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();

    }

通过分析我们知道,我们需要随机生成一个中文,然后还要随机生成文字的颜色,所以先来写一个工具类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;
    }
}

代码不是很复杂,这里定义了两种颜色的获取,使用的时候可以根据自己的需要来使用或者修改.

先来搞定干扰项的问题,干扰项已经定义过了,是一些数字和字母,随机颜色的方法也写在工具类了,之后就是去生成这些干扰项,生成之前,还需要随机生成位置,然后去绘制在画布上就可以了.

干扰项:


    /**
     * 创建干扰项
     *
     * @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();
    }

    /**
     * 随机生成干扰项画笔样式
     */
    private void randomDBStyele() {
        if (0 == DEFAULT_DBCOLOR) {
            dbColor = Util.randomDarkColor();
        } else {
            dbColor = DEFAULT_DBCOLOR;
        }
        dbPaint = new Paint();
        dbPaint.setColor(dbColor);
        dbPaint.setTextSize(dbTextSize);
    }

    /**
     * 随机生成干扰项的显示位置
     */
    private void randomDBPosition() {
        dbRandomX = (float) Math.random() * getWidth();
        dbRandomY = (float) Math.random() * getHeight();
    }

    /**
    * 绘制
    */ 
    @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);
    }

然后来看一下验证文字,我们对外提供一个设置验证文字的方法,因为这些需要验证的文字肯定不是固定的,所以需要提供一个方法,来随时改变这些验证文字,获取到这些验证文字之后,就可以去绘制了.

验证文字:

    /**
     * 设置验证码文字
     *
     * @param codes
     */
    public void setCode(List<String> codes) {
        this.codeList = codes;
        invalidate();
    }

    /**
     * 随机生成汉字画笔样式
     */
    private void randomHZStyle() {
        hzPaint = new Paint();
        hzPaint.setColor(hzColor);
        hzPaint.setFakeBoldText(random.nextBoolean());
        hzPaint.setTextSize(hzTextSize);
        hzPaint.getTextBounds("一", 0, "一".length(), mBounds);
    }

    /**
    * 绘制
    */ 
    @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, hzPaint);
                canvas.restore();
            }
        }
    }

绘制验证文字的时候需要注意一下,这里只是对画布进行了一个旋转操作,如果需要设计不同的效果,可以去添加修改这部分,如果需要特殊字体,也可以在初始化的时候去加载一些特殊字体,这里就不去做这些内容了.

最后就是对外提供一个重置的方法,很简单

    /**
     * 重置
     */
    public void reSet(List<String> codes) {
        // 重置汉字画笔颜色
        if (0 == DEFAULT_HZCOLOR) {
            hzColor = Util.randomDarkColor();
        } else {
            hzColor = DEFAULT_HZCOLOR;
        }
        this.codeList.clear();
        this.codeList = codes;
        invalidate();
    }

三、完整代码实及现

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, 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();
    }

}

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:layout_marginTop="10dp"
            android:gravity="center"
            android:orientation="horizontal"
            android:visibility="visible"
            >
            <com.example.junweiliu.hanzicode.CodeView
                android:id="@+id/cv_hz"
                android:layout_width="120dp"
                android:layout_height="40dp"
                />

            <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>
    </LinearLayout>
</LinearLayout>

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 CodeView mCodeView;
    /**
     * 重置按钮
     */
    private Button mResetBtn;
    /**
     * 正确答案
     */
    private List<String> mCorrectList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
    }

    /**
     * 初始化数据
     */
    private void initData() {
        mCorrectList = new ArrayList<String>();
        // 添加文字
        for (int i = 0; i < 4; i++) {
            mCorrectList.add(Util.getRandomJianHan(1));
        }
    }

    /**
     * 初始化控件
     */
    private void initView() {        
        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();
                // 刷新验证码
                mCodeView.reSet(mCorrectList);
            }
        }); 
     } 
}  

发布了25 篇原创文章 · 获赞 63 · 访问量 11万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章