«

Android 手势密码

时间:2024-3-2 17:00     作者:韩俊     分类: Android


最近看了慕课网一老师的视频,关于手势密码的研究,挺不错的,不过没上传源码,还有就是旋转角度的计算个人感觉不太好,于是整理出源代码如下:

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

import mg.lanyan.ui.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

@SuppressLint({ "DrawAllocation", "ClickableViewAccessibility" })
public class LockPaternView extends View{

    public boolean isOnTouch=false;
    private int mScreenWidth;
    private int mScreenHeight;
    /**九宫格的点集合*/
    private Point [][] pointArray=new Point[3][3];
    /**避免每次都初始化点*/
    private boolean isFirst;
    /**X轴的偏移量*/
    private float offsetX;
    /**Y轴的偏移量*/
    private float offsetY;
    /**所需要的图片资源id*/
    private int normal=R.drawable.nor,press=R.drawable.press,error=R.drawable.error,linePress=R.drawable.linepress,lineError=R.drawable.lineerror;
    /**通过资源id得到的图片Bitmap*/
    private Bitmap mBitmapNormal,mBitmapPress,mBitmapError,mBitmapLinePress,mBitmapLineError;
    /**绘制图案画笔*/
    private Paint mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);;
    /**图案半径*/
    private int mRadioR;
    /**存储按下的点集合*/
    private List<Point> pointList=new ArrayList<Point>();

    private float mCurrx,mCurrY;
    /**是否选择*/
    private boolean isSelect;
    /**是否继续绘制*/
    private boolean isMovePoint;
    /**是否结束*/
    private boolean isFinished;
    /**用于缩放测量的矩阵*/
    private Matrix matrix=new Matrix();

    private int STATUS_PASSWORD=0;
    private int STATUS_PASSWORD_OK=0;
    private int STATUS_PASSWORD_ERROR=1;

    public LockPaternView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub

    }

    public LockPaternView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
        // TODO Auto-generated constructor stub
    }

    public LockPaternView(Context context) {
        this(context,null);
        // TODO Auto-generated constructor stub
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        isOnTouch=true;
        isMovePoint=false;
        isFinished=false;
        int action=event.getAction();
        mCurrx=event.getX();
        mCurrY=event.getY();

        Point mPointIntersection=null;
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            resetPointList();
            mPointIntersection=checkPoint();
            if(mPointIntersection!=null){
                isSelect=true;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if(isSelect){
                mPointIntersection=checkPoint();
                if(mPointIntersection==null){
                    isMovePoint=true;
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            isFinished=true;
            isSelect=false;
            isOnTouch=false;
            break;
        default:
            break;
        }

        //手势没有结束
        if(!isFinished&&isSelect&&mPointIntersection!=null){
            if(crossPoint(mPointIntersection)){
                isMovePoint=true;
            }else{
                mPointIntersection.status=Point.STATU_PRESS;
                pointList.add(mPointIntersection);
            }
        }
        //手势结束
        if(isFinished){
            if(pointList.size()<=4&&pointList.size()>=2){
                //绘制错误
                errorPoint();
                STATUS_PASSWORD=STATUS_PASSWORD_ERROR;
            }else if(pointList.size()<=1){
                resetPointList();//绘制不成立
            }else{
                STATUS_PASSWORD=STATUS_PASSWORD_OK;
            }
        }
        postInvalidate();

        if(isFinished&&listener!=null){
            if(STATUS_PASSWORD==STATUS_PASSWORD_ERROR){
                listener.onFail();
            }else if(STATUS_PASSWORD==STATUS_PASSWORD_OK){
                String mPassword=getPassword();
                listener.onSucceed(mPassword);
            }
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        if(!isFirst){
            initPoint();
        }
        pointToCanvas(canvas);

        if(pointList.size()>0){
            Point a=pointList.get(0);
            for (int i = 0; i < pointList.size(); i++) {
                Point b=pointList.get(i);
                lineToCanvas(canvas, a, b);
                a=b;
            }

            if(isMovePoint){
                lineToCanvas(canvas, a, new Point(mCurrx,mCurrY));
            }
        }
    }
    /*************************************Method****************************************/
    /**
     * 求两点之间的夹角
     * @param px1
     * @param py1
     * @param px2
     * @param py2
     * @return
     */
    public static float getAngle(float px1, float py1, float px2, float py2) {  
        // 两点的x、y值   
        float x = px2 - px1;  
        float y = py2 - py1;  
        double hypotenuse = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));  
        // 斜边长度   
        double cos = x / hypotenuse;  
        double radian = Math.acos(cos);  
        // 求出弧度   
        float angle = (float) (180 / (Math.PI / radian));  
        // 用弧度算出角度   
        if (y < 0) {  
            angle = 180 + (180 - angle);  
        } else if ((y == 0) && (x < 0)) {  
            angle = 180;  
        }else if(x==0&&y==0){
            angle=0;
        }  
        return angle;  
    } 
    /**
     * 绘制线条
     * @param canvas
     * @param a
     * @param b
     */
    public void lineToCanvas(Canvas canvas,Point a,Point b){
        float scaleX=(float) getDistance(a, b)/mBitmapLinePress.getWidth();
        float mAngle=getAngle(a.x, a.y, b.x, b.y);
        canvas.rotate(mAngle,a.x,a.y);
        if(a.status==Point.STATU_PRESS){
            matrix.setScale(scaleX, 1);
            matrix.postTranslate(a.x-mBitmapLinePress.getWidth()/2, a.y-mBitmapLinePress.getHeight()/2);//偏移
            canvas.drawBitmap(mBitmapLinePress, matrix, mPaint);
        }else{
            matrix.setScale(scaleX, 1);
            matrix.postTranslate(a.x-mBitmapLineError.getWidth()/2, a.y-mBitmapLineError.getHeight()/2);//偏移
            canvas.drawBitmap(mBitmapLineError, matrix, mPaint);
        }
        canvas.rotate(-mAngle,a.x,a.y);
    }
    /**
     * 判断是否是交叉点
     * @param point
     * @return
     */
    public boolean crossPoint(Point point){
        if(pointList.contains(point)){
            return true;
        }else{
            /*point.status=Point.STATU_PRESS;
            pointList.add(point);*/
            return false;
        }
    }
    public void resetPointList(){

        if(pointList.size()>0){
            for (int i = 0; i < pointList.size(); i++) {
                Point point=pointList.get(i);
                point.status=Point.STATU_NORMAL;
            }
        }
        pointList.clear();
    }
    public void errorPoint(){
        for(Point point:pointList){
            point.status=Point.STATU_ERROR;
        }
    }
    /***
     * 检查点是否和九宫格的点有交集
     */
    private Point checkPoint(){
        for (int i = 0; i < pointArray.length; i++) {
            for (int j = 0; j < pointArray[i].length; j++) {
                Point point=pointArray[i][j];
                if(isIntersection(point, new Point(mCurrx,mCurrY), mRadioR)){
                    return point;
                }

            }
        }

        return null;
    }
    /**
     * 初始化图案的点
     */
    private void initPoint(){
        mScreenWidth=getWidth();
        mScreenHeight=getHeight();
        //横屏
        if(mScreenWidth>mScreenHeight){
            offsetX=(mScreenWidth-mScreenHeight)/2;
            //正方形屏幕锁
            mScreenWidth=mScreenHeight;
        }
        //竖屏
        else{
            offsetY=(mScreenHeight-mScreenWidth)/2;
            mScreenHeight=mScreenWidth;
        }

        mBitmapNormal=BitmapFactory.decodeResource(getResources(), normal);
        mBitmapPress=BitmapFactory.decodeResource(getResources(), press);
        mBitmapError=BitmapFactory.decodeResource(getResources(), error);

        mBitmapLinePress=BitmapFactory.decodeResource(getResources(), linePress);
        mBitmapLineError=BitmapFactory.decodeResource(getResources(), lineError);

        pointArray[0][0]=new Point(offsetX+mScreenWidth/4,offsetY+mScreenWidth/4);
        pointArray[0][1]=new Point(offsetX+mScreenWidth/2,offsetY+mScreenWidth/4);
        pointArray[0][2]=new Point(offsetX+mScreenWidth-mScreenWidth/4,offsetY+mScreenWidth/4);

        pointArray[1][0]=new Point(offsetX+mScreenWidth/4,offsetY+mScreenWidth/2);
        pointArray[1][1]=new Point(offsetX+mScreenWidth/2,offsetY+mScreenWidth/2);
        pointArray[1][2]=new Point(offsetX+mScreenWidth-mScreenWidth/4,offsetY+mScreenWidth/2);

        pointArray[2][0]=new Point(offsetX+mScreenWidth/4,offsetY+mScreenWidth-mScreenWidth/4);
        pointArray[2][1]=new Point(offsetX+mScreenWidth/2,offsetY+mScreenWidth-mScreenWidth/4);
        pointArray[2][2]=new Point(offsetX+mScreenWidth-mScreenWidth/4,offsetY+mScreenWidth-mScreenWidth/4);

        mRadioR=mBitmapNormal.getWidth()/2;

        int index=1;
        for(Point[] point:pointArray){
            for(Point mp:point){
                mp.index=index;
                index++;
            }
        }
        isFirst=true;
    }

    /**
     * 把点集合绘制到画布上
     */
    private void pointToCanvas(Canvas canvas){
        for (int i = 0; i < pointArray.length; i++) {
            for (int j = 0; j < pointArray[i].length; j++) {
                Point mPoint=pointArray[i][j];
                if(mPoint.status==Point.STATU_NORMAL){
                    canvas.drawBitmap(mBitmapNormal, mPoint.x-mRadioR, mPoint.y-mRadioR, mPaint);
                }else if(mPoint.status==Point.STATU_PRESS){
                    canvas.drawBitmap(mBitmapPress, mPoint.x-mRadioR, mPoint.y-mRadioR, mPaint);
                }else if(mPoint.status==Point.STATU_ERROR){
                    canvas.drawBitmap(mBitmapError, mPoint.x-mRadioR, mPoint.y-mRadioR, mPaint);
                }
            }
        }
    }

    /***
     * 获取手势密码
     * @return
     */
    private String getPassword() {
        // TODO Auto-generated method stub
        String mPassword="";
        for (int i = 0; i < pointList.size(); i++) {
            Point p=pointList.get(i);
            mPassword+=String.valueOf(p.index);
            /*for (int k = 0; k < pointArray.length; k++) {
                for (int j = 0; j < pointArray[k].length; j++) {
                    Point mp=pointArray[k][j];
                    if(p==mp){
                        mPassword+=k+""+j;
                    }
                }
            }*/
        }

        return mPassword;
    }
    /**
     * 图案锁的点
     * @author Administrator
     *
     */
    public static class Point{
        /**图案锁的三种状态:正常状态*/
        public static int STATU_NORMAL=0;
        /**图案锁的三种状态:按下状态*/
        public static int STATU_PRESS=1;
        /**图案锁的三种状态:错误状态*/
        public static int STATU_ERROR=2;
        /**图案的x.y的点坐标*/
        public float x;
        public float y;

        public int index,status;

        public Point(){

        }
        public Point(float x,float y){
            this.x=x;
            this.y=y;
        }

    }
    /**
     * 计算两点之间的距离
     * @param a
     * @param b
     * @return
     */
    public static double getDistance(Point a,Point b){
        return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    /**
     * 判断是否有交集
     * @param a
     * @param b
     * @param r
     * @return
     */
    public static boolean isIntersection(Point a,Point b,float r){
        return getDistance(a, b)<r;
    }

    public interface OnLockPatternListener{
        void onFail();
        void onSucceed(String password);
    }
    private OnLockPatternListener listener;
    public void setListener(OnLockPatternListener listener) {
        this.listener = listener;
    }

}
上面是自定义控件,主要用法: 布局引入view,activity 或者Fragment给控件设置监听回调函数判断。

主要用到的方法如下:


Handler handler = new Handler();

    public void updateLockPatern() {
        handler.postDelayed(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                if (!mLockPatern.isOnTouch) {
                    mLockPatern.resetPointList();
                    mLockPatern.postInvalidate();
                }
            }
        }, 1000);
    }

@Override
    public void onFail() {
        // TODO Auto-generated method stub
        super.onFail();
        mLockToast.setText("手势密码连接最少5个点");
    }

    @Override
    public void onSucceed(String password) {
        // TODO Auto-generated method stub
        super.onSucceed(password);
        if (BaseFragmentActivity.mLockPatern.getLock().equals(password)) {
               Intent intent=new Intent(getActivity(),APIClass.mLockLogin);
               startActivity(intent);
               getActivity().finish();
        } else {
           mLockToast.setText("密码错误,请重新绘制");
           mLockPatern.errorPoint();
           mLockPatern.postInvalidate();
           updateLockPatern();
        }
    }

该项目需要资源文件:

nor.png ,press.png,error.png,linepress.png,lineerror.png

App接入后要考虑手势密码的几种情况: A .启动应用,如果有手势密码需要输入手势密码

B.创建手势密码

C.修改手势密码

D.onResume 生命周期监听屏幕开关时间间隔判断弹出手势密码界面,.

个人觉得开发用BaseActivity 提供registerReceiver,LockPaternActivity extends FragmentActivity 嵌套四个Fragmnent,根据Intent传入参数选择Fragment,我写了demo不过没进行手势加密,就咋不上传了。

标签: android

热门推荐