国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義View九宮格手勢密碼解鎖

2019-10-21 21:44:37
字體:
來源:轉載
供稿:網友

由于公司新的項目需要用到九宮格手勢密碼解鎖的功能,于是覺得自己寫一個。廢話不多說,直接上效果圖:

Android,View,九宮格,手勢密碼

首選我們來分析下實現的思路: 

1. 繪制出相對于這個View的居中的九個點,作為默認狀態的點 
2. 點擊屏幕的時候是否點擊在這九個點上 
3. 在屏幕上滑動的時候,繪制兩個點之間的線條,以及選中狀態的點 
4. 手指離開屏幕的時候判斷手勢密碼是否正確,如若錯誤這把錯誤狀態下的點和線繪制出來。

具體實現: 

首先我們得繪制出默認正常狀態下的九個點: 

Android,View,九宮格,手勢密碼

/** * 點的bean * Created by Administrator on 2015/9/21. */public class Point { // 正常狀態 public static final int STATE_NORMAL = 1; // 按下狀態 public static final int STATE_PRESS = 2; // 錯誤狀態 public static final int STATE_ERROR = 3; float x; float y; int state = STATE_NORMAL; public Point(float x, float y){  this.x = x;  this.y = y; } /**  * 計算兩點間的距離  * @param a 另外一個點  * @return  */ public float getInstance(Point a){  return (float) Math.sqrt((x-a.x)*(x-a.x) + (y-a.y)*(y-a.y)); }}

可以看到,給一個點定義了x、y值以及這個點的狀態有三種,默認的初始狀態是正常的狀態。

@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); init();// 初始化正常狀態下的九個點,以及三種狀態所需要用到的圖片資源}private void init() {  mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  mPressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  mErrorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  // 按下狀態的畫筆  mPressPaint.setColor(Color.parseColor("#00B7EE"));  mPressPaint.setStrokeWidth(7);  // 錯誤狀態的畫筆  mErrorPaint.setColor(Color.parseColor("#FB0C13"));  mErrorPaint.setStrokeWidth(7);  // 加載三種狀態圖片  mNormalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_point_normal);  mPressBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_point_press);  mErrorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_point_error);  mPointRadius = mNormalBitmap.getWidth() / 2;  // 當前視圖的大小  int width = getWidth();  int height = getHeight();  // 九宮格點的偏移量  int offSet = Math.abs(width - height) / 2;  // x、y軸上的偏移量  int offSetX = 0, offSetY = 0;  int pointItemWidth = 0; // 每個點所占用方格的寬度  if (width > height){ // 橫屏的時候   offSetX = offSet;   offSetY = 0;   pointItemWidth = height / 4;  }  if (width < height){ // 豎屏的時候   offSetX = 0;   offSetY = offSet;   pointItemWidth = width / 4;  }  // 初始化九個點  mPoints[0][0] = new Point(offSetX + pointItemWidth, offSetY + pointItemWidth);  mPoints[0][1] = new Point(offSetX + pointItemWidth * 2, offSetY + pointItemWidth);  mPoints[0][2] = new Point(offSetX + pointItemWidth * 3, offSetY + pointItemWidth);  mPoints[1][0] = new Point(offSetX + pointItemWidth, offSetY + pointItemWidth * 2);  mPoints[1][1] = new Point(offSetX + pointItemWidth * 2, offSetY + pointItemWidth * 2);  mPoints[1][2] = new Point(offSetX + pointItemWidth * 3, offSetY + pointItemWidth * 2);  mPoints[2][0] = new Point(offSetX + pointItemWidth, offSetY + pointItemWidth * 3);  mPoints[2][1] = new Point(offSetX + pointItemWidth * 2, offSetY + pointItemWidth * 3);  mPoints[2][2] = new Point(offSetX + pointItemWidth * 3, offSetY + pointItemWidth * 3); }

這段代碼要注意的是橫豎屏的偏移量,橫屏的時候計算X軸的偏移量,豎屏的時候計算Y軸的偏移量。計算出x y的偏移量后,來初始化九個點的位置。我們要讓九宮格的點繪制的位置在 當前這個自定義視圖View的正中間,那么如上圖顯示,第一個點的起始點就是x = x軸的偏移量 + 格子寬度, y = y軸的偏移量 + 格子寬度。以此可見第二列的點的x值 = 兩個格子的寬度 + x軸的偏移量,同理第二行的點的y值 = 兩個格子的寬度 + y周的偏移量。以此類推初始化九個點的位置。 

九個點的位置初始化后,我們需要來繪制九個點,這里我用了三種狀態的圖片來作為頂點。在init()方法中,初始化了三種bitmap圖片對象。以及計算了點的半徑也就是圖片的一半,當然我這里的三張圖片大小是一樣的。如果不一樣,還是要重新計算過。 

