欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-06 22:38:56
简介 本文介绍一个android手势密码开源库的使用及实现的详细过程,该开源库主要实现以下几个功能: 支持手势密码的绘制,并支持密码保存功能,解锁时自动比对密码...

简介

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

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

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

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

github链接    个人博客

使用效果

首先看下使用效果:

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

使用方法

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