Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 簡易手勢密碼開源庫詳解

Android 簡易手勢密碼開源庫詳解

編輯:關於Android編程

簡介

本文介紹一個Android手勢密碼開源庫的使用及實現的詳細過程,該開源庫主要實現以下幾個功能:

  1. 支持手勢密碼的繪制,並支持密碼保存功能,解鎖時自動比對密碼給出結果
  2. 封裝了繪制密碼的方法,比對兩次密碼是否一致,可以快捷地進行手勢密碼的設置
  3. 可以設置密碼輸入錯誤後的重試次數上限
  4. 可以自定義不同狀態下手勢密碼圖案的顏色
  5. 可以自定義手勢密碼的觸摸點數量(n*n)

最近需要用到手勢密碼解鎖功能,找了一些demo感覺用起來都有點麻煩,於是參考一些文章自己造了下輪子,封裝了相關的一些方法,使用起來比較便捷。

github鏈接如下,覺得還可以請幫忙star支持下~

github鏈接    個人博客

使用效果

首先看下使用效果:

使用方法

XML布局文件中使用該控件

<com.syd.oden.gesturelock.view.GestureLockViewGroup
  android:id="@+id/gesturelock"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:preference_id="1"
  android:layout_marginTop="30dp"
  app:count="3" />

可以設置的一些參數,說明如下:

  1. color_no_finger:未觸摸時圓形的顏色
  2. color_finger_on:觸摸時圓形的顏色
  3. color_finger_up_correct:輸入正確時圓形的顏色
  4. color_finger_up_error:出錯時圓形的顏色
  5. count:收拾密碼的圓形數量,n*n
  6. preference_id:手勢密碼保存的id號,不輸入或輸入-1則使用默認的id

初始化

private void initGesture() {
  mGestureLockViewGroup = (GestureLockViewGroup) findViewById(R.id.gesturelock);
  gestureEventListener();
  gesturePasswordSettingListener();
  gestureRetryLimitListener();
 }

設置手勢密碼監聽事件

private void gestureEventListener() {
  mGestureLockViewGroup.setGestureEventListener(new GestureEventListener() {
   @Override
   public void onGestureEvent(boolean matched) {
    mylog.d("onGestureEvent matched: " + matched);
    if (!matched) {
     tv_state.setTextColor(Color.RED);
     tv_state.setText("手勢密碼錯誤");
    } else {
     if (isReset) {
      isReset = false;
      Toast.makeText(MainActivity.this, "清除成功!", Toast.LENGTH_SHORT).show();
      resetGesturePattern();
     } else {
      tv_state.setTextColor(Color.WHITE);
      tv_state.setText("手勢密碼正確");
     }
    }
   }
  });
 }

若已經設置有密碼則會進入該回調,在這裡對結果進行處理,上面的例子中加入了一個重設密碼的處理。

手勢密碼設置

private void gesturePasswordSettingListener() {
  mGestureLockViewGroup.setGesturePasswordSettingListener(new GesturePasswordSettingListener() {
   @Override
   public boolean onFirstInputComplete(int len) {
    if (len > 3) {
     tv_state.setTextColor(Color.WHITE);
     tv_state.setText("再次繪制手勢密碼");
     return true;
    } else {
     tv_state.setTextColor(Color.RED);
     tv_state.setText("最少連接4個點,請重新輸入!");
     return false;
    }
   }

   @Override
   public void onSuccess() {
    tv_state.setTextColor(Color.WHITE);
    Toast.makeText(MainActivity.this, "密碼設置成功!", Toast.LENGTH_SHORT).show();
    tv_state.setText("請輸入手勢密碼解鎖!");
   }

   @Override
   public void onFail() {
    tv_state.setTextColor(Color.RED);
    tv_state.setText("與上一次繪制不一致,請重新繪制");
   }
  });
 }

若還未設置密碼,繪制手勢的時候會進入該回調,返回值為繪制的觸摸點的數量,onFirstInputComplete中返回true則進入第二手勢密碼的繪制,兩次輸入一致後自動保存密碼。

重試次數超過限制監聽

