配色: 字号:
Android 简易手势密码开源库详解
2016-12-05 | 阅:  转:  |  分享 
  
Android简易手势密码开源库详解

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

简介

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

支持手势密码的绘制,并支持密码保存功能,解锁时自动比对密码给出结果

封装了绘制密码的方法,比对两次密码是否一致,可以快捷地进行手势密码的设置

可以设置密码输入错误后的重试次数上限

可以自定义不同状态下手势密码图案的颜色

可以自定义手势密码的触摸点数量(nn)

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

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

github链接个人博客

使用效果

首先看下使用效果:



使用方法

XML布局文件中使用该控件

1

2

3

4

5

6

7
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:收拾密码的圆形数量,nn

preference_id:手势密码保存的id号,不输入或输入-1则使用默认的id

初始化

?

1

2

3

4

5

6 privatevoidinitGesture(){

mGestureLockViewGroup=(GestureLockViewGroup)findViewById(R.id.gesturelock);

gestureEventListener();

gesturePasswordSettingListener();

gestureRetryLimitListener();

} 设置手势密码监听事件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21 privatevoidgestureEventListener(){

mGestureLockViewGroup.setGestureEventListener(newGestureEventListener(){

@Override

publicvoidonGestureEvent(booleanmatched){

mylog.d("onGestureEventmatched:"+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("手势密码正确");

}

}

}

});

} 若已经设置有密码则会进入该回调,在这里对结果进行处理,上面的例子中加入了一个重设密码的处理。

手势密码设置

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29 privatevoidgesturePasswordSettingListener(){

mGestureLockViewGroup.setGesturePasswordSettingListener(newGesturePasswordSettingListener(){

@Override

publicbooleanonFirstInputComplete(intlen){

if(len>3){

tv_state.setTextColor(Color.WHITE);

tv_state.setText("再次绘制手势密码");

returntrue;

}else{

tv_state.setTextColor(Color.RED);

tv_state.setText("最少连接4个点,请重新输入!");

returnfalse;

}

}

@Override

publicvoidonSuccess(){

tv_state.setTextColor(Color.WHITE);

Toast.makeText(MainActivity.this,"密码设置成功!",Toast.LENGTH_SHORT).show();

tv_state.setText("请输入手势密码解锁!");

}

@Override

publicvoidonFail(){

tv_state.setTextColor(Color.RED);

tv_state.setText("与上一次绘制不一致,请重新绘制");

}

});

} 若还未设置密码,绘制手势的时候会进入该回调,返回值为绘制的触摸点的数量,onFirstInputComplete中返回true则进入第二手势密码的绘制,两次输入一致后自动保存密码。

重试次数超过限制监听

1

2

3

4

5

6

7

8

9 privatevoidgestureRetryLimitListener(){

mGestureLockViewGroup.setGestureUnmatchedExceedListener(3,newGestureUnmatchedExceedListener(){

@Override

publicvoidonUnmatchedExceedBoundary(){

tv_state.setTextColor(Color.RED);

tv_state.setText("错误次数过多,请稍后再试!");

}

});

} 若设置了该监听事件,则输入错误有次数限制,超过上限后进入回调,在该回调中进行处理。

清除密码的逻辑自己加个判断处理下即可,具体可以看下github上的demo

其他的一些API

1

2

3

4

5

6 publicvoidremovePassword():清除密码

publicvoidsavePassword():保存密码,设置手势密码成功后会自动保存,也可以调用该接口另外设置密码

publicvoidgetPassword():获取密码

publicvoidsetRetryTimes(intretryTimes):设置重试次数上限

publicbooleanisSetPassword():返回现在是否已经设置有密码

publicvoidresetView():将视图Reset 1

2

3

4

5

6 allprojects{

repositories{

...

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

}

} module的build.gradle中加入依赖:

1

2

3 dependencies{

compile''com.github.autume:GestureLock:1.0.0''

} 总的使用就是这样,是不是很简单!

具体实现过程

下面讲下实现的过程,如果只是直接拿来用的话也可以略过这部分。

自定义手势密码的圆形view

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

初始化传入参数

1

2

3

4

5

6

7

8

9 publicGestureLockView(Contextcontext,intcolorNoFingerr,intcolorFingerOn,intcolorCorrect,intcolorError){

super(context);

this.mColorNoFinger=colorNoFingerr;

this.mColorFingerOn=colorFingerOn;

this.mColorFingerUpCorrect=colorCorrect;

this.mColorFingerUpError=colorError;

mPaint=newPaint(Paint.ANTI_ALIAS_FLAG);

mArrowPath=newPath();

} 根据不同的触摸状态绘制不同颜色的圆

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40 @Override