接下來就在onDraw方法里繪制出九個點:

@Override protected void onDraw(Canvas canvas) {  // 繪制點  drawPoints(canvas);  // 繪制連線  drawLines(canvas); } /**  * 繪制所有的點  * @param canvas  */ private void drawPoints(Canvas canvas){  for (int i = 0; i < mPoints.length; i++){   for (int j = 0; j < mPoints[i].length; j++){    Point point = mPoints[i][j];    // 不同狀態繪制點    switch (point.state){     case Point.STATE_NORMAL:      canvas.drawBitmap(mNormalBitmap, point.x - mPointRadius, point.y - mPointRadius, mPaint);      break;     case Point.STATE_PRESS:      canvas.drawBitmap(mPressBitmap, point.x - mPointRadius, point.y - mPointRadius, mPaint);      break;     case Point.STATE_ERROR:      canvas.drawBitmap(mErrorBitmap, point.x - mPointRadius, point.y - mPointRadius, mPaint);      break;    }   }  } }

我們變量初始化好的九個點對象的狀態,不同狀態繪制不同的圖片。這里繪制的時候要注意初始化點的時候的x、y值是包括了點圓的半徑的,而繪制圖片又是從左上角開始的,所以在繪制的時候需要減去圖片本身的半徑。

繪制后默認的九個點后,我們接下來處理手勢滑動,覆寫onTouchEvent方法:

 /**  * 獲取選擇的點的位置  * @return  */ private int[] getSelectedPointPosition(){  Point point = new Point(mX, mY);  for (int i = 0; i < mPoints.length; i++) {   for (int j = 0; j < mPoints[i].length; j++) {    // 判斷觸摸的點和遍歷的當前點的距離是否小于當個點的半徑    if(mPoints[i][j].getInstance(point) < mPointRadius){     // 小于則獲取作為被選中,并返回選中點的位置     int[] result = new int[2];     result[0] = i;     result[1] = j;     return result;    }   }  }  return null; }

首先我們要判斷手指點擊的位置是否是在點上,獲取屏幕觸摸的點的位置mX、mY,初始化一個點,然后遍歷所有的點與觸摸點的距離 是否 小于 一個點的圖片的半徑,如果小于表示觸摸的位置在這九個點中的一個上。getInstance(point)是計算兩點之間的距離的方法。公式是:distance = Math.sqrt((x1 - x2)(x1 - x2) + (y1 - y2) (y1 - y2))。如果是觸摸的位置在點上,那就返回這個點的在九宮格數組中的下標位置。

我們來看onTouchEvent方法:

@Override public boolean onTouchEvent(MotionEvent event) {  // 獲取手指觸摸的xy位置  mX = event.getX();  mY = event.getY();  int[] position;  int i, j;  switch (event.getAction()){   case MotionEvent.ACTION_DOWN:    // 重置所有的點    resetPoints();    // 獲取選擇的點的位置    position = getSelectedPointPosition();    if (position != null){     isDraw = true; // 標記為繪制狀態     i = position[0];     j = position[1];     mPoints[i][j].state = Point.STATE_PRESS;     // 被選擇的點存入一個集合中     mSelectedPoints.add(mPoints[i][j]);     mPassPositions.add(i * 3 + j); // 把選中的點的路徑轉換成一位數組存儲起來    }    break;   case MotionEvent.ACTION_MOVE:    if (isDraw){     position = getSelectedPointPosition();     if (position != null){      i = position[0];      j = position[1];      if (!mSelectedPoints.contains(mPoints[i][j])){       mPoints[i][j].state = Point.STATE_PRESS;       mSelectedPoints.add(mPoints[i][j]);       mPassPositions.add(i * 3 + j);      }     }    }    break;   case MotionEvent.ACTION_UP:    // 標記為不在繪制    isDraw = false;    break;  }  // 更新繪制視圖  invalidate();  return true; }

按下的時候堅持到觸摸的位置就在點上的時候,就把這個點的狀態改成被按下的狀態,同時存入到mSelectedPoints被選中點的集合中。并標記現在是繪制的狀態下。同樣在移動手指的時候也是把檢測到的點存儲起來,修改狀態為按下。當手指離開屏幕的時候,把標記改為不在繪制。然后通過invalidate()方法更新視圖(會去調用onDraw方法繪制)。 
通過上面的步驟,我們把選中的點都收集了起來,接下來就是繪制兩個點連接線:

/**  * 繪制兩點之間的線  * @param canvas  * @param a  * @param b*/ 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, mPressPaint);  }  if (a.state == Point.STATE_ERROR){   canvas.drawLine(a.x, a.y, b.x, b.y, mErrorPaint);  } }

繪制兩點的連接線比較簡單,我們只繪制按下和錯誤時候的連線。這是繪制單條連接線的。而我們的手勢密碼路徑是有多條的,繼續看:

 /**  * 繪制所有的線  * @param canvas  */ private void drawLines(Canvas canvas){  if (mSelectedPoints.size() > 0){   // 從第一個被選中的點開始繪制   Point a = mSelectedPoints.get(0);   for (int i = 1; i < mSelectedPoints.size(); i++){    Point b = mSelectedPoints.get(i);    drawLine(canvas, a, b); // 連接兩個點    a = b; // 把下一個點作為下一次繪制的第一個點   }   if (isDraw){// 如果還在繪制狀態,那就繼續繪制連接線    drawLine(canvas, a, new Point(mX, mY));   }  } }

如果被選中點的集合不是空的,那我們選擇從第一個被選中點開始繪制連接線,遍歷所有被選中點的時候就要從第二個點開始也就是index為1的時候,繪制完一個點,就要把下一次繪制連接線的起點改為這一次的連接線的終點,也是 a=b;這句的作用。所有被選中的點繪制完后,如果當前還處在繪制狀態(手機沒有離開屏幕),那我們就new一個手指觸摸位置作為連接線的終點。好了所有的線都繪制完了,那我們只要在onDraw調用就好了:

@Override protected void onDraw(Canvas canvas) {  // 繪制點  drawPoints(canvas);  // 繪制連線  drawLines(canvas); }

這樣繪制的工作基本就完成了,接下來我們需要做一個用來監聽手勢滑動完后的接口:

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

回調的方法里的passPositions是手勢滑動的時候存儲的九宮格的路徑,對于九宮格路徑的定義如圖:

Android,View,九宮格,手勢密碼

在onTouchEvent中,當Action動作是Up的時候(手指離開屏幕):就會觸發手勢密碼繪制完成的接口:

case MotionEvent.ACTION_UP:    boolean valid = false;    if (mListener != null && isDraw){     // 獲取繪制路徑是否正確     valid = mListener.onDrawFinished(mPassPositions);    }    if (!valid){// 判斷繪制路徑不正確的所有被選中的點的狀態改為出錯     for (Point p: mSelectedPoints){      p.state = Point.STATE_ERROR;     }    }    isDraw = false;    break;

當設置了監聽接口,并且還處于繪制狀態的時候,回調接口把路徑傳出去給實現這個接口的使用者,然后在實現這個接口方法的地方判斷和之前設置存儲的手勢密碼是否一致,如果不一致返回為false。然后去修改所有的被選中的點的狀態為錯誤的。然后invalidate()去更新視圖。 

路徑給出去了,在最初設定的時候可以用md5等不可逆的加密方式存儲在手機中。在需要解鎖的時候,拿到這個md5值和解鎖時候繪制的路徑的md5值做比較就可以了:

// 這個自定義視圖的使用方法:<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"  android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.jerry.testproject.ui.ScreenLockActivity"> <com.jerry.testproject.widget.lockview.LockView  android:id="@+id/lockView"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:background="#2B2B2B"  /></FrameLayout>LockView lockView = (LockView) findViewById(R.id.lockView);  lockView.setOnDrawFinishedListener(new LockView.OnDrawFinishedListener() {   @Override   public boolean onDrawFinished(List<Integer> passPositions) {    StringBuilder sb = new StringBuilder();    for (Integer i :      passPositions) {     sb.append(i.intValue());    }    // 把字符串md5    String md5Str = MD5Utils.getMD5String(sb.toString());    // 比較路徑是否一致    boolean valid = comparePath(sb.toString());    if (valid){     Toast.makeText(ScreenLockActivity.this, "手勢密碼正確!", Toast.LENGTH_SHORT).show();    } else {     Toast.makeText(ScreenLockActivity.this, "手勢密碼錯誤,請重試!", Toast.LENGTH_SHORT).show();    }    return valid;   }  });

至此自定義九宮格手勢密碼View介紹就結束了。

下面附上控件的源碼和所用到的資源:Android九宮格手勢密碼解鎖

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 昌黎县| 车险| 天等县| 和平县| 台北市| 泗阳县| 辽中县| 郴州市| 加查县| 报价| 台东市| 孟连| 江山市| 伽师县| 乡宁县| 桦南县| 阿鲁科尔沁旗| 武义县| 黔东| 鸡东县| 措美县| 伊通| 道孚县| 黄浦区| 青铜峡市| 江安县| 张家川| 嘉峪关市| 五大连池市| 应城市| 阳春市| 海丰县| 霍邱县| 灵宝市| 钟山县| 永康市| 大竹县| 昌宁县| 故城县| 黄石市| 东乡族自治县|