編輯:關於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 ListpointList=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不過沒進行手勢加密,就咋不上傳了。
Android系統有五種數據存儲形式,分別是文件存儲、SP存儲、數據庫存儲、contentprovider 內容提供者、網絡存儲。其中,前四個是本地存儲。存儲的類型包括簡
Android的媒體效果框架允許開發者可以很容易的應用多種令人印象深刻的視覺效果到照片或視頻之上。作為這個媒體效果的框架,它使用GPU來處理圖片處理的過程,它僅僅接收Op
這裡記錄一個比較方便的方式來解決Textview設置不同顏色的字體的方法。可能第一反應是布局的嵌套,這個方法肯定可以啊,但是肯定不推薦啊,布局要盡量減少布局的嵌套,其次,
QQ厘米秀是騰訊手機QQ推出的全新功能玩法,厘米秀添加了讓人眼前一亮的人物動作互動功能,用戶可以通過手機QQ厘米秀的窗口與好友互動。還有一個故事卡線索功能,