protectedvoidonDraw(Canvascanvas){

switch(mCurrentStatus){

caseSTATUS_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,mRadiusmInnerCircleRadiusRate,mPaint);

break;

caseSTATUS_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,mRadiusmInnerCircleRadiusRate,mPaint);

drawArrow(canvas);

break;

caseSTATUS_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,mRadiusmInnerCircleRadiusRate,mPaint);

break;

}

} 1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31 @Override

protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){

super.onMeasure(widthMeasureSpec,heightMeasureSpec);

mWidth=MeasureSpec.getSize(widthMeasureSpec);

mHeight=MeasureSpec.getSize(heightMeasureSpec);

//取长和宽中的小值

mWidth=mWidth
mRadius=mCenterX=mCenterY=mWidth/2;

mRadius-=mStrokeWidth/2;

//绘制三角形,初始时是个默认箭头朝上的一个等腰三角形,用户绘制结束后,根据由两个GestureLockView决定需要旋转多少度

floatmArrowLength=mWidth/2mArrowRate;

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

}

privatevoiddrawArrow(Canvascanvas){

if(mArrowDegree!=-1){

mPaint.setStyle(Paint.Style.FILL);

canvas.save();

canvas.rotate(mArrowDegree,mCenterX,mCenterY);

canvas.drawPath(mArrowPath,mPaint);

canvas.restore();

}

} 自定义手势密码的ViewGroup

加入自定义view的属性

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20































获取参数及初始化

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35 publicGestureLockViewGroup(Contextcontext,AttributeSetattrs,

intdefStyle){

super(context,attrs,defStyle);

/

获得所有自定义的参数的值

/

TypedArraya=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=newGesturePreference(context,mPrferenceId);

password=gesturePreference.ReadStringPreference();

Log.d(TAG,"passwordnowis:"+password);

isSetPassword=!password.equals("null");//判断是否已经保存有密码

isInPasswordSettingMode=!isSetPassword;//当未设置密码,进入密码设置模式

//初始化画笔

mPaint=newPaint(Paint.ANTI_ALIAS_FLAG);

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setStrokeCap(Paint.Cap.ROUND);

mPaint.setStrokeJoin(Paint.Join.ROUND);

mPath=newPath();

} 根据参数绘制出圆

在onMeasure后调用该方法,绘制圆形矩阵

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55 privateGestureLockView[]mGestureLockViews;//保存所有的GestureLockView

