Android 简易手势密码开源库详解
简介
本文介绍一个android手势密码开源库的使用及实现的详细过程,该开源库主要实现以下几个功能:
- 支持手势密码的绘制,并支持密码保存功能,解锁时自动比对密码给出结果
- 封装了绘制密码的方法,比对两次密码是否一致,可以快捷地进行手势密码的设置
- 可以设置密码输入错误后的重试次数上限
- 可以自定义不同状态下手势密码图案的颜色
- 可以自定义手势密码的触摸点数量(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" />
可以设置的一些参数,说明如下:
- color_no_finger:未触摸时圆形的颜色
- color_finger_on:触摸时圆形的颜色
- color_finger_up_correct:输入正确时圆形的颜色
- color_finger_up_error:出错时圆形的颜色
- count:收拾密码的圆形数量,n*n
- 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实现简易手势密码的资料整理,后续继续整理相关资料,谢谢大家对本站的支持!
推荐阅读