private void gestureRetryLimitListener() {
  mGestureLockViewGroup.setGestureUnmatchedExceedListener(3, new GestureUnmatchedExceedListener() {
   @Override
   public void onUnmatchedExceedBoundary() {
    tv_state.setTextColor(Color.RED);
    tv_state.setText("錯誤次數過多,請稍後再試!");
   }
  });
 }

若設置了該監聽事件,則輸入錯誤有次數限制,超過上限後進入回調,在該回調中進行處理。

清除密碼的邏輯自己加個判斷處理下即可,具體可以看下github上的demo

其他的一些API

public void removePassword() :清除密碼
public void savePassword() : 保存密碼,設置手勢密碼成功後會自動保存,也可以調用該接口另外設置密碼
public void getPassword(): 獲取密碼
public void setRetryTimes(int retryTimes) : 設置重試次數上限
public boolean isSetPassword() : 返回現在是否已經設置有密碼
public void resetView() : 將視圖Reset

在項目中導入該庫

僅需加入兩行代碼:

在工程的 build.gradle中加入:

allprojects {
  repositories {
   ...
   maven { url "https://jitpack.io" }
  }
 }

module的build.gradle中加入依賴:

dependencies {
   compile 'com.github.autume:GestureLock:1.0.0'
 }

總的使用就是這樣,是不是很簡單!

具體實現過程

下面講下實現的過程,如果只是直接拿來用的話也可以略過這部分。

自定義手勢密碼的圓形view

這部分主要參考Hongyang大大的博客,稍微修改了一下

初始化傳入參數

public GestureLockView(Context context, int colorNoFingerr, int colorFingerOn, int colorCorrect, int colorError) {
  super(context);
  this.mColorNoFinger = colorNoFingerr;
  this.mColorFingerOn = colorFingerOn;
  this.mColorFingerUpCorrect = colorCorrect;
  this.mColorFingerUpError = colorError;
  mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mArrowPath = new Path();
 }

根據不同的觸摸狀態繪制不同顏色的圓

@Override
 protected void onDraw(Canvas canvas) {

  switch (mCurrentStatus) {
   case STATUS_FINGER_ON:
    // 繪制外圓
    mPaint.setStyle(Style.STROKE);
    mPaint.setColor(mColorFingerOn);
    mPaint.setStrokeWidth(2);
    canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
    // 繪制內圓
    mPaint.setStyle(Style.FILL);
    canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);
    break;
   case STATUS_FINGER_UP:
    // 繪制外圓
    if (GestureLockViewGroup.isCorrect)
     mPaint.setColor(mColorFingerUpCorrect);
    else
     mPaint.setColor(mColorFingerUpError);
    mPaint.setStyle(Style.STROKE);
    mPaint.setStrokeWidth(2);
    canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
    // 繪制內圓
    mPaint.setStyle(Style.FILL);
    canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);
    drawArrow(canvas);
    break;
   case STATUS_NO_FINGER:
    // 繪制外圓
    mPaint.setStyle(Style.STROKE);
    mPaint.setColor(mColorNoFinger);
    canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
    // 繪制內圓
    mPaint.setStyle(Style.FILL);
    mPaint.setColor(mColorNoFinger);
    canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);
    break;
  }
 }

繪制箭頭

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);

  mWidth = MeasureSpec.getSize(widthMeasureSpec);
  mHeight = MeasureSpec.getSize(heightMeasureSpec);

  // 取長和寬中的小值
  mWidth = mWidth < mHeight ? mWidth : mHeight;
  mRadius = mCenterX = mCenterY = mWidth / 2;
  mRadius -= mStrokeWidth / 2;

  // 繪制三角形,初始時是個默認箭頭朝上的一個等腰三角形,用戶繪制結束後,根據由兩個GestureLockView決定需要旋轉多少度
  float mArrowLength = mWidth / 2 * mArrowRate;
  mArrowPath.moveTo(mWidth / 2, mStrokeWidth + 2);
  mArrowPath.lineTo(mWidth / 2 - mArrowLength, mStrokeWidth + 2 + mArrowLength);
  mArrowPath.lineTo(mWidth / 2 + mArrowLength, mStrokeWidth + 2 + mArrowLength);
  mArrowPath.close();
  mArrowPath.setFillType(Path.FillType.WINDING);
 }

 private void drawArrow(Canvas canvas) {
  if (mArrowDegree != -1) {
   mPaint.setStyle(Paint.Style.FILL);

   canvas.save();
   canvas.rotate(mArrowDegree, mCenterX, mCenterY);
   canvas.drawPath(mArrowPath, mPaint);
   canvas.restore();
  }
 }

