热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android简易手势密码开源库详解

本文主要介绍Android简易手势密码,这里主要介绍手势密码如何实现及简单的示例代码,有需要的同学可以参考下

简介

本文介绍一个Android手势密码开源库的使用及实现的详细过程,该开源库主要实现以下几个功能:

  1. 支持手势密码的绘制,并支持密码保存功能,解锁时自动比对密码给出结果
  2. 封装了绘制密码的方法,比对两次密码是否一致,可以快捷地进行手势密码的设置
  3. 可以设置密码输入错误后的重试次数上限
  4. 可以自定义不同状态下手势密码图案的颜色
  5. 可以自定义手势密码的触摸点数量(n*n)

最近需要用到手势密码解锁功能,找了一些demo感觉用起来都有点麻烦,于是参考一些文章自己造了下轮子,封装了相关的一些方法,使用起来比较便捷。

github链接如下,觉得还可以请帮忙star支持下~

github链接    个人博客

使用效果

首先看下使用效果:

使用方法

XML布局文件中使用该控件

可以设置的一些参数,说明如下:

  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 

自定义手势密码的ViewGroup

加入自定义view的属性

<&#63;xml version="1.0" encoding="utf-8"&#63;>


 
 
 
 
 
 

 
  
  
  
  
  
  
 



获取参数及初始化

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  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 

在触摸监听中处理不同事件

@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 

 设置密码处理:

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实现简易手势密码的资料整理,后续继续整理相关资料,谢谢大家对本站的支持!


推荐阅读
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 打开文件管理器_【教程】模组管理器3.1食用指南
    文编:byakko最近有部分小伙伴反应还不会使用unity模组管理器,现在我就给大家讲一下unity模组管理器——从下载到使用。完整视频版以下是无WiF ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 学习笔记(34):第三阶段4.2.6:SpringCloud Config配置中心的应用与原理第三阶段4.2.6SpringCloud Config配置中心的应用与原理
    立即学习:https:edu.csdn.netcourseplay29983432482?utm_sourceblogtoedu配置中心得核心逻辑springcloudconfi ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
author-avatar
lv
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有