Android自定义View手势密码

Android 自定义View 当然是十分重要的,笔者这两天写了一个自定义 View 的手势密码,和大家分享分享:

首先,我们来创建一个表示点的类,Point.java:

public class Point {

 // 点的三种状态
 public static final int POINT_STATUS_NORMAL = 0;
 public static final int POINT_STATUS_CLICK = 1;
 public static final int POINT_STATUS_ERROR = 2;

 // 默认状态
 public int state = POINT_STATUS_NORMAL;

 // 点的坐标
 public float mX;
 public float mY;

 public Point(float x,float y){
  this.mX = x;
  this.mY = y;
 }

 // 获取两个点的距离
 public float getInstance(Point a){
  return (float) Math.sqrt((mX-a.mX)*(mX-a.mX)+(mY-a.mY)*(mY-a.mY));
 }

}

然后我们创建一个 HandleLock.java 继承自 View,并重写其三种构造方法(不重写带两个参数的构造方法会导致程序出错):

首先,我们先把后面需要用的变量写出来,方便大家明白这些变量是干嘛的:

// 三种画笔
 private Paint mNormalPaint;
 private Paint mClickPaint;
 private Paint mErrorPaint;

 // 点的半径
 private float mRadius;

 // 九个点,使用二维数组
 private Point[][] mPoints = new Point[3][3];

 // 保存手势划过的点
 private ArrayList<Point> mClickPointsList = new ArrayList<Point>();
 // 手势的 x 坐标,y 坐标
 private float mHandleX;
 private float mHandleY;

 private OnDrawFinishListener mListener;

 // 保存滑动路径
 private StringBuilder mRoute = new StringBuilder();
 // 是否在画错误状态
 private boolean isDrawError = false;
 接下来我们来初始化数据:

// 初始化数据
 private void initData() {

  // 初始化三种画笔,正常状态为灰色,点下状态为蓝色,错误为红色
  mNormalPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mNormalPaint.setColor(Color.parseColor("#ABABAB"));
  mClickPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mClickPaint.setColor(Color.parseColor("#1296db"));
  mErrorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mErrorPaint.setColor(Color.parseColor("#FB0C13"));

  // 获取点间隔
  float offset = 0;
  if (getWidth() > getHeight()) {
   // 横屏
   offset = getHeight() / 7;
   mRadius = offset / 2;
   mPoints[0][0] = new Point(getWidth() / 2 - offset * 2, offset + mRadius);
   mPoints[0][1] = new Point(getWidth() / 2, offset + mRadius);
   mPoints[0][2] = new Point(getWidth() / 2 + offset * 2, offset + mRadius);
   mPoints[1][0] = new Point(getWidth() / 2 - offset * 2, offset * 3 + mRadius);
   mPoints[1][1] = new Point(getWidth() / 2, offset * 3 + mRadius);
   mPoints[1][2] = new Point(getWidth() / 2 + offset * 2, offset * 3 + mRadius);
   mPoints[2][0] = new Point(getWidth() / 2 - offset * 2, offset * 5 + mRadius);
   mPoints[2][1] = new Point(getWidth() / 2, offset * 5 + mRadius);
   mPoints[2][2] = new Point(getWidth() / 2 + offset * 2, offset * 5 + mRadius);
  } else {
   // 竖屏
   offset = getWidth() / 7;
   mRadius = offset / 2;
   mPoints[0][0] = new Point(offset + mRadius, getHeight() / 2 - 2 * offset);
   mPoints[0][1] = new Point(offset * 3 + mRadius, getHeight() / 2 - 2 * offset);
   mPoints[0][2] = new Point(offset * 5 + mRadius, getHeight() / 2 - 2 * offset);
   mPoints[1][0] = new Point(offset + mRadius, getHeight() / 2);
   mPoints[1][1] = new Point(offset * 3 + mRadius, getHeight() / 2);
   mPoints[1][2] = new Point(offset * 5 + mRadius, getHeight() / 2);
   mPoints[2][0] = new Point(offset + mRadius, getHeight() / 2 + 2 * offset);
   mPoints[2][1] = new Point(offset * 3 + mRadius, getHeight() / 2 + 2 * offset);
   mPoints[2][2] = new Point(offset * 5 + mRadius, getHeight() / 2 + 2 * offset);
  }

 }

大家可以看到,我来给点定坐标是,是按照比较窄的边的 1/7 作为点的直径,这样保证了,不管你怎么定义 handleLock 的宽高,都可以使里面的九个点看起来位置很舒服。

接下来我们就需要写一些函数,将点、线绘制到控件上,我自己把绘制分成了三部分,一部分是点,一部分是点与点之间的线,一部分是手势的小点和手势到最新点的线。

// 画点,按照我们选择的半径画九个圆
 private void drawPoints(Canvas canvas) {
  // 便利所有的点,并且判断这些点的状态
  for (int i = 0; i < 3; i++) {
   for (int j = 0; j < 3; j++) {
    Point point = mPoints[i][j];
    switch (point.state) {
     case Point.POINT_STATUS_NORMAL:
      canvas.drawCircle(point.mX, point.mY, mRadius, mNormalPaint);
      break;
     case Point.POINT_STATUS_CLICK:
      canvas.drawCircle(point.mX, point.mY, mRadius, mClickPaint);
      break;
     case Point.POINT_STATUS_ERROR:
      canvas.drawCircle(point.mX, point.mY, mRadius, mErrorPaint);
      break;
     default:
      break;

    }
   }
  }
 }
 // 画点与点之间的线
 private void drawLines(Canvas canvas) {
  // 判断手势是否已经划过点了
  if (mClickPointsList.size() > 0) {
   Point prePoint = mClickPointsList.get(0);
   // 将所有已选择点的按顺序连线
   for (int i = 1; i < mClickPointsList.size(); i++) {
    // 判断已选择点的状态
    if (prePoint.state == Point.POINT_STATUS_CLICK) {
     mClickPaint.setStrokeWidth(7);
     canvas.drawLine(prePoint.mX, prePoint.mY, mClickPointsList.get(i).mX, mClickPointsList.get(i).mY, mClickPaint);
    }
    if (prePoint.state == Point.POINT_STATUS_ERROR) {
     mErrorPaint.setStrokeWidth(7);
     canvas.drawLine(prePoint.mX, prePoint.mY, mClickPointsList.get(i).mX, mClickPointsList.get(i).mY, mErrorPaint);
    }
    prePoint = mClickPointsList.get(i);
   }

  }

 }
 // 画手势点
 private void drawFinger(Canvas canvas) {
  // 有选择点后再出现手势点
  if (mClickPointsList.size() > 0) {
   canvas.drawCircle(mHandleX, mHandleY, mRadius / 2, mClickPaint);
  }
  // 最新点到手指的连线,判断是否有已选择的点,有才能画
  if (mClickPointsList.size() > 0) {
   canvas.drawLine(mClickPointsList.get(mClickPointsList.size() - 1).mX, mClickPointsList.get(mClickPointsList.size() - 1).mY,
     mHandleX, mHandleY, mClickPaint);
  }
 }

上面的代码我们看到需要使用到手势划过的点,我们是怎么选择的呢?

// 获取手指移动中选取的点
private int[] getPositions() {
 Point point = new Point(mHandleX, mHandleY);
 int[] position = new int[2];
 // 遍历九个点,看手势的坐标是否在九个圆内,有则返回这个点的两个下标
 for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
   if (mPoints[i][j].getInstance(point) <= mRadius) {
    position[0] = i;
    position[1] = j;
    return position;
   }
  }

 }
 return null;
}

我们需要重写其 onTouchEvent 来通过手势动作来提交选择的点,并更新视图:

// 重写点击事件
 @Override
 public boolean onTouchEvent(MotionEvent event) {
  // 获取手势的坐标
  mHandleX = event.getX();
  mHandleY = event.getY();
  int[] position;
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    position = getPositions();
    // 判断点下时是否选择到点
    if (position != null) {
     // 添加到已选择点中,并改变其状态
     mClickPointsList.add(mPoints[position[0]][position[1]]);
     mPoints[position[0]][position[1]].state = Point.POINT_STATUS_CLICK;
     // 保存路径,依次保存其横纵下标
     mRoute.append(position[0]);
     mRoute.append(position[1]);
    }
    break;
   case MotionEvent.ACTION_MOVE:
    position = getPositions();
    // 判断手势移动时是否选择到点
    if (position != null) {
     // 判断当前选择的点是否已经被选择过
     if (!mClickPointsList.contains(mPoints[position[0]][position[1]])) {
      // 添加到已选择点中,并改变其状态
      mClickPointsList.add(mPoints[position[0]][position[1]]);
      mPoints[position[0]][position[1]].state = Point.POINT_STATUS_CLICK;
      // 保存路径,依次保存其横纵下标
      mRoute.append(position[0]);
      mRoute.append(position[1]);
     }
    }
    break;
   case MotionEvent.ACTION_UP:
    // 重置数据
    resetData();
    break;
   default:
    break;
  }
  // 更新视图
  invalidate();

  return true;
 }
// 重置数据
 private void resetData() {
  // 将所有选择过的点的状态改为正常
  for (Point point :
    mClickPointsList) {
   point.state = Point.POINT_STATUS_NORMAL;
  }
  // 清空已选择点
  mClickPointsList.clear();
  // 清空保存的路径
  mRoute = new StringBuilder();
  // 不再画错误状态
  isDrawError = false;
 }

那我们怎么绘制视图呢?我们通过重写其 onDraw() 方法:

@Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  // 判断是否画错误状态,画错误状态不需要画手势点已经于最新选择点的连线
  if (isDrawError) {
   drawPoints(canvas);
   drawLines(canvas);
  } else {
   drawPoints(canvas);
   drawLines(canvas);
   drawFinger(canvas);
  }
 }

那么这个手势密码绘制过程就结束了,但是整个控件还没有结束,我们还需要给它一个监听器,监听其绘制完成,选择后续事件:

private OnDrawFinishListener mListener;

 // 定义绘制完成的接口
 public interface OnDrawFinishListener {
  public boolean drawFinish(String route);
 }

 // 定义绘制完成的方法,传入接口
 public void setOnDrawFinishListener(OnDrawFinishListener listener) {
  this.mListener = listener;
 }

然后我们就需要在手势离开的时候 ,来进行绘制完成时的事件:

case MotionEvent.ACTION_UP:
    // 完成时回调绘制完成的方法,返回比对结果,判断手势密码是否正确
    mListener.drawFinish(mRoute.toString());
    // 返回错误,则将所有已选择点状态改为错误
    if (!mListener.drawFinish(mRoute.toString())) {
     for (Point point :
       mClickPointsList) {
      point.state = Point.POINT_STATUS_ERROR;
     }
     // 将是否绘制错误设为 true
     isDrawError = true;
     // 刷新视图
     invalidate();
     // 这里我们使用 handler 异步操作,使其错误状态保持 0.5s
     new Thread(new Runnable() {
      @Override
      public void run() {
       if (!mListener.drawFinish(mRoute.toString())) {
        Message message = new Message();
        message.arg1 = 0;
        handler.sendMessage(message);
       }
      }
     }).run();
    } else {
     resetData();
    }
    invalidate();

    break;
private Handler handler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
   switch (msg.arg1) {
    case 0:
     try {
      // 沉睡 0.5s
      Thread.sleep(500);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
     // 重置数据,并刷新视图
     resetData();
     invalidate();
     break;
    default:
     break;
   }

  }
 };

好了,handleLock,整个过程就结束了,笔者这里定义了一个监听器只是给大家提供一种思路,笔者将保存的大路径传给了使用者,是为了保证使用者可以自己保存密码,并作相关操作,大家也可以使用 HandleLock 来  保存密码,不传给使用者,根据自己的需求写出更多更丰富的监听器,而且这里笔者在 MotionEvent.ACTION_UP 中直接回调了 drawFinish() 方法,就意味着要使用该 HandleLock 就必须给它设置监听器。

接下来我们说说 HandleLock 的使用,首先是在布局文件中使用:

<com.example.a01378359.testapp.lock.HandleLock
  android:id="@+id/handlelock_test"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />

接下来是代码中使用:

handleLock = findViewById(R.id.handlelock_test);
  handleLock.setOnDrawFinishListener(new HandleLock.OnDrawFinishListener() {
   @Override
   public boolean drawFinish(String route) {
    // 第一次滑动,则保存密码
    if (count == 0){
     password = route;
     count++;
     Toast.makeText(LockTestActivity.this,"已保存密码",Toast.LENGTH_SHORT).show();
     return true;
    }else {
     // 与保存密码比较,返回结果,并且做出相应事件
     if (password.equals(route)){
      Toast.makeText(LockTestActivity.this,"密码正确",Toast.LENGTH_SHORT).show();
      return true;
     }else {
      Toast.makeText(LockTestActivity.this,"密码错误",Toast.LENGTH_SHORT).show();
      return false;
     }
    }
   }
  });

项目地址:源代码

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

(0)

相关推荐

  • Android手势密码的实现

    一.大致界面介绍: 图1 图2 图3 图4 图1:手势密码绘制界面 [主要是绘制上方的9个提示图标和9个宫格密码图标] 图2:设置手势密码 [监听手势的输入,TouchEvent的事件处理,获取输入的手势密码,同时显示在上方的提示区域] 图3:再绘制一次,两次密码不一致提示界面 [这里在实现的时候,错误提示文字加了"左右晃动的动画",错误路径颜色标记为红色] 图4:校验手势密码,输入的密码错误,给予红色路径+错误文字提示 二.实现思路: 1. 正上方的提示区域,用一个类(LockInd

  • Android自定义UI手势密码终结版

    之前写过3篇手势密码的demo,不过没有集成到真实的企业项目中,这几天正好领到一个手势密码项目,昨天刚好弄完,今天抽空整理下,目前还没有完善,有一些地方需要更改,不过基本的流程都可以跑通了. 源码下载地址:http://xiazai.jb51.net/201610/yuanma/AndroidGestureLock(jb51.net).rar 先看主界面的入口把.里面有2个button(一个是设置手势密码.一个是校验手势密码) activity_main.xml <RelativeLayout

  • Android九宫格手势密码代码设计

    最近因为项目需要用到九宫格密码(也叫手势轨迹密码),特地去学习了一下九宫格密码的实现原来.可能有人认为九宫格密码事例网上到处都有,很多甚至直接拷贝过来就可以运行为什么还要学习呢.还特地写到网上有必要吗?其实写这个目的是为了增强和锻炼自己的语言组织能力.其实如果只是既然是学习就不光是要知道答案(完成效果)更重要的是知道他什么实现. 好了题外话就不多说了,要实现九宫格密码要解决的问题有: 1.给九宫格密码界面布局九个点,即确定每个节点的位置.每个点到上下左右的距离都是相同的.即九个点刚好围成一个正方

  • Android自定义UI手势密码简单版

    先看看效果图: ImageLockActivity package com.example.imagelock; import com.example.view.NinePointLineView; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; public class ImageLockActivity extends Acti

  • Android仿支付宝手势密码解锁功能

    Starting 创建手势密码可以查看 CreateGestureActivity.java 文件. 登陆验证手势密码可以看 GestureLoginActivity.java 文件. Features 使用了 JakeWharton/butterknife butterknife 使用了 ACache 来存储手势密码 /** * 保存手势密码 */ private void saveChosenPattern(List<LockPatternView.Cell> cells) { byte[

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

    Android手势解锁密码效果图 首先呢想写这个手势密码的想法呢,完全是凭空而来的,然后笔者就花了一天时间弄出来了.本以为这个东西很简单,实际上手的时候发现,还有很多逻辑需要处理,稍不注意就容易乱套.写个UI效果图大约只花了3个小时,但是处理逻辑就处理了2个小时!废话不多说,下面开始讲解.      楼主呢,自己比较自定义控件,什么东西都掌握在自己的手里感觉那是相当不错(对于赶工期的小伙瓣儿们还是别手贱了,非常容易掉坑),一有了这个目标,我就开始构思实现方式.      1.整个自定义控件是继承

  • Android手势密码实现实例代码

    一.效果实现 二.实现思路: 1. 正上方的提示区域,用一个类(LockIndicator.java)来实现,自定义view来绘制9个提示图标: 2. 手势密码绘制区域,用一个类(GestureContentView.java)来实现,它继承自ViewGroup里面, 添加9个ImageView来表示图标, 在onLayout()方法中设置它们的位置: 3. 手势路径绘制, 用一个类(GestureDrawline.java)来实现,复写onTouchEvent()方法,在这个方法里面监听Tou

  • Android手势密码view学习笔记(一)

    刚接触Android的时候看到别人写的手势密码view,然后当时就在想,我什么时候才能写出如此高端的东西?? 没关系,不要怕哈,说出这样话的人不是你技术不咋地而是你不愿意花时间去研究它,其实也没有那么难哦(世上无难事,只怕有心人!),下面我们就一步一步实现一个手势密码view. 想必都看过手势密码view,但我们还是看看我们今天要实现的效果吧: 上面是一个手势view的提示view,下面是一个手势view. 用法: <com.leo.library.view.GestureContentView

  • Android自定义UI手势密码改进版源码下载

    在之前文章的铺垫下,再为大家分享一篇:Android手势密码,附源码下载,不要错过. 源码下载:http://xiazai.jb51.net/201610/yuanma/androidLock(jb51.net).rar 先看第一张图片的布局文件 activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://sc

  • Android手势密码view学习笔记(二)

    我们还是接着我们上一篇博客中的内容往下讲哈,上一节 Android手势密码view笔记(一)我们已经实现了我们的IndicatorView指示器view了: 下面我们来实现下我们的手势密码view: 实现思路: 1.我们照样需要拿到用户需要显示的一些属性(行.列.选中的图片.未选中的图片.错误显示的图片.连接线的宽度跟颜色......). 2.我们需要根据手势的变换然后需要判断当前手指位置是不是在某个点中,在的话就把该点设置为选中状态,然后每移动到两个点(也就是一个线段)就记录该两个点. 3.最

随机推荐