自定義手勢密碼的ViewGroup

加入自定義view的屬性

<?xml version="1.0" encoding="utf-8"?>
<resources>

 <attr name="color_no_finger" format="color" />
 <attr name="color_finger_on" format="color" />
 <attr name="color_finger_up_correct" format="color" />
 <attr name="color_finger_up_error" format="color" />
 <attr name="count" format="integer" />
 <attr name="preference_id" format="integer" />

 <declare-styleable name="GestureLockViewGroup">
  <attr name="color_no_finger" />
  <attr name="color_finger_on" />
  <attr name="color_finger_up_correct" />
  <attr name="color_finger_up_error" />
  <attr name="count" />
  <attr name="preference_id" />
 </declare-styleable>

</resources>

獲取參數及初始化

public GestureLockViewGroup(Context context, AttributeSet attrs,
        int defStyle) {
  super(context, attrs, defStyle);

  /**
   * 獲得所有自定義的參數的值
   */
  TypedArray a = context.obtainStyledAttributes(attrs,
    R.styleable.GestureLockViewGroup, defStyle, 0);

  mNoFingerColor = a.getColor(R.styleable.GestureLockViewGroup_color_no_finger, mNoFingerColor);
  mFingerOnColor = a.getColor(R.styleable.GestureLockViewGroup_color_finger_on, mFingerOnColor);
  mFingerUpColorCorrect = a.getColor(R.styleable.GestureLockViewGroup_color_finger_up_correct, mFingerUpColorCorrect);
  mFingerUpColorError = a.getColor(R.styleable.GestureLockViewGroup_color_finger_up_error, mFingerUpColorError);
  mCount = a.getInt(R.styleable.GestureLockViewGroup_count, mCount);
  mPrferenceId = a.getInt(R.styleable.GestureLockViewGroup_preference_id, mPrferenceId);

  a.recycle();

  /**
   * 獲取密碼狀態
   */
  gesturePreference = new GesturePreference(context, mPrferenceId);
  password = gesturePreference.ReadStringPreference();
  Log.d(TAG, "password now is : " + password);
  isSetPassword = !password.equals("null"); //判斷是否已經保存有密碼
  isInPasswordSettingMode = !isSetPassword;  //當未設置密碼,進入密碼設置模式

  // 初始化畫筆
  mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mPaint.setStyle(Paint.Style.STROKE);
  mPaint.setStrokeCap(Paint.Cap.ROUND);
  mPaint.setStrokeJoin(Paint.Join.ROUND);
  mPath = new Path();
 }

根據參數繪制出圓

在onMeasure後調用該方法,繪制圓形矩陣

private GestureLockView[] mGestureLockViews; //保存所有的GestureLockView

