很多品牌的Android手机都实现了图案解锁屏幕的功能,有些应用程序出于保护的目的也使用了图案锁(比如支付宝),本文将介绍一种图案锁的实现方式,这种实现的一个优势在于方便扩展和自定义,我们先看一下效果图。
首先是连线阶段,整个连线为两部分:第一部分是点和点之间的固定线段,第二部分是最后一个点到鼠标移动位置的自由线段。
接下来是连线结束之后,需要判断图案是否正确,我这里暂时写死的Z字形为正确图案,实际应用时需要记录用户的输入为设置的图案密码。
首先我们考虑在哪里完成点和线的绘图。通常我们想到的是写一个自定义的View(即继承自View类),添加onTouchEvent进行控制,同时覆写onDraw()方法,完成绘制。不过我这里没有采用这种方式,考虑到onTouchEvent只能接收在View之上的触摸事件,从上面第一张图中可以看出,如果文字和自定义View平铺摆放的话,那么当手指滑动到文字上面的时候,已经超出了自定义View的范围,因此无法响应触摸事件。虽说有一种补救方式,就是让其他控件和自定义View叠在一起,即摆放在一个FrameLayout里面,不过帧布局对控件位置的控制不像RelativeLayout这样灵活,因此我的实现方式是自定义RelativeLayout,并且在dispatchDraw()方法里,完成点和线的绘制。dispatchDraw()会在布局绘制子控件时调用,具体的可以参考谷歌官方文档。
首先需要有一个类来记录九个圆点的基本信息。我们可以视为这九个圆是分布于3*3的方格子里面,其中每一个圆位于方格子的中心,在绘制这些圆时,有以下基本信息是要知道的:
1、这些方格子的位置(左上角的X,Y坐标)
2、方格子的边长有多大?
3、方格子的边到圆的边有多大的间隔?
4、圆心的位置(圆心X,Y坐标)
5、圆的半径是多少?
6、这个圆当前应该显示什么颜色?(即圆点的状态)
7、由于我们不可能记录图案整体,而是记录连接点的顺序,那么这个圆所表示的密码值是多少?
不过上面这7个值是相互依赖的,比如我知道了1和2,就能知道4;知道了2和3,就能知道5。因此,在定义这些值的时候,应当让用户提供充分但不冲突的信息(比如我这里从外部获取的是1、2、3、6、7,而4和5是算出来的)。我在实现的时候,把定义下来就再也用不到的信息写在了一个类里面,把绘制点时还需要获取的信息写在了另一个类里面,并且这个类提供了一些外部调用的方法(实际上这两个类合二为一是完全合理的),代码如下。
package com.liusiqian.patternlock;
/**
* Créé par liusiqian 15/12/18.
*/
public abstract class PatternPointBase
{
protected int centerX; //圆心X
protected int centerY; //圆心Y
protected int radius; //半径
protected String tag; //密码标签
public int status; //状态
public static final int STATE_NORMAL = 0; //正常
public static final int STATE_SELECTED = 1; //选中
public static final int STATE_ERROR = 2; //错误
public int getCenterX()
{
return centerX;
}
public int getCenterY()
{
return centerY;
}
public boolean isPointArea(double x, double y)
{
double len = Math.sqrt(Math.pow(centerX - x, 2) + Math.pow(centerY - y, 2));
return radius > len;
}
public String getTag()
{
return tag;
}
public int getRadius()
{
return radius;
}
}
package com.liusiqian.patternlock;
/**
* Créé par liusiqian 15/12/18.
*/
public class PatternPoint extends PatternPointBase
{
protected static final int MIN_SIDE = 20; //最小边长
protected static final int MIN_PADDING = 4; //最小间隔
protected static final int MIN_RADIUS = 6; //最小半径
protected int left, top, side, padding; //side:边长
public PatternPoint(int left, int top, int side, int padding, String tag)
{
this.left = left;
this.top = top;
this.tag = tag;
if (side < MIN_SIDE)
{
side = MIN_SIDE;
}
this.side = side;
if (padding < MIN_PADDING)
{
padding = MIN_PADDING;
}
radius = side / 2 - padding;
if (radius < MIN_RADIUS)
{
radius = MIN_RADIUS;
padding = side / 2 -