当前位置: 移动技术网 > IT编程>移动开发>Android > Android 简易手势密码开源库详解

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

2019年07月24日  | 移动技术网IT编程  | 我要评论

超级搞笑二人转,一女n男肉文,保利垄上私人会所

简介

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网