private void initViews() {
  // 初始化mGestureLockViews
  if (mGestureLockViews == null) {
   mGestureLockViews = new GestureLockView[mCount * mCount];
   // 計算每個GestureLockView的寬度
   mGestureLockViewWidth = (int) (4 * mWidth * 1.0f / (5 * mCount + 1));
   //計算每個GestureLockView的間距
   mMarginBetweenLockView = (int) (mGestureLockViewWidth * 0.25);
   // 設置畫筆的寬度為GestureLockView的內圓直徑稍微小點
   mPaint.setStrokeWidth(mGestureLockViewWidth * 0.29f);

   for (int i = 0; i < mGestureLockViews.length; i++) {
    //初始化每個GestureLockView
    mGestureLockViews[i] = new GestureLockView(getContext(), mNoFingerColor, mFingerOnColor, mFingerUpColorCorrect, mFingerUpColorError);
    mGestureLockViews[i].setId(i + 1);
    //設置參數,主要是定位GestureLockView間的位置
    RelativeLayout.LayoutParams lockerParams = new RelativeLayout.LayoutParams(
      mGestureLockViewWidth, mGestureLockViewWidth);

    // 不是每行的第一個,則設置位置為前一個的右邊
    if (i % mCount != 0) {
     lockerParams.addRule(RelativeLayout.RIGHT_OF,
       mGestureLockViews[i - 1].getId());
    }
    // 從第二行開始,設置為上一行同一位置View的下面
    if (i > mCount - 1) {
     lockerParams.addRule(RelativeLayout.BELOW,
       mGestureLockViews[i - mCount].getId());
    }
    //設置右下左上的邊距
    int rightMargin = mMarginBetweenLockView;
    int bottomMargin = mMarginBetweenLockView;
    int leftMagin = 0;
    int topMargin = 0;
    /**
     * 每個View都有右外邊距和底外邊距 第一行的有上外邊距 第一列的有左外邊距
     */
    if (i >= 0 && i < mCount)// 第一行
    {
     topMargin = mMarginBetweenLockView;
    }
    if (i % mCount == 0)// 第一列
    {
     leftMagin = mMarginBetweenLockView;
    }

    lockerParams.setMargins(leftMagin, topMargin, rightMargin,
      bottomMargin);
    mGestureLockViews[i].setMode(Mode.STATUS_NO_FINGER);
    addView(mGestureLockViews[i], lockerParams);
   }
  }
 }

在觸摸監聽中處理不同事件

@Override
 public boolean onTouchEvent(MotionEvent event) {
  int action = event.getAction();
  int x = (int) event.getX();
  int y = (int) event.getY();
  Log.d(TAG, "mTryTimes : " + mTryTimes);

  //重試次數超過限制,直接返回
  if (mTryTimes <= 0 && isRetryTimeLimit) { 
   return true;
  }

  switch (action) {
   case MotionEvent.ACTION_DOWN:
    reset();  // 重置
    break;
   case MotionEvent.ACTION_MOVE:
    drawAndGetSelectedWhenTouchMove(x, y);
    break;
   case MotionEvent.ACTION_UP:
    if (isInPasswordSettingMode) {
     if (gesturePasswordSettingListener != null)
      setPasswordHandle(); //設置密碼
    } else {
     if (mChoose.size() > 0) {
      isCorrect = checkAnswer();
     } else {
      return true;
     }

     if (gestureEventListener != null) {
      gestureEventListener.onGestureEvent(isCorrect); //將結果回調
     }
     if (this.mTryTimes == 0) {
      gestureUnmatchedExceedListener.onUnmatchedExceedBoundary(); //超出重試次數,進入回調
     }
    }
    drawWhenTouchUp();
    break;
  }
  invalidate();
  return true;
 }

 private void drawAndGetSelectedWhenTouchMove(int x, int y) {
  mPaint.setColor(mFingerOnColor);
  mPaint.setAlpha(50);
  GestureLockView child = getChildIdByPos(x, y);
  if (child != null) {
   int cId = child.getId();
   if (!mChoose.contains(cId)) {
    mChoose.add(cId);
    mChooseString = mChooseString + cId;
    child.setMode(Mode.STATUS_FINGER_ON);
    // 設置指引線的起點
    mLastPathX = child.getLeft() / 2 + child.getRight() / 2;
    mLastPathY = child.getTop() / 2 + child.getBottom() / 2;

    if (mChoose.size() == 1)// 當前添加為第一個
    {
     mPath.moveTo(mLastPathX, mLastPathY);
    } else
    // 非第一個,將兩者使用線連上
    {
     mPath.lineTo(mLastPathX, mLastPathY);
    }
   }
  }
  // 指引線的終點
  mTmpTarget.x = x;
  mTmpTarget.y = y;
 }

 private void drawWhenTouchUp() {
  if (isCorrect) {
   mPaint.setColor(mFingerUpColorCorrect);
  } else {
   mPaint.setColor(mFingerUpColorError);
  }
  mPaint.setAlpha(50);
  Log.d(TAG, "mChoose = " + mChoose);
  // 將終點設置位置為起點,即取消指引線
  mTmpTarget.x = mLastPathX;
  mTmpTarget.y = mLastPathY;

  // 改變子元素的狀態為UP
  setItemModeUp();

  // 計算每個元素中箭頭需要旋轉的角度
  for (int i = 0; i + 1 < mChoose.size(); i++) {
   int childId = mChoose.get(i);
   int nextChildId = mChoose.get(i + 1);

   GestureLockView startChild = (GestureLockView) findViewById(childId);
   GestureLockView nextChild = (GestureLockView) findViewById(nextChildId);

   int dx = nextChild.getLeft() - startChild.getLeft();
   int dy = nextChild.getTop() - startChild.getTop();
   // 計算角度
   int angle = (int) Math.toDegrees(Math.atan2(dy, dx)) + 90;
   startChild.setArrowDegree(angle);
  }
 }

 設置密碼處理:

