Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 手勢密碼

Android 手勢密碼

編輯:關於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 pointList=new ArrayList();

	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)上面是自定義控件,主要用法: 布局引入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不過沒進行手勢加密,就咋不上傳了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved