当前位置: 移动技术网 > 移动技术>移动开发>Android > Android自定义控件实现手势密码

Android自定义控件实现手势密码

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

android手势解锁密码效果图 

     首先呢想写这个手势密码的想法呢,完全是凭空而来的,然后笔者就花了一天时间弄出来了。本以为这个东西很简单,实际上手的时候发现,还有很多逻辑需要处理,稍不注意就容易乱套。写个ui效果图大约只花了3个小时,但是处理逻辑就处理了2个小时!废话不多说,下面开始讲解。 
    楼主呢,自己比较自定义控件,什么东西都掌握在自己的手里感觉那是相当不错(对于赶工期的小伙瓣儿们还是别手贱了,非常容易掉坑),一有了这个目标,我就开始构思实现方式。 
    1、整个自定义控件是继承view还是surfaceview呢?我的经验告诉我:需要一直不断绘制的最好继承surfaceview,而需要频繁与用户交互的最好就继承view。(求大神来打脸) 
    2、为了实现控件的屏幕适配性,当然必须重写onmeasure方法,然后在ondraw方法中进行绘制。 
    3、面向对象性:这个控件其实由两个对象组成:1、9个圆球;2、圆球之间的连线。 
    4、仔细观察圆球的特征:普通状态是白色、touch状态是蓝色、错误状态是红色、整体分为外围空心圆和内实心圆、所代表的位置信息(密码值) 
    5、仔细观察连线的特征:普通状态为蓝色、错误状态为红色、始终连接两个圆的中心、跟随手指移动而拓展连线、连线之间未点亮的圆球也要点亮。 
    6、通过外露参数来设置圆球的颜色、大小等等 
    7、通过上面的分析,真个控件可模块化为三个任务:onmeasure计算控件宽高以及小球半径、ondraw绘制小球与连线、ontouchevent控制绘制变化。 

    我把整个源码分为三个类文件:lockview、circle、util,其中lockview代表整个控件,circle代表小圆球、util封装工具方法(path因为太简单就没封装,若有代码洁癖请自行封装),下面展示util类的源代码。 

public class util{
 
 private static final string sp_name = "lockview";
 private static final string sp_key = "password";

 public static void savepwd(context mcontext ,list<integer> password){
  sharedpreferences sp = mcontext.getsharedpreferences(sp_name, context.mode_private);
  sp.edit().putstring(sp_key, listtostring(password)).commit();
 }
 
 public static string getpwd(context mcontext){
  sharedpreferences sp = mcontext.getsharedpreferences(sp_name, context.mode_private);
  return sp.getstring(sp_key, "");
 }
 
 public static void clearpwd(context mcontext){
  sharedpreferences sp = mcontext.getsharedpreferences(sp_name, context.mode_private);
  sp.edit().remove(sp_key).commit();
 }
 
 public static string listtostring(list<integer> lists){
  stringbuffer sb = new stringbuffer();
  for(int i = 0; i < lists.size(); i++){
   sb.append(lists.get(i));
  }
  return sb.tostring();
 }
 
 public static list<integer> stringtolist(string string){
  list<integer> lists = new arraylist<>();
  for(int i = 0; i < string.length(); i++){
   lists.add(integer.parseint(string.charat(i) + ""));
  }
  return lists;
 }
}

     这个工具方法其实很简单,就是对sharedpreferences的一个读写,还有就是list与string类型的互相转换。这里就不描述了。下面展示circle的源码 

public class circle{
 //默认值
 public static final int default_color = color.white;
 public static final int default_bound = 5;
 public static final int default_center_bound = 15;
 //状态值
 public static final int status_default = 0;
 public static final int status_touch = 1;
 public static final int status_success = 2;
 public static final int status_failed = 3;
 
 //圆形的中点x、y坐标
 private int centerx;
 private int centery;
 //圆形的颜色值
 private int colordefault = default_color;
 private int colorsuccess;
 private int colorfailed;
 //圆形的宽度
 private int bound = default_bound;
 //中心的宽度
 private int centerbound = default_center_bound;
 //圆形的半径
 private int radius;
 //圆形的状态
 private int status = status_default;
 //圆形的位置
 private int position;
 
 public circle(int centerx, int centery, int colorsuccess, int colorfailed, int radius, int position){
  super();
  this.centerx = centerx;
  this.centery = centery;
  this.colorsuccess = colorsuccess;
  this.colorfailed = colorfailed;
  this.radius = radius;
  this.position = position;
 }

 public circle(int centerx, int centery, int colordefault, int colorsuccess, int colorfailed, int bound,
   int centerbound, int radius, int status, int position){
  super();
  this.centerx = centerx;
  this.centery = centery;
  this.colordefault = colordefault;
  this.colorsuccess = colorsuccess;
  this.colorfailed = colorfailed;
  this.bound = bound;
  this.centerbound = centerbound;
  this.radius = radius;
  this.status = status;
  this.position = position;
 }

 public int getcenterx(){
  return centerx;
 }

 public void setcenterx(int centerx){
  this.centerx = centerx;
 }

 public int getcentery(){
  return centery;
 }

 public void setcentery(int centery){
  this.centery = centery;
 }

 public int getcolordefault(){
  return colordefault;
 }

 public void setcolordefault(int colordefault){
  this.colordefault = colordefault;
 }

 public int getcolorsuccess(){
  return colorsuccess;
 }

 public void setcolorsuccess(int colorsuccess){
  this.colorsuccess = colorsuccess;
 }

 public int getcolorfailed(){
  return colorfailed;
 }

 public void setcolorfailed(int colorfailed){
  this.colorfailed = colorfailed;
 }

 public int getbound(){
  return bound;
 }

 public void setbound(int bound){
  this.bound = bound;
 }

 public int getcenterbound(){
  return centerbound;
 }

 public void setcenterbound(int centerbound){
  this.centerbound = centerbound;
 }

 public int getradius(){
  return radius;
 }

 public void setradius(int radius){
  this.radius = radius;
 }

 public int getstatus(){
  return status;
 }

 public void setstatus(int status){
  this.status = status;
 }

 public int getposition(){
  return position;
 }

 public void setposition(int position){
  this.position = position;
 }

 /** 
  * @description:改变圆球当前状态 
 */
 public void changestatus(int status){
  this.status = status;
 }
 
 /** 
  * @description:绘制这个圆形 
 */
 public void draw(canvas canvas ,paint paint){
  switch(status){
   case status_default:
    paint.setcolor(colordefault);
    break;
   case status_touch:
   case status_success:
    paint.setcolor(colorsuccess);
    break;
   case status_failed:
    paint.setcolor(colorfailed);
    break;
   default:
    paint.setcolor(colordefault);
    break;
  }
  paint.setstyle(paint.style.fill);
  //绘制中心实心圆
  canvas.drawcircle(centerx, centery, centerbound, paint);
  //绘制空心圆
  paint.setstyle(paint.style.stroke);
  paint.setstrokewidth(bound);
  canvas.drawcircle(centerx, centery, radius, paint);
 }
}

     这个circle其实也非常简单。上面定义的成员变量一眼便明,并且有注释。重点在最后的draw方法,首先呢根据当前圆球的不同状态设置不同的颜色值,然后绘制中心的实心圆,再绘制外围的空心圆。所有的参数要么是外界传递,要么是默认值。(ps:面向对象真的非常有用,解耦良好的代码写起来也舒服看起来也舒服)。 

    最后的重点来了,lockview的源码,首先贴源码,然后再针对性讲解。 

public class lockview extends view{
 
 private static final int count_per_raw = 3;
 private static final int duration = 1500;
 private static final int min_pwd_number = 6;
 //@fields status_no_pwd : 当前没有保存密码
 public static final int status_no_pwd = 0;
 //@fields status_retry_pwd : 需要再输入一次密码
 public static final int status_retry_pwd = 1;
 //@fields status_save_pwd : 成功保存密码
 public static final int status_save_pwd = 2;
 //@fields status_success_pwd : 成功验证密码
 public static final int status_success_pwd = 3;
 //@fields status_failed_pwd : 验证密码失败
 public static final int status_failed_pwd = 4;
 //@fields status_error : 输入密码长度不够
 public static final int status_error = 5;
 
 private int width;
 private int height;
 private int padding = 0;
 private int colorsuccess = color.blue;
 private int colorfailed = color.red;
 private int minpwdnumber = min_pwd_number;
 private list<circle> circles = new arraylist<>();
 private paint mpaint = new paint(paint.anti_alias_flag);
 private path mpath = new path();
 private path backupspath = new path();
 private list<integer> result = new arraylist<>();
 private int status = status_no_pwd;
 private onlocklistener listener;
 private handler handler = new handler();

 public lockview(context context, attributeset attrs, int defstyle){
  super(context, attrs, defstyle);
  initstatus();
 }

 public lockview(context context, attributeset attrs){
  super(context, attrs);
  initstatus();
 }

 public lockview(context context){
  super(context);
  initstatus();
 }

 /** 
  * @description:初始化当前密码的状态
 */
 public void initstatus(){
  if(textutils.isempty(util.getpwd(getcontext()))){
   status = status_no_pwd;
  }else{
   status = status_save_pwd;
  }
 }
 
 public int getcurrentstatus(){
  return status;
 }

 /** 
  * @description:初始化参数,若不调用则使用默认值
  * @param padding 圆球之间的间距
  * @param colorsuccess 密码正确时圆球的颜色
  * @param colorfailed 密码错误时圆球的颜色
  * @return lockview
 */
 public lockview initparam(int padding ,int colorsuccess ,int colorfailed ,int minpwdnumber){
  this.padding = padding;
  this.colorsuccess = colorsuccess;
  this.colorfailed = colorfailed;
  this.minpwdnumber = minpwdnumber;
  init();
  return this;
 }
 
 /** 
  * @description:若第一次调用则创建圆球,否则更新圆球
 */
 private void init(){
  int circleradius = (width - (count_per_raw + 1) * padding) / count_per_raw /2;
  if(circles.size() == 0){   
   for(int i = 0; i < count_per_raw * count_per_raw; i++){
    createcircles(circleradius, i);
   }
  }else{
   for(int i = 0; i < count_per_raw * count_per_raw; i++){
    updatecircles(circles.get(i), circleradius);
   }
  }
 }
 
 private void createcircles(int radius, int position){
  int centerx = (position % 3 + 1) * padding + (position % 3 * 2 + 1) * radius;
  int centery = (position / 3 + 1) * padding + (position / 3 * 2 + 1) * radius;
  circle circle = new circle(centerx, centery, colorsuccess, colorfailed, radius, position);
  circles.add(circle);
 }
 
 private void updatecircles(circle circle ,int radius){
  int centerx = (circle.getposition() % 3 + 1) * padding + (circle.getposition() % 3 * 2 + 1) * radius;
  int centery = (circle.getposition() / 3 + 1) * padding + (circle.getposition() / 3 * 2 + 1) * radius;
  circle.setcenterx(centerx);
  circle.setcentery(centery);
  circle.setradius(radius);
  circle.setcolorsuccess(colorsuccess);
  circle.setcolorfailed(colorfailed);
 }

 @override
 protected void ondraw(canvas canvas){
  init();
  //绘制圆
  for(int i = 0; i < circles.size() ;i++){
   circles.get(i).draw(canvas, mpaint);
  }
  if(result.size() != 0){   
   //绘制path
   circle temp = circles.get(result.get(0));
   mpaint.setcolor(temp.getstatus() == circle.status_failed ? colorfailed : colorsuccess);
   mpaint.setstrokewidth(circle.default_center_bound);
   canvas.drawpath(mpath, mpaint);
  }
 }
 
 @override
 public boolean ontouchevent(motionevent event){
  switch(event.getaction()){
   case motionevent.action_down:
    backupspath.reset();
    for(int i = 0; i < circles.size() ;i++){
     circle circle = circles.get(i);
     if(event.getx() >= circle.getcenterx() - circle.getradius()
       && event.getx() <= circle.getcenterx() + circle.getradius()
       && event.gety() >= circle.getcentery() - circle.getradius()
       && event.gety() <= circle.getcentery() + circle.getradius()){
      circle.setstatus(circle.status_touch);
      //将这个点放入path
      backupspath.moveto(circle.getcenterx(), circle.getcentery());
      //放入结果
      result.add(circle.getposition());
      break;
     }
    }
    invalidate();
    return true;
    
   case motionevent.action_move:
    for(int i = 0; i < circles.size() ;i++){
     circle circle = circles.get(i);
     if(event.getx() >= circle.getcenterx() - circle.getradius()
       && event.getx() <= circle.getcenterx() + circle.getradius()
       && event.gety() >= circle.getcentery() - circle.getradius()
       && event.gety() <= circle.getcentery() + circle.getradius()){
      if(!result.contains(circle.getposition())){       
       circle.setstatus(circle.status_touch);
       //首先判断是否连线中间也有满足条件的圆
       circle lastcircle = circles.get(result.get(result.size() - 1));
       int cx = (lastcircle.getcenterx() + circle.getcenterx()) / 2;
       int cy = (lastcircle.getcentery() + circle.getcentery()) / 2;
       for(int j = 0; j < circles.size(); j++){
        circle tempcircle = circles.get(j);
        if(cx >= tempcircle.getcenterx() - tempcircle.getradius()
          && cx <= tempcircle.getcenterx() + tempcircle.getradius()
          && cy >= tempcircle.getcentery() - tempcircle.getradius()
          && cy <= tempcircle.getcentery() + tempcircle.getradius()){
         //处理满足条件的圆
         backupspath.lineto(tempcircle.getcenterx(), tempcircle.getcentery());
         //放入结果
         tempcircle.setstatus(circle.status_touch);
         result.add(tempcircle.getposition());
        }
       }
       //处理现在的圆
       backupspath.lineto(circle.getcenterx(), circle.getcentery());
       //放入结果
       circle.setstatus(circle.status_touch);
       result.add(circle.getposition());
       break;
      }
     }
    }
    mpath.reset();
    mpath.addpath(backupspath);
    mpath.lineto(event.getx(), event.gety());
    invalidate();
    break;
    
   case motionevent.action_up:
    mpath.reset();
    mpath.addpath(backupspath);
    invalidate();
    if(result.size() < minpwdnumber){
     if(listener != null){      
      listener.onerror();
     }
     if(status == status_retry_pwd){
      util.clearpwd(getcontext());
     }
     status = status_error;
     for(int i = 0; i < result.size(); i++){
      circles.get(result.get(i)).setstatus(circle.status_failed);
     }
    }else{
     if(status == status_no_pwd){ //当前没有密码
      //保存密码,重新录入
      util.savepwd(getcontext(), result);
      status = status_retry_pwd;
      if(listener != null){
       listener.ontypeinonce(util.listtostring(result));
      }
     }else if(status == status_retry_pwd){ //需要重新绘制密码
      //判断两次输入是否相等
      if(util.getpwd(getcontext()).equals(util.listtostring(result))){
       status = status_save_pwd;
       if(listener != null){
        listener.ontypeintwice(util.listtostring(result), true);
       }
       for(int i = 0; i < result.size(); i++){
        circles.get(result.get(i)).setstatus(circle.status_success);
       }
      }else{
       status = status_no_pwd;
       util.clearpwd(getcontext());
       if(listener != null){
        listener.ontypeintwice(util.listtostring(result), false);
       }
       for(int i = 0; i < result.size(); i++){
        circles.get(result.get(i)).setstatus(circle.status_failed);
       }
      }
     }else if(status == status_save_pwd){ //验证密码
      //判断密码是否正确
      if(util.getpwd(getcontext()).equals(util.listtostring(result))){
       status = status_success_pwd;
       if(listener != null){
        listener.onunlock(util.listtostring(result), true);
       }
       for(int i = 0; i < result.size(); i++){
        circles.get(result.get(i)).setstatus(circle.status_success);
       }
      }else{
       status = status_failed_pwd;
       if(listener != null){
        listener.onunlock(util.listtostring(result), false);
       }
       for(int i = 0; i < result.size(); i++){
        circles.get(result.get(i)).setstatus(circle.status_failed);
       }
      }
     }
    }
    invalidate();
    handler.postdelayed(new runnable(){
     
     @override
     public void run(){
      result.clear();
      mpath.reset();
      backupspath.reset();
     //  initstatus();
      // 重置下状态
      if(status == status_success_pwd || status == status_failed_pwd){
       status = status_save_pwd;
      }else if(status == status_error){
       initstatus();
      }
      for(int i = 0; i < circles.size(); i++){
       circles.get(i).setstatus(circle.status_default);
      }
      invalidate();
     }
    }, duration);
    break;
   default:
    break;
  }
  return super.ontouchevent(event);
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec){
  width = measurespec.getsize(widthmeasurespec);
  height = width - getpaddingleft() - getpaddingright() + getpaddingtop() + getpaddingbottom();
  setmeasureddimension(width, height);
 }
 
 public void setonlocklistener(onlocklistener listener){
  this.listener = listener;
 }
 
 public interface onlocklistener{
  /** 
   * @description:没有密码时,第一次录入密码触发器
  */
  void ontypeinonce(string input);
  /**
   * @description:已经录入第一次密码,录入第二次密码触发器
   */
  void ontypeintwice(string input ,boolean issuccess);
  /** 
   * @description:验证密码触发器 
  */
  void onunlock(string input ,boolean issuccess);
  
  /**
   * @description:密码长度不够
   */
  void onerror();
 }
}

好了,逐次讲解。 

 首先是对status的初始化,其实在static域我已经申明了6个状态,分别是: 

 //当前没有保存密码
 public static final int status_no_pwd = 0;
 //需要再输入一次密码
 public static final int status_retry_pwd = 1;
 //成功保存密码
 public static final int status_save_pwd = 2;
 //成功验证密码
 public static final int status_success_pwd = 3;
 //验证密码失败
 public static final int status_failed_pwd = 4;
 //输入密码长度不够
 public static final int status_error = 5; 

 在刚初始化的时候,就初始化当前的状态,初始化状态就只有2个状态:有密码、无密码。 

 public void initstatus(){
  if(textutils.isempty(util.getpwd(getcontext()))){
   status = status_no_pwd;
  }else{
   status = status_save_pwd;
  }
 }
 
 public int getcurrentstatus(){
  return status;
 }

     然后就是通过外界的设置初始化一些参数(若不调用initparam方法,则采用默认值): 

 public lockview initparam(int padding ,int colorsuccess ,int colorfailed ,int minpwdnumber){
  this.padding = padding;
  this.colorsuccess = colorsuccess;
  this.colorfailed = colorfailed;
  this.minpwdnumber = minpwdnumber;
  init();
  return this;
 }
 
 /** 
  * @description:若第一次调用则创建圆球,否则更新圆球
 */
 private void init(){
  int circleradius = (width - (count_per_raw + 1) * padding) / count_per_raw /2;
  if(circles.size() == 0){   
   for(int i = 0; i < count_per_raw * count_per_raw; i++){
    createcircles(circleradius, i);
   }
  }else{
   for(int i = 0; i < count_per_raw * count_per_raw; i++){
    updatecircles(circles.get(i), circleradius);
   }
  }
 }

上述代码主要根据设置的padding值,计算出小球的大小,然后判断是否是初始化小球,还是更新小球。 

 private void createcircles(int radius, int position){
  int centerx = (position % 3 + 1) * padding + (position % 3 * 2 + 1) * radius;
  int centery = (position / 3 + 1) * padding + (position / 3 * 2 + 1) * radius;
  circle circle = new circle(centerx, centery, colorsuccess, colorfailed, radius, position);
  circles.add(circle);
 }
 
 private void updatecircles(circle circle ,int radius){
  int centerx = (circle.getposition() % 3 + 1) * padding + (circle.getposition() % 3 * 2 + 1) * radius;
  int centery = (circle.getposition() / 3 + 1) * padding + (circle.getposition() / 3 * 2 + 1) * radius;
  circle.setcenterx(centerx);
  circle.setcentery(centery);
  circle.setradius(radius);
  circle.setcolorsuccess(colorsuccess);
  circle.setcolorfailed(colorfailed);
 }

别忘了上面的方法依赖一个width值,这个值是在onmeasure中计算出来的 

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec){
  width = measurespec.getsize(widthmeasurespec);
  height = width - getpaddingleft() - getpaddingright() + getpaddingtop() + getpaddingbottom();
  setmeasureddimension(width, height);
 }

然后就是绘制方法了,因为我们的高度解耦性,本应该非常复杂的ondraw方法,却如此简单。就只绘制了小球和路径。 

 @override
 protected void ondraw(canvas canvas){
  init();
  //绘制圆
  for(int i = 0; i < circles.size() ;i++){
   circles.get(i).draw(canvas, mpaint);
  }
  if(result.size() != 0){   
   //绘制path
   circle temp = circles.get(result.get(0));
   mpaint.setcolor(temp.getstatus() == circle.status_failed ? colorfailed : colorsuccess);
   mpaint.setstrokewidth(circle.default_center_bound);
   canvas.drawpath(mpath, mpaint);
  }
 }

控件是需要和外界进行交互的,我喜欢的方法就是自定义监听器,然后接口回调。 

 public void setonlocklistener(onlocklistener listener){
  this.listener = listener;
 }
 
 public interface onlocklistener{
  /** 
   * @description:没有密码时,第一次录入密码触发器
  */
  void ontypeinonce(string input);
  /**
   * @description:已经录入第一次密码,录入第二次密码触发器
   */
  void ontypeintwice(string input ,boolean issuccess);
  /** 
   * @description:验证密码触发器 
  */
  void onunlock(string input ,boolean issuccess);
  
  /**
   * @description:密码长度不够
   */
  void onerror();
 }

最后最最最重要的一个部分来了,ontouchevent方法,这个方法其实也可以分为三个部分讲解:down事件、move事件和up事件。首先贴出down事件代码 

 case motionevent.action_down:
    backupspath.reset();
    for(int i = 0; i < circles.size() ;i++){
     circle circle = circles.get(i);
     if(event.getx() >= circle.getcenterx() - circle.getradius()
       && event.getx() <= circle.getcenterx() + circle.getradius()
       && event.gety() >= circle.getcentery() - circle.getradius()
       && event.gety() <= circle.getcentery() + circle.getradius()){
      circle.setstatus(circle.status_touch);
      //将这个点放入path
      backupspath.moveto(circle.getcenterx(), circle.getcentery());
      //放入结果
      result.add(circle.getposition());
      break;
     }
    }
    invalidate();
    return true;

也就是对按下的x、y坐标进行判断,是否属于我们的小球范围内,若属于,则放入路径集合、更改状态、加入密码结果集。这里别忘了return true,大家都知道吧。 
然后是move事件,move事件主要做三件事情:变更小球的状态、添加到路径集合、对路径覆盖的未点亮小球进行点亮。代码有详细注释就不过多讲解了。 

case motionevent.action_move:
    for(int i = 0; i < circles.size() ;i++){
     circle circle = circles.get(i);
     if(event.getx() >= circle.getcenterx() - circle.getradius()
       && event.getx() <= circle.getcenterx() + circle.getradius()
       && event.gety() >= circle.getcentery() - circle.getradius()
       && event.gety() <= circle.getcentery() + circle.getradius()){
      if(!result.contains(circle.getposition())){       
       circle.setstatus(circle.status_touch);
       //首先判断是否连线中间也有满足条件的圆
       circle lastcircle = circles.get(result.get(result.size() - 1));
       int cx = (lastcircle.getcenterx() + circle.getcenterx()) / 2;
       int cy = (lastcircle.getcentery() + circle.getcentery()) / 2;
       for(int j = 0; j < circles.size(); j++){
        circle tempcircle = circles.get(j);
        if(cx >= tempcircle.getcenterx() - tempcircle.getradius()
          && cx <= tempcircle.getcenterx() + tempcircle.getradius()
          && cy >= tempcircle.getcentery() - tempcircle.getradius()
          && cy <= tempcircle.getcentery() + tempcircle.getradius()){
         //处理满足条件的圆
         backupspath.lineto(tempcircle.getcenterx(), tempcircle.getcentery());
         //放入结果
         tempcircle.setstatus(circle.status_touch);
         result.add(tempcircle.getposition());
        }
       }
       //处理现在的圆
       backupspath.lineto(circle.getcenterx(), circle.getcentery());
       //放入结果
       circle.setstatus(circle.status_touch);
       result.add(circle.getposition());
       break;
      }
     }
    }
    mpath.reset();
    mpath.addpath(backupspath);
    mpath.lineto(event.getx(), event.gety());
    invalidate();
    break;

这里我用了两个path对象,backupspath用于只存放小球的中点坐标,mpath不仅要存储小球的中点坐标,还要存储当前手指触碰坐标,为了实现连线跟随手指运动的效果。 
最后是up事件,这里有太多复杂的状态转换,我估计文字讲解是描述不清的,大家还是看源代码吧。           

 case motionevent.action_up:
    mpath.reset();
    mpath.addpath(backupspath);
    invalidate();
    if(result.size() < minpwdnumber){
     if(listener != null){      
      listener.onerror();
     }
     if(status == status_retry_pwd){
      util.clearpwd(getcontext());
     }
     status = status_error;
     for(int i = 0; i < result.size(); i++){
      circles.get(result.get(i)).setstatus(circle.status_failed);
     }
    }else{
     if(status == status_no_pwd){ //当前没有密码
      //保存密码,重新录入
      util.savepwd(getcontext(), result);
      status = status_retry_pwd;
      if(listener != null){
       listener.ontypeinonce(util.listtostring(result));
      }
     }else if(status == status_retry_pwd){ //需要重新绘制密码
      //判断两次输入是否相等
      if(util.getpwd(getcontext()).equals(util.listtostring(result))){
       status = status_save_pwd;
       if(listener != null){
        listener.ontypeintwice(util.listtostring(result), true);
       }
       for(int i = 0; i < result.size(); i++){
        circles.get(result.get(i)).setstatus(circle.status_success);
       }
      }else{
       status = status_no_pwd;
       util.clearpwd(getcontext());
       if(listener != null){
        listener.ontypeintwice(util.listtostring(result), false);
       }
       for(int i = 0; i < result.size(); i++){
        circles.get(result.get(i)).setstatus(circle.status_failed);
       }
      }
     }else if(status == status_save_pwd){ //验证密码
      //判断密码是否正确
      if(util.getpwd(getcontext()).equals(util.listtostring(result))){
       status = status_success_pwd;
       if(listener != null){
        listener.onunlock(util.listtostring(result), true);
       }
       for(int i = 0; i < result.size(); i++){
        circles.get(result.get(i)).setstatus(circle.status_success);
       }
      }else{
       status = status_failed_pwd;
       if(listener != null){
        listener.onunlock(util.listtostring(result), false);
       }
       for(int i = 0; i < result.size(); i++){
        circles.get(result.get(i)).setstatus(circle.status_failed);
       }
      }
     }
    }
    invalidate();
    handler.postdelayed(new runnable(){
     
     @override
     public void run(){
      result.clear();
      mpath.reset();
      backupspath.reset();
     //  initstatus();
      // 重置下状态
      if(status == status_success_pwd || status == status_failed_pwd){
       status = status_save_pwd;
      }else if(status == status_error){
       initstatus();
      }
      for(int i = 0; i < circles.size(); i++){
       circles.get(i).setstatus(circle.status_default);
      }
      invalidate();
     }
    }, duration);
    break;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网