privatevoidinitViews(){

//初始化mGestureLockViews

if(mGestureLockViews==null){

mGestureLockViews=newGestureLockView[mCountmCount];

//计算每个GestureLockView的宽度

mGestureLockViewWidth=(int)(4mWidth1.0f/(5mCount+1));

//计算每个GestureLockView的间距

mMarginBetweenLockView=(int)(mGestureLockViewWidth0.25);

//设置画笔的宽度为GestureLockView的内圆直径稍微小点

mPaint.setStrokeWidth(mGestureLockViewWidth0.29f);

for(inti=0;i
//初始化每个GestureLockView

mGestureLockViews[i]=newGestureLockView(getContext(),mNoFingerColor,mFingerOnColor,mFingerUpColorCorrect,mFingerUpColorError);

mGestureLockViews[i].setId(i+1);

//设置参数,主要是定位GestureLockView间的位置

RelativeLayout.LayoutParamslockerParams=newRelativeLayout.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());

}

//设置右下左上的边距

intrightMargin=mMarginBetweenLockView;

intbottomMargin=mMarginBetweenLockView;

intleftMagin=0;

inttopMargin=0;

/

每个View都有右外边距和底外边距第一行的有上外边距第一列的有左外边距

/

if(i>=0&&i
{

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

}

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103 @Override

publicbooleanonTouchEvent(MotionEventevent){

intaction=event.getAction();

intx=(int)event.getX();

inty=(int)event.getY();

Log.d(TAG,"mTryTimes:"+mTryTimes);

//重试次数超过限制,直接返回

if(mTryTimes<=0&&isRetryTimeLimit){

returntrue;

}

switch(action){

caseMotionEvent.ACTION_DOWN:

reset();//重置

break;

caseMotionEvent.ACTION_MOVE:

drawAndGetSelectedWhenTouchMove(x,y);

break;

caseMotionEvent.ACTION_UP:

if(isInPasswordSettingMode){

if(gesturePasswordSettingListener!=null)

setPasswordHandle();//设置密码

}else{

if(mChoose.size()>0){

isCorrect=checkAnswer();

}else{

returntrue;

}

if(gestureEventListener!=null){

gestureEventListener.onGestureEvent(isCorrect);//将结果回调

}

if(this.mTryTimes==0){

gestureUnmatchedExceedListener.onUnmatchedExceedBoundary();//超出重试次数,进入回调

}

}

drawWhenTouchUp();

break;

}

invalidate();

returntrue;

}

privatevoiddrawAndGetSelectedWhenTouchMove(intx,inty){

mPaint.setColor(mFingerOnColor);

mPaint.setAlpha(50);

GestureLockViewchild=getChildIdByPos(x,y);

if(child!=null){

intcId=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;

}

privatevoiddrawWhenTouchUp(){

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(inti=0;i+1
intchildId=mChoose.get(i);

intnextChildId=mChoose.get(i+1);

GestureLockViewstartChild=(GestureLockView)findViewById(childId);

GestureLockViewnextChild=(GestureLockView)findViewById(nextChildId);

intdx=nextChild.getLeft()-startChild.getLeft();

intdy=nextChild.getTop()-startChild.getTop();

//计算角度

intangle=(int)Math.toDegrees(Math.atan2(dy,dx))+90;

startChild.setArrowDegree(angle);

}

} 设置密码处理:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17 privatevoidsetPasswordHandle(){

if(isWaitForFirstInput){

if(gesturePasswordSettingListener.onFirstInputComplete(mChooseString.length())){

firstInputPassword=mChooseString;

isWaitForFirstInput=false;

}

}else{

if(firstInputPassword.www.hunanwang.netequals(mChooseString)){

gesturePasswordSettingListener.onSuccess();

savePassword(mChooseString);

isInPasswordSettingMode=false;

}else{

gesturePasswordSettingListener.onFail();

}

}

reset();

} 检查手势密码是否正确:

1

2

3

4

5

6

7

8

9 publicbooleancheckAnswer(){

if(password.equals(mChooseString)){

returntrue;

}else{

if(isRetryTimeLimit)

this.mTryTimes--;

returnfalse;

}

} 重置:

1

2

3

4

5

6

7

8

9 privatevoidreset(){

mChoose.www.visa158.comclear();

mChooseString="";

mPath.reset();

for(GestureLockViewgestureLockView:mGestureLockViews){

gestureLockView.setMode(Mode.STATUS_NO_FINGER);

gestureLockView.setArrowDegree(-1);

}

} 对外公开的一些方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42 publicvoidsetGestureEventListener(GestureEventListenergestureEventListener){

this.gestureEventListener=gestureEventListener;

}

publicvoidsetGestureUnmatchedExceedListener(intretryTimes,GestureUnmatchedExceedListenergestureUnmatchedExceedListener){

isRetryTimeLimit=true;

this.mTryTimes=retryTimes;

this.gestureUnmatchedExceedListener=gestureUnmatchedExceedListener;

}

publicvoidsetGesturePasswordSettingListener(GesturePasswordSettingListenergesturePasswordSettingListener){

this.gesturePasswordSettingListener=gesturePasswordSettingListener;

}

publicvoidremovePassword(){

gesturePreference.WriteStringPreference("null");

this.isSetPassword=false;

isWaitForFirstInput=true;

isInPasswordSettingMode=true;

}

publicvoidsavePassword(Stringpassword){

this.password=password;

gesturePreference.WriteStringPreference(password);

}

publicStringgetPassword(){

returnpassword;

}

publicvoidresetView(){

reset();

invalidate();

}

publicvoidsetRetryTimes(intretryTimes){

this.mTryTimes=retryTimes;

}

publicbooleanisSetPassword(){

returnisSetPassword;

} 定义密码存储的Preference

就是简单的存和读

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17 publicGesturePreference(Contextcontext,intnameTableId){

this.context=context;

if(nameTableId!=-1)

this.nameTable=nameTable+nameTableId;

}

publicvoidWriteStringPreference(Stringdata){

SharedPreferencespreferences=context.getSharedPreferences(fileName,Context.MODE_PRIVATE);

SharedPreferences.Editoreditor=preferences.edit();

editor.putString(nameTable,data);

editor.commit();

}

publicStringReadStringPreference(){

SharedPreferencespreferences=context.getSharedPreferences(fileName,Context.MODE_PRIVATE);

returnpreferences.getString(nameTable,"null");

} 总结

ok,至此,整个手势密码的实现就完成了。























献花(0)
+1
(本文系白狐一梦首藏)