private void setPasswordHandle() {
  if (isWaitForFirstInput) {
   if (gesturePasswordSettingListener.onFirstInputComplete(mChooseString.length())) {
    firstInputPassword = mChooseString;
    isWaitForFirstInput = false;
   }
  } else {
   if (firstInputPassword.equals(mChooseString)) {
    gesturePasswordSettingListener.onSuccess();
    savePassword(mChooseString);
    isInPasswordSettingMode = false;
   } else {
    gesturePasswordSettingListener.onFail();
   }
  }
  reset();
 }

檢查手勢密碼是否正確:

public boolean checkAnswer() {
  if (password.equals(mChooseString)) {
   return true;
  } else {
   if (isRetryTimeLimit)
    this.mTryTimes--;
   return false;
  }
 }

重置:

private void reset() {
  mChoose.clear();
  mChooseString = "";
  mPath.reset();
  for (GestureLockView gestureLockView : mGestureLockViews) {
   gestureLockView.setMode(Mode.STATUS_NO_FINGER);
   gestureLockView.setArrowDegree(-1);
  }
 }

對外公開的一些方法

public void setGestureEventListener(GestureEventListener gestureEventListener) {
  this.gestureEventListener = gestureEventListener;
 }

 public void setGestureUnmatchedExceedListener(int retryTimes, GestureUnmatchedExceedListener gestureUnmatchedExceedListener) {
  isRetryTimeLimit = true;
  this.mTryTimes = retryTimes;
  this.gestureUnmatchedExceedListener = gestureUnmatchedExceedListener;
 }

 public void setGesturePasswordSettingListener(GesturePasswordSettingListener gesturePasswordSettingListener) {
  this.gesturePasswordSettingListener = gesturePasswordSettingListener;
 }

 public void removePassword() {
  gesturePreference.WriteStringPreference("null");
  this.isSetPassword = false;
  isWaitForFirstInput = true;
  isInPasswordSettingMode = true;
 }

 public void savePassword(String password) {
  this.password = password;
  gesturePreference.WriteStringPreference(password);
 }

 public String getPassword() {
  return password;
 }

 public void resetView() {
  reset();
  invalidate();
 }

 public void setRetryTimes(int retryTimes) {
  this.mTryTimes = retryTimes;
 }

 public boolean isSetPassword() {
  return isSetPassword;
 }

定義密碼存儲的Preference

就是簡單的存和讀

public GesturePreference(Context context, int nameTableId) {
  this.context = context;
  if (nameTableId != -1)
   this.nameTable = nameTable + nameTableId;
 }

 public void WriteStringPreference(String data) {
  SharedPreferences preferences = context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();
  editor.putString(nameTable, data);
  editor.commit();
 }

 public String ReadStringPreference() {
  SharedPreferences preferences = context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
  return preferences.getString(nameTable, "null");
 }

總結

ok,至此,整個手勢密碼的實現就完成了。

以上就是小編對Android實現簡易手勢密碼的資料整理,後續繼續整理相關資料,謝謝大家對本站的支持!

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