Android 手势密码

基本上很多应用都有一个手势密码锁.估计很多大神可能会觉得这个东西很简单,就是九个点连接起来,然后保存在本地,当然如果你要加密的话用MD5或者HASH来加密一下,然后就来一个锁,锁代表安全,代表个人专属,代表身份象征哈,有钱淫都有锁,穷人没的锁哈..好了,不扯淡了..接下来咱们来搞一下这个东西

思路:
以3 * 3 的正方形手势密码绘制为例

先要想办法得到这个手势密码的九个点的坐标,然后进行绘制,并且以状态来改变此九个点的状态是按下还是普通,以及错误.最后将密码保存在本地,然后在测试的时候进行比对判断.大致思路如此,咱们还是上代码一步步走吧,毕竟步子走的大了容易扯到蛋..

首先: 首先个毛线啊,让我缕缕思路哈..我觉得要不先来个图吧..
这里写图片描述

这里在下粗略的画了一个图,毕竟是只猿,艺术么,和咱关系不大,玩不来那东西,于是乎就画了个比较丑的图,咋样才能得到这九个点的坐标呢,咱们要不这样吧..以手机屏幕的宽度为边长,在屏幕中心画一个四方块,然后把这个四方块呢在画成16个小方格子,如上图哈…然后得到每个小方格子的右下角的坐标就可以得到圆心的坐标系了哈..

那么好,接下来咱们来搞一下这个玩意儿.

首先,咱们要得到这九个点的坐标,咱先来个实体类来记录这些坐标的值.那咱就来个Point类.

package text.scc.com.scclock;

/**
 * Created by scc on 2016/11/18.
 */
public class Point {
    /**
     * 咱在这里定义三个常量来表示是普通呢还是按下呢还是错误呢的常量状态
     */
    public static int STATE_NORMAL = 0;
    public static int STATE_PRESS = 1;
    public static int STATE_ERROR = 2;

    /**
     * 咱再在这里定义点的坐标系的值为x和y,通过底下的此类的构造方法来
     * 传进来x和y的值
     */
    public float x;
    public float y;

    /**
     * 咱上面定义了三种状态的常量,咱再来个变量state来记录当下的状态
     * 默认为普通状态
     */
    int state = STATE_NORMAL;

    /**
     * 这是此类构造方法,就不说啥了吧\(^o^)/~
     * @param x
     * @param y
     */
    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }

    /**
     * 这个方法呢后面会用到,是用来监测用户按下屏幕的时候
     * 的坐标点在不在咱们的密码点的距离范围之内.后面咱在说哈
     * @param a
     * @return
     */
    public float distance(Point a) {
        float distance = (float) Math.sqrt((x - a.x) * (x - a.x) + (y - a.y) * (y - a.y));
        return distance;
    }
}

好了,准备工作做好了,该克里马擦的实现绘制了(注: 克里马擦为陕西话,意为赶紧的)

package text.scc.com.scclock;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by scc on 2016/11/18.
 */
public class GestureLock extends View {
    /**
     * 九个坐标点为三行三列的正方形,那就声明为一个二维数组吧
     */
    private Point[][] points = new Point[3][3];

    /**
     * inited是是否为第一次初始化绘制,只需要初始化绘制一次就可以了.
     */
    private boolean inited = false;

    /**
     * 定义三个位图对象,正常和按下和错误(找三张大小一样的图片就好了)
     */
    private Bitmap bitmapPointError;
    private Bitmap bitmapPointNormal;
    private Bitmap bitmapPointPress;

    /**
     * 此为接口,用户是否已经画完了???
     */
    private OnDrawFinishedListener listener;

    /**
     * 初始化paint画笔,后面的参数为消除锯齿
     */
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    /**
     * 初始化绘制按下时的paint画笔
     */
    private Paint pressPaint = new Paint();

    /**
     * 初始化绘制错误时的paint画笔
     */
    private Paint errorPaint = new Paint();

    /**
     * 三个圆形位图对象的半径,不然的话在全屏的时候,位置会出现右下移
     */
    private float bitmapR;

