Android中图案锁的实现

Posted Manaasdfasdf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中图案锁的实现相关的知识,希望对你有一定的参考价值。

原文地址:http://blog.csdn.net/liusiqian0209/article/details/50372448



很多品牌的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是算出来的)。我在实现的时候,把定义下来就再也用不到的信息写在了一个类里面,把绘制点时还需要获取的信息写在了另一个类里面,并且这个类提供了一些外部调用的方法(实际上这两个类合二为一是完全合理的),代码如下。

<code class="hljs java has-numbering"><span class="hljs-keyword">package</span> com.liusiqian.patternlock;

<span class="hljs-javadoc">/**
 * Créé par liusiqian 15/12/18.
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatternPointBase</span>
{</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">int</span> centerX;     <span class="hljs-comment">//圆心X</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">int</span> centerY;     <span class="hljs-comment">//圆心Y</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">int</span> radius;      <span class="hljs-comment">//半径</span>
    <span class="hljs-keyword">protected</span> String tag;      <span class="hljs-comment">//密码标签</span>

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> status;         <span class="hljs-comment">//状态</span>

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> STATE_NORMAL = <span class="hljs-number">0</span>;       <span class="hljs-comment">//正常</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> STATE_SELECTED = <span class="hljs-number">1</span>;     <span class="hljs-comment">//选中</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> STATE_ERROR = <span class="hljs-number">2</span>;        <span class="hljs-comment">//错误</span>

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCenterX</span>()
    {
        <span class="hljs-keyword">return</span> centerX;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCenterY</span>()
    {
        <span class="hljs-keyword">return</span> centerY;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isPointArea</span>(<span class="hljs-keyword">double</span> x, <span class="hljs-keyword">double</span> y)
    {
        <span class="hljs-keyword">double</span> len = Math.sqrt(Math.pow(centerX - x, <span class="hljs-number">2</span>) + Math.pow(centerY - y, <span class="hljs-number">2</span>));
        <span class="hljs-keyword">return</span> radius > len;
    }

    <span class="hljs-keyword">public</span> String <span class="hljs-title">getTag</span>()
    {
        <span class="hljs-keyword">return</span> tag;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getRadius</span>()
    {
        <span class="hljs-keyword">return</span> radius;
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li></ul>
<code class="hljs java has-numbering"><span class="hljs-keyword">package</span> com.liusiqian.patternlock;

<span class="hljs-javadoc">/**
 * Créé par liusiqian 15/12/18.
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatternPoint</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">PatternPointBase</span>
{</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> MIN_SIDE = <span class="hljs-number">20</span>;        <span class="hljs-comment">//最小边长</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> MIN_PADDING = <span class="hljs-number">4</span>;        <span class="hljs-comment">//最小间隔</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> MIN_RADIUS = <span class="hljs-number">6</span>;        <span class="hljs-comment">//最小半径</span>

    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">int</span> left, top, side, padding;     <span class="hljs-comment">//side:边长</span>

    <span class="hljs-keyword">public</span> <span class="hljs-title">PatternPoint</span>(<span class="hljs-keyword">int</span> left, <span class="hljs-keyword">int</span> top, <span class="hljs-keyword">int</span> side, <span class="hljs-keyword">int</span> padding, String tag)
    {
        <span class="hljs-keyword">this</span>.left = left;
        <span class="hljs-keyword">this</span>.top = top;
        <span class="hljs-keyword">this</span>.tag = tag;

        <span class="hljs-keyword">if</span> (side < MIN_SIDE)
        {
            side = MIN_SIDE;
        }
        <span class="hljs-keyword">this</span>.side = side;

        <span class="hljs-keyword">if</span> (padding < MIN_PADDING)
        {
            padding = MIN_PADDING;
        }

        radius = side / <span class="hljs-number">2</span> - padding;
        <span class="hljs-keyword">if</span> (radius < MIN_RADIUS)
        {
            radius = MIN_RADIUS;
            padding = side / <span class="hljs-number">2</span> - radius;
        }
        <span class="hljs-keyword">this</span>.padding = padding;
        centerX = left + side / <span class="hljs-number">2</span>;
        centerY = top + side / <span class="hljs-number">2</span>;
        status = STATE_NORMAL;
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul>

  可以看到,在基类里面定义了圆点的状态常量。此外还提供了一个方法叫做isPointArea(),这个方法用于判断对于给定的一个点,它是否在这个圆之内。我们在进行连线时,如果经过了一个点,则需要把它连接起来,这时需要用到这个函数。
  接下来是这个扩展的RelativeLayout,这里先给出整个类的代码,然后再逐步解释。

<code class="hljs java has-numbering"><span class="hljs-keyword">package</span> com.liusiqian.patternlock;

<span class="hljs-keyword">import</span> android.content.Context;
<span class="hljs-keyword">import</span> android.graphics.Canvas;
<span class="hljs-keyword">import</span> android.graphics.Paint;
<span class="hljs-keyword">import</span> android.os.Handler;
<span class="hljs-keyword">import</span> android.util.AttributeSet;
<span class="hljs-keyword">import</span> android.view.MotionEvent;
<span class="hljs-keyword">import</span> android.widget.RelativeLayout;

<span class="hljs-keyword">import</span> java.util.ArrayList;

<span class="hljs-javadoc">/**
 * Créé par liusiqian 15/12/18.
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatternLockLayout</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">RelativeLayout</span>
{</span>
    <span class="hljs-keyword">public</span> <span class="hljs-title">PatternLockLayout</span>(Context context)
    {
        <span class="hljs-keyword">super</span>(context);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-title">PatternLockLayout</span>(Context context, AttributeSet attrs)
    {
        <span class="hljs-keyword">super</span>(context, attrs);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-title">PatternLockLayout</span>(Context context, AttributeSet attrs, <span class="hljs-keyword">int</span> defStyleAttr)
    {
        <span class="hljs-keyword">super</span>(context, attrs, defStyleAttr);
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> hasinit;                <span class="hljs-comment">//初始化是否完成</span>
    <span class="hljs-keyword">private</span> PatternPoint[] points = <span class="hljs-keyword">new</span> PatternPoint[<span class="hljs-number">9</span>];        <span class="hljs-comment">//九个圆圈对象</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> width, height, side;                        <span class="hljs-comment">//布局可用宽,布局可用高,小方格子的边长</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> sidePadding, topBottomPadding;      <span class="hljs-comment">//侧边和上下边预留空间</span>

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> startLine;      <span class="hljs-comment">//是否开始连线</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> errorMode;      <span class="hljs-comment">//连线是否使用表示错误的颜色</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> drawEnd;        <span class="hljs-comment">//是否已经抬手</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> resetFinished;  <span class="hljs-comment">//重置是否已经完成(是否可以进行下一次连线)</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">float</span> moveX, moveY;     <span class="hljs-comment">//手指位置</span>
    <span class="hljs-keyword">private</span> ArrayList<PatternPoint> selectedPoints = <span class="hljs-keyword">new</span> ArrayList<>();     <span class="hljs-comment">//所有已经选中的点</span>

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> PAINT_COLOR_NORMAL = <span class="hljs-number">0xffcccccc</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> PAINT_COLOR_SELECTED = <span class="hljs-number">0xff00dd00</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> PAINT_COLOR_ERROR = <span class="hljs-number">0xffdd0000</span>;

    <span class="hljs-keyword">private</span> Handler mHandler;

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">dispatchDraw</span>(Canvas canvas)
    {
        <span class="hljs-keyword">super</span>.dispatchDraw(canvas);
        <span class="hljs-keyword">if</span> (!hasinit)
        {
            <span class="hljs-comment">//暂时写死,后面通过XML设置</span>
            sidePadding = <span class="hljs-number">40</span>;
            topBottomPadding = <span class="hljs-number">40</span>;
            initPoints();
            resetFinished = <span class="hljs-keyword">true</span>;
        }

        drawCircle(canvas);
        drawLine(canvas);
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onTouchEvent</span>(MotionEvent event)
    {
        moveX = event.getX();
        moveY = event.getY();

        <span class="hljs-keyword">switch</span> (event.getAction())
        {
            <span class="hljs-keyword">case</span> MotionEvent.ACTION_DOWN:
            {
                <span class="hljs-keyword">int</span> index = whichPointArea();
                <span class="hljs-keyword">if</span> (-<span class="hljs-number">1</span> != index && resetFinished)
                {
                    addSelectedPoint(index);
                    startLine = <span class="hljs-keyword">true</span>;
                }
            }
            <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> MotionEvent.ACTION_MOVE:
            {
                <span class="hljs-keyword">if</span> (startLine && resetFinished)
                {
                    <span class="hljs-keyword">int</span> index = whichPointArea();
                    <span class="hljs-keyword">if</span> (-<span class="hljs-number">1</span> != index && points[index].status == PatternPointBase.STATE_NORMAL)
                    {
                        <span class="hljs-comment">//查看是否有中间插入点</span>
                        insertPointIfNeeds(index);
                        <span class="hljs-comment">//增加此点到队列中</span>
                        addSelectedPoint(index);
                    }
                }
            }
            <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> MotionEvent.ACTION_UP:
            {
                <span class="hljs-keyword">if</span> (startLine && resetFinished)
                {
                    resetFinished = <span class="hljs-keyword">false</span>;
                    <span class="hljs-keyword">int</span> delay = processFinish();
                    mHandler.postDelayed(<span class="hljs-keyword">new</span> Runnable()
                    {
                        <span class="hljs-annotation">@Override</span>
                        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>()
                        {
                            reset();
                        }
                    }, delay);
                }
            }
            <span class="hljs-keyword">break</span>;
        }

        invalidate();

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }


    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAllSelectedPointsError</span>()
    {
        errorMode = <span class="hljs-keyword">true</span>;
        <span class="hljs-keyword">for</span> (PatternPoint point : selectedPoints)
        {
            point.status = PatternPointBase.STATE_ERROR;
        }
        invalidate();
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">reset</span>()
    {
        <span class="hljs-keyword">for</span> (PatternPoint point : points)
        {
            point.status = PatternPointBase.STATE_NORMAL;
        }
        selectedPoints.clear();
        startLine = <span class="hljs-keyword">false</span>;
        errorMode = <span class="hljs-keyword">false</span>;
        drawEnd = <span class="hljs-keyword">false</span>;
        <span class="hljs-keyword">if</span> (listener != <span class="hljs-keyword">null</span>)
        {
            listener.onReset();
        }
        resetFinished = <span class="hljs-keyword">true</span>;
        invalidate();
    }

    <span class="hljs-comment">//返回值为reset延迟的毫秒数</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">processFinish</span>()
    {
        drawEnd = <span class="hljs-keyword">true</span>;
        <span class="hljs-keyword">if</span> (selectedPoints.size() < <span class="hljs-number">2</span>)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
        }
        <span class="hljs-keyword">else</span>            <span class="hljs-comment">//长度过短、密码错误的判断留给外面</span>
        {
            <span class="hljs-keyword">int</span> size = selectedPoints.size();
            StringBuilder sbPassword = <span class="hljs-keyword">new</span> StringBuilder();
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < size; i++)
            {
                sbPassword.append(selectedPoints.get(i).tag);
            }
            <span class="hljs-keyword">if</span> (listener != <span class="hljs-keyword">null</span>)
            {
                listener.onFinish(sbPassword.toString(), size);
            }
            <span class="hljs-keyword">return</span> <span class="hljs-number">2000</span>;
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">OnPatternStateListener</span>
    {</span>
        <span class="hljs-keyword">void</span> onFinish(String password, <span class="hljs-keyword">int</span> sizeOfPoints);

        <span class="hljs-keyword">void</span> onReset();
    }

    <span class="hljs-keyword">private</span> OnPatternStateListener listener;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setOnPatternStateListener</span>(OnPatternStateListener listener)
    {
        <span class="hljs-keyword">this</span>.listener = listener;
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">insertPointIfNeeds</span>(<span class="hljs-keyword">int</span> curIndex)
    {
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span>[][] middleNumMatrix = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[][]{{-<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">3</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">4</span>}, {-<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, -<span class="hljs-number">1</span>}, {<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">5</span>}, {-<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>}, {-<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>}, {-<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>}, {<span class="hljs-number">3</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">7</span>}, {-<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>}, {<span class="hljs-number">4</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">5</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">7</span>, -<span class="hljs-number">1</span>, -<span class="hljs-number">1</span>}};

        <span class="hljs-keyword">int</span> selectedSize = selectedPoints.size();
        <span class="hljs-keyword">if</span> (selectedSize > <span class="hljs-number">0</span>)
        {
            <span class="hljs-keyword">int</span> lastIndex = Integer.parseInt(selectedPoints.get(selectedSize - <span class="hljs-number">1</span>).tag) - <span class="hljs-number">1</span>;
            <span class="hljs-keyword">int</span> middleIndex = middleNumMatrix[lastIndex][curIndex];
            <span class="hljs-keyword">if</span> (middleIndex != -<span class="hljs-number">1</span> && (points[middleIndex].status == PatternPointBase.STATE_NORMAL) && (points[curIndex].status == PatternPointBase.STATE_NORMAL))
            {
                addSelectedPoint(middleIndex);
            }

        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addSelectedPoint</span>(<span class="hljs-keyword">int</span> index)
    {
        selectedPoints.add(points[index]);
        points[index].status = PatternPointBase.STATE_SELECTED;
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> <span class="hljs-title">whichPointArea</span>()
    {
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">9</span>; i++)
        {
            <span class="hljs-keyword">if</span> (points[i].isPointArea(moveX, moveY))
            {
                <span class="hljs-keyword">return</span> i;
            }
        }
        <span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">drawLine</span>(Canvas canvas)
    {
        Paint paint = getCirclePaint(errorMode ? PatternPoint.STATE_ERROR : PatternPoint.STATE_SELECTED);
        paint.setStrokeWidth(<span class="hljs-number">15</span>);

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < selectedPoints.size(); i++)
        {
            <span class="hljs-keyword">if</span> (i != selectedPoints.size() - <span class="hljs-number">1</span>)      <span class="hljs-comment">//连接线</span>
            {
                PatternPoint first = selectedPoints.get(i);
                PatternPoint second = selectedPoints.get(i + <span class="hljs-number">1</span>);
                canvas.drawLine(first.getCenterX(), first.getCenterY(),
                        second.getCenterX(), second.getCenterY(), paint);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!drawEnd)                        <span class="hljs-comment">//自由线,抬手之后就不用画了</span>
            {
                PatternPoint last = selectedPoints.get(i);
                canvas.drawLine(last.getCenterX(), last.getCenterY(),
                        moveX, moveY, paint);
            }
        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">drawCircle</span>(Canvas canvas)
    {
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">9</span>; i++)
        {
            PatternPoint point = points[i];
            Paint paint = getCirclePaint(point.status);
            canvas.drawCircle(point.getCenterX(), point.getCenterY(), points[i].getRadius(), paint);
        }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initPoints</span>()
    {
        width = getWidth() - getPaddingLeft() - getPaddingRight() - sidePadding * <span class="hljs-number">2</span>;
        height = getHeight() - getPaddingTop() - getPaddingBottom() - topBottomPadding * <span class="hljs-number">2</span>;

        <span class="hljs-comment">//使用时暂定强制竖屏(即认定height>width)</span>
        <span class="hljs-keyword">int</span> left, top;
        left = getPaddingLeft() + sidePadding;
        top = height + getPaddingTop() + topBottomPadding - width;
        side = width / <span class="hljs-number">3</span>;

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">3</span>; i++)
        {
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j < <span class="hljs-number">3</span>; j++)
            {
                <span class="hljs-keyword">int</span> leftX = left + j * side;
                <span class="hljs-keyword">int</span> topY = top + i * side;
                <span class="hljs-keyword">int</span> index = i * <span class="hljs-number">3</span> + j;
                points[index] = <span class="hljs-keyword">new</span> PatternPoint(leftX, topY, side, side / <span class="hljs-number">3</span>, String.valueOf(index + <span class="hljs-number">1</span>));
            }
        }

        mHandler = <span class="hljs-keyword">new</span> Handler();

        hasinit = <span class="hljs-keyword">true</span>;
    }

    <span class="hljs-keyword">private</span> Paint <span class="hljs-title">getCirclePaint</span>(<span class="hljs-keyword">int</span> state)
    {
        Paint paint = <span class="hljs-keyword">new</span> Paint();
        <span class="hljs-keyword">switch</span> (state)
        {
            <span class="hljs-keyword">case</span> PatternPoint.STATE_NORMAL:
                paint.setColor(PAINT_COLOR_NORMAL);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> PatternPoint.STATE_SELECTED:
                paint.setColor(PAINT_COLOR_SELECTED);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> PatternPoint.STATE_ERROR:
                paint.setColor(PAINT_COLOR_ERROR);
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">default</span>:
                paint.setColor(PAINT_COLOR_NORMAL);
        }
        <span class="hljs-keyword">return</span> paint;
    }
}
</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li><li>123</li><li>124</li><li>125</li><li>126</li><li>127</li><li>128</li><li>129</li><li>130</li><li>131</li><li>132</li><li>133</li><li>134</li><li>135</li><li>136</li><li>137</li><li>138</li><li>139</li><li>140</li><li>141</li><li>142</li><li>143</li><li>144</li><li>145</li><li>146</li><li>147</li><li>148</li><li>149</li><li>150</li><li>151</li><li>152</li><li>153</li><li>154</li><li>155</li><li>156</li><li>157</li><li>158</li><li>159</li><li>160</li><li>161</li><li>162</li><li>163</li><li>164</li><li>165</li><li>166</li><li>167</li><li>168</li><li>169</li><li>170</li><li>171</li><li>172</li><li>173</li><li>174</li><li>175</li><li>176</li><li>177</li><li>178</li><li>179</li><li>180</li><li>181</li><li>182</li><li>183</li><li>184</li><li>185</li><li>186</li><li>187</li><li>188</li><li>189</li><li>190</li><li>191</li><li>192</li><li>193</li><li>194</li><li>195</li><li>196</li><li>197</li><li>198</li><li>199</li><li>200</li><li>201</li><li>202</li><li>203</li><li>204</li><li>205</li><li>206</li><li>207</li><li>208</li><li>209</li><li>210</li><li>211</li><li>212</li><li>213</li><li>214</li><li>215</li><li>216</li><li>217</li><li>218</li><li>219</li><li>220</li><li>221</li><li>222</li><li>223</li><li>224</li><li>225</li><li>226</li><li>227</li><li>228</li><li>229</li><li>230</li><li>231</li><li>232</li><li>233</li><li>234</li><li>235</li><li>236</li><li>237</li><li>238</li><li>239</li><li>240</li><li>241</li><li>242</li><li>243</li><li>244</li><li>245</li><li>246</li><li>247</li><li>248</li><li>249</li><li>250</li><li>251</li><li>252</li><li>253</li><li>254</li><li>255</li><li>256</li><li>257</li><li>258</li><li>259</li><li>260</li><li>261</li><li>262</li><li>263</li><li>264</li><li>265</li><li>266</li><li>267</li><li>268</li><li>269</li><li>270</li><li>271</li><li>272</li><li>273</li><li>274</li><li>275</li><li>276</li><li>277</li><li>278</li><li>279</li><li>280</li><li>281</li><li>282</li><li>283</li><li>284</li><li>285</li><li>286</li><li>287</li><li>288</li><li>289</li><li>290</li><li>291</li><li>292</li><li>293</li><li>294</li><li>295</li><li>296</li><li>297</li><li>298</li><li>299</li><li>300</li><li>301</li><li>302</li><li>303</li><li>304</li><li>305</li><li>306</li><li>307</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li><li>123</li><li>124</li><li>125</li><li>126</li><li>127</li><li>128</li><li>129</li><li>130</li><li>131</li><li>132</li><li>133</li><li>134</li><li>135</li><li>136</li><li>137</li><li>138</li><li>139</li><li>140</li><li>141</li><li>142</li><li>143</li><li>144</li><li>145</li><li>146</li><li>147</li><li>148</li><li>149</li><li>150</li><li>151</li><li>152</li><li>153</li><li>154</li><li>155</li><li>156</li><li>157</li><li>158</li><li>159</li><li>160</li><li>161</li><li>162</li><li>163</li><li>164</li><li>165</li><li>166</li><li>167</li><li>168</li><li>169</li><li>170</li><li>171</li><li>172</li><li>173</li><li>174</li><li>175</li><li>176</li><li>177</li><li>178</li><li>179</li><li>180</li><li>181</li><li>182</li><li>183</li><li>184</li><li>185</li><li>186</li><li>187</li><li>188</li><li>189</li><li>190</li><li>191</li><li>192</li><li>193</li><li>194</li><li>195</li><li>196</li><li>197</li><li>198</li><li>199</li><li>200</li><li>201</li><li>202</li><li>203</li><li>204</li><li>205</li><li>206</li><li>207</li><li>208</li><li>209</li><li>210</li><li>211</li><li>212</li><li>213</li><li>214</li><li>215</li><li>216</li><li>217</li><li>218</li><li>219</li><li>220</li><li>221</li><li>222</li><li>223</li><li>224</li><li>225</li><li>226</li><li>227</li><li>228</li><li>229</li><li>230</li><li>231</li><li>232</li><li>233</li><li>234</li><li>235</li><li>236</li><li>237</li><li>238</li><li>239</li><li>240</li><li>241</li><li>242</li><li>243</li><li>244</li><li>245</li><li>246</li><li>247</li><li>248</li><li>249</li><li>250</li><li>251</li><li>252</li><li>253</li><li>254</li><li>255</li><li>256</li><li>257</li><li>258</li><li>259</li><li>260</li><li>261</li><li>262</li><li>263</li><li>264</li><li>265</li><li>266</li><li>267</li><li>268</li><li>269</li><li>270</li><li>271</li><li>272</li><li>273</li><li>274</li><li>275</li><li>276</li><li>277</li><li>278</li><li>279</li><li>280</li><li>281</li><li>282</li><li>283</li><li>284</li><li>285</li><li>286</li><li>287</li><li>288</li><li>289</li><li>290</li><li>291</li><li>292</li><li>293</li><li>294</li><li>295</li><li>296</li><li>297</li><li>298</li><li>299</li><li>300</li><li>301</li><li>302</li><li>303</li><li>304</li><li>305</li><li>306</li><li>307</li></ul>

  先梳理一下流程。首先是绘制,在dispatchDraw()方法中的代码如下:

<code class="hljs java has-numbering">    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">dispatchDraw</span>(Canvas canvas)
    {
        <span class="hljs-keyword">super</span>.dispatchDraw(canvas);
        <span class="hljs-keyword">if</span> (!hasinit)
        {
            <span class="hljs-comment">//暂时写死,应该通过XML设置</span>
            sidePadding = <span class="hljs-number">40</span>;
            topBottomPadding = <span class="hljs-number">40</span>;
            initPoints();
            resetFinished = <span class="hljs-keyword">true</span>;
        }

        drawCircle(canvas);
        drawLine(canvas);
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>

  首先先绘制布局中的其他控件,它们与图案锁没有任何关系。接下来分为3步:
  1、初始化。参见initPoints()方法,其作用为创建九个PatternPoint对象,并确定每一个圆的位置和密码。我们之前说视为这九个圆位于3*3的方格子中,不过这3*3的方格子不一定要紧贴着布局的边界,因此定义了两个变量sidePadding和topBottomPadding,用于记录方格子与布局边界之间的距离。不过我这里图省事儿直接将这两个值写死了,实际上最妥当的方案是在attrs.xml中定义这两个属性,然后在布局xml中定义这两个属性的值,最后在源文件中获取这两个属性,并且将它们的值赋值给变量。此外需要注意的是,初始化代码只需执行一次就够了,而dispatchDraw()会反复调用,因此需要一个控制变量记录初始化是否完毕。

  2、画圆。这个比较简单,根据不同圆当前处于的状态进行绘制即可。参见drawCircle()和getCirclePaint()方法。

  3、画线。这是最复杂的一部分,实现部分在drawLine()方法中,首先我们需要知道要画的是哪个颜色的线。从上面的效果展示可知,线的颜色一共分为两种:正在连线时和连线正确时是同一种颜色,另外就是连线错误时的颜色。这里需要使用一个变量记录当前是否处于连线错误状态,并且根据这个变量的值去获取不同的画笔(Paint对象)。
  前面说过,连线分为两部分,一部分是点和点之间的连线(我们称之为连接线),另一部分是最后一个点和当前手指的位置的连线(我们称之为自由线)。无论是连接线还是自由线,都需要知道我之前所有连接过的点的顺序,因此需要一个ArrayList来记录它。在绘制自由线的时候,需要知道当前手指的位置(X,Y坐标),这两个值是在onTouchEvent()中获取的,因此需要两个类变量记录它。此外,当我的手抬起来之后,表示我的一次连线已经结束了,这时是不需要绘制自由线的

以上是关于Android中图案锁的实现的主要内容,如果未能解决你的问题,请参考以下文章

天才程序员教你写一个手势就能解锁的控件!是不是非常神奇!

Android实现九宫格解锁的实例代码

选项卡执行android中下一个片段中存在的代码

Redis实现分布式锁(设计模式应用实战)

Redis实现分布式锁(设计模式应用实战)

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情