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不过没进行手势加密,就咋不上传了。   

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。