    /**
     * 手指按下时的坐标点的x值和y值
     */
    private float mouseX, mouseY;

    /**
     * 是否在绘制过程中
     */
    private boolean isDraw = false;

    /**
     * 来一个集合存储九个坐标点
     */
    private ArrayList<Point> pointList = new ArrayList();

    /**
     * 来一个集合存储绘制好的图形的样式
     */
    private ArrayList<Integer> passList = new ArrayList<>();

    /**
     * 来一个构造方法
     * @param context
     */
    public GestureLock(Context context) {
        super(context);
    }

    /**
     * 再来一个构造方法
     * @param context
     * @param attrs
     */
    public GestureLock(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 我还来一个构造方法
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public GestureLock(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //如果没有初始化,就先初始化,调用init方法
        if (!inited) {
            init();
        }

        //当咱们初始化完了之后,就该绘制了.咱们定义一个方法叫做drawPoints来绘制点
        drawPoints(canvas);

        if (pointList.size() > 0) {
            Point a = pointList.get(0);
            for (int j = 1; j < pointList.size(); j++) {
                Point b = pointList.get(j);
                drawLine(canvas, a, b);
                a = b;
            }
            if (isDraw) {
                drawLine(canvas, a, new Point(mouseX, mouseY));
            }
        }
    }

    private void drawPoints(Canvas canvas) {
        /**
         * 此处呢咱们需要绘制的是一个二维数组的3*3的图形,双层循环嵌套一下就好
         * 外循环代表行,内循环代表列
         * 好了,开始绘制
         * 此处需要注意的就是要在点的x值和y值要减去自身图片的半径.
         * 原因是因为坐标点呢是图片的左上方开始计算的.减去后咱们就居中了哈,
         */
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
                if (points[i][j].state == Point.STATE_NORMAL) {
                    //normal
                    canvas.drawBitmap(bitmapPointNormal, points[i][j].x - bitmapR, points[i][j].y - bitmapR, mPaint);
                } else if (points[i][j].state == Point.STATE_PRESS) {
                    //press
                    canvas.drawBitmap(bitmapPointPress, points[i][j].x - bitmapR, points[i][j].y - bitmapR, mPaint);
                } else {
                    //error
                    canvas.drawBitmap(bitmapPointError, points[i][j].x - bitmapR, points[i][j].y - bitmapR, mPaint);
                }
            }
        }
    }

    private void init() {

        /**
         * 对按下和错误时的状态的paint进行初始化
         */
        pressPaint.setColor(Color.YELLOW);
        pressPaint.setStrokeWidth(6);
        errorPaint.setColor(Color.RED);
        errorPaint.setStrokeWidth(6);

        /**
         * 将资源drawable图片转换为bitmap位图对象
         */
        bitmapPointError = BitmapFactory.decodeResource(getResources(), R.drawable.error);
        bitmapPointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
        bitmapPointPress = BitmapFactory.decodeResource(getResources(), R.drawable.press);

        /**
         * 因为我的三个图片是一样大的,所以呢半径为任意一个图片的高度除以2即可
         */
        bitmapR = bitmapPointError.getHeight() / 2;


        int width = getWidth();     //获得屏幕的宽度
        int height = getHeight();   //获得屏幕的高度
        int offset = Math.abs(width - height) / 2; //获得横屏和竖屏的偏移量offset
        int offsetX, offsetY;    //水平和垂直的偏移量
        int space;


        if (width > height) {
            //横屏状态下(其中space为每个小正方形的边长)
            space = height / 4;
            offsetX = offset;
            offsetY = 0;

        } else {
            //竖屏状态下
            space = width / 4;
            offsetY = offset;
            offsetX = 0;
        }

        /**
         * 初始化九个点的坐标,横屏书评一起初始化了,这里有图,不然是不是有点乱哈
         */
        points[0][0] = new Point(offsetX + space, offsetY + space);
        points[0][1] = new Point(offsetX + space * 2, offsetY + space);
        points[0][2] = new Point(offsetX + space * 3, offsetY + space);

        points[1][0] = new Point(offsetX + space, offsetY + space * 2);
        points[1][1] = new Point(offsetX + space * 2, offsetY + space * 2);
        points[1][2] = new Point(offsetX + space * 3, offsetY + space * 2);

        points[2][0] = new Point(offsetX + space, offsetY + space * 3);
        points[2][1] = new Point(offsetX + space * 2, offsetY + space * 3);
        points[2][2] = new Point(offsetX + space * 3, offsetY + space * 3);

        /**
         * 初始化之后让inited为true就好了,因为咱只初始化一遍就ok了
         */
        inited = true;
    }

    /**
     * 好了,一切准给工作好了之后呢,咱们就开始来添加事件了.
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mouseX = event.getX();  //获得用户手指按下的x点
        mouseY = event.getY();  //获得用户手指按下的y点
        int[] ij;   //来个数组记录一下
        int i;      //坐标点x值
        int j;      //坐标点y值
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //此方法来恢复重新绘制
                resetPoints();
                //此方法来获得用户点击的点是否在我们绘制的距离之内
                //如果是,设置状态为按下,并添加进两个集合
                //一个集合是来装这点的坐标,一个集合是用来存储用户绘制的密码点
                ij = getSelectPoint();
                if (ij != null) {
                    isDraw = true;
                    i = ij[0];
                    j = ij[1];
                    points[i][j].state = Point.STATE_PRESS;
                    pointList.add(points[i][j]);
                    passList.add(i * 3 + j);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                //判断用户是否重复绘制,然后在一次添加进两个集合
                if (isDraw) {
                    ij = getSelectPoint();
                    if (ij != null) {
                        i = ij[0];
                        j = ij[1];
                        if (!pointList.contains(points[i][j])) {
                            points[i][j].state = Point.STATE_PRESS;
                            pointList.add(points[i][j]);
                            passList.add(i * 3 + j);
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                //如果手指弹起,不绘制,调用接口判断是否和之前一致,不一致,状态为错误
                boolean valid = false;
                if (listener != null && isDraw){
                    valid = listener.onDrawFinished(passList);
                }
                if (!valid){
                    for (Point p : pointList) {
                        p.state = Point.STATE_ERROR;
                    }
                }
                isDraw = false;
                break;
        }
        this.postInvalidate();
        return true;
    }

    private int[] getSelectPoint() {
        Point pMouse = new Point(mouseX, mouseY);
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
                if (points[i][j].distance(pMouse) < bitmapR) {
                    int[] result = new int[2];
                    result[0] = i;
                    result[1] = j;
                    return result;
                }
            }
        }
        return null;
    }

    private void drawLine(Canvas canvas, Point a, Point b) {
        if (a.state == Point.STATE_PRESS) {
            canvas.drawLine(a.x, a.y, b.x, b.y, pressPaint);
        } else if (a.state == Point.STATE_ERROR) {
            canvas.drawLine(a.x, a.y, b.x, b.y, errorPaint);
        }
    }

    public void resetPoints() {
        pointList.clear();
        passList.clear();
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
                points[i][j].state = Point.STATE_NORMAL;
            }
        }
        this.postInvalidate();
    }

    public interface OnDrawFinishedListener {
        boolean onDrawFinished(List<Integer> passList);
    }

    public void setOnDrawFinishedListener(OnDrawFinishedListener listener){
        this.listener = listener;
    }
}

这里写图片描述

好了,到这里基本就ok了,图片的话我就不上了,因为我不会录制GIF图片,哈哈,有兴趣的各位工友的话,可以复制代码自己试一个demo就可以了..当然我也是个渣渣,感谢OPEN中的一个大神的此段代码的开发,感谢岗位工友的阅读,套用我们公司的一句话:”互帮互助,成就你我”,最后送各位工友一个程序猿段子,毕竟每天开发和维护工作我们都是很累的,relax一下.

女神:你能让这个论坛的人都吵起来,我今晚就跟你走。
程序猿:PHP语言是最好的语言!论坛炸锅了,各种吵架。
女神:服了你了,我们走吧,你想干啥都行。
程序猿:今天不行,我一定要说服他们,PHP语言是最好的语言。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值