原文地址: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()中获取的,因此需要两个类变量记录它。此外,当我的手抬起来之后,表示我的一次连线已经结束了,这时是不需要绘制自由线的,因此这里要额外加一个判断。
接下来分析一下触摸事件,它的设计思路大致如下:
1、在按下时,如果我手指的位置正处于某个点中,那么一次连线开始,并且把这个点加入到选中点的List中,作为第一个点。
2、在移动时,如果我已经开始连线,那么需要明确的是我的选中列中至少已经有一个点了(至少会有一个起始点)。此时需要判断是否经过了某一个点,并且这个点是还没有进入选中列中的点。在满足这些条件之后,进行下面判断:
a)查看我上一个连接的点和这次经过的点中间是否需要插入点(比如上一个点是左上角的点,这里经过的点是右上角的点,并且正上方的点还没有进入选中列,此时,应当将正上方的点加入到选中列中,并且在右上角这个点之前插入)
b)增加这个经过的点到选中列中。
3、在抬起时,如果我已经开始连线,表明我这次连线结束了。这时如果存在连接线而不是仅仅有自由线(即选中列中的点至少有两个),则去计算这个图案对应的密码,提供给外部进行密码长度和密码正误的判断。既然说到要给外部进行回调,因此需要提供一个接口。
4、在每当发生触摸事件之后,都重新绘制连线。
下面强调几个特殊的方法。
1、insertPointIfNeeds(),这个方法用于上面说的触摸事件中2a这个步骤,判断两个点中间是否需要插入额外的一个点到选中队列中。我在程序里把9个点从左到右,从上到下分别标为1-9。那么1和3中需要插入2,4和6中需要插入5等等这些判断,我通过一个常量矩阵进行获取,这样就避免了大片的if,else。矩阵中的值表示需要插入点的index值,-1表示没有。当然有这样的点不一定就表示需要插入到选中列中,还需要满足当前经过的点和中间插入的点之前都没有在选中列中的条件。
2、setAllSelectedPointsError(),这个方法提供给外部Activity调用,当用户判断出图案密码太短或者图案密码错误时,将所有选中列中的点的状态设为错误状态,同时,将连线的颜色设为错误时连线的颜色。注意设置完成之后需要重绘。
3、processFinish(),这个方法主要说一下返回值,从程序中可以看出,它的返回值是一个时间值。因为当用户连线完成之后,无论其连线正确与否,都需要将这个连线图案保持一段时间,而并不是瞬间就恢复到初始状态。
4、reset()方法和resetFinished变量,reset()的作用是将所有记录状态的值都恢复到初始化完成的状态,随后将resetFinished置为true。而在resetFinished为false时,按下、移动、抬起这些触摸事件都是不起作用的。之前说过,当用户连线完成之后,需要保持图案一定时间,而这段时间之内,是不允许用户进行连线的,resetFinished变量的作用就是控制这个部分。reset()方法中,当所有变量都重置之后,又给外部提供了一个回调方法,它的作用是告诉Activity已经重置完成,如果Activity中有关于密码正误判断的显示,则可在这个回调中进行重置。
最后附带上这个扩展的RelativeLayout的使用,即Activity和对应的xml布局中的代码,这部分很容易理解,就不解释了。
<code class="hljs avrasm has-numbering"><<span class="hljs-keyword">com</span><span class="hljs-preprocessor">.liusiqian</span><span class="hljs-preprocessor">.patternlock</span><span class="hljs-preprocessor">.PatternLockLayout</span> xmlns:android=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span> android:id=<span class="hljs-string">"@+id/layout_lock"</span> android:layout_width=<span class="hljs-string">"match_parent"</span> android:layout_height=<span class="hljs-string">"match_parent"</span>> <TextView android:id=<span class="hljs-string">"@+id/txt_patternlock_info"</span> android:layout_width=<span class="hljs-string">"wrap_content"</span> android:layout_height=<span class="hljs-string">"wrap_content"</span> android:layout_centerHorizontal=<span class="hljs-string">"true"</span> android:text=<span class="hljs-string">"信息"</span> android:textSize=<span class="hljs-string">"28sp"</span> android:layout_marginTop=<span class="hljs-string">"60dp"</span>/> </<span class="hljs-keyword">com</span><span class="hljs-preprocessor">.liusiqian</span><span class="hljs-preprocessor">.patternlock</span><span class="hljs-preprocessor">.PatternLockLayout</span>></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>
<code class="hljs java has-numbering"><span class="hljs-keyword">package</span> com.liusiqian.patternlock; <span class="hljs-keyword">import</span> android.app.Activity; <span class="hljs-keyword">import</span> android.os.Bundle; <span class="hljs-keyword">import</span> android.widget.TextView; <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Activity</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PatternLockLayout</span>.<span class="hljs-title">OnPatternStateListener</span> {</span> <span class="hljs-keyword">private</span> TextView tvInfo; <span class="hljs-keyword">private</span> PatternLockLayout lockLayout; <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) { <span class="hljs-keyword">super</span>.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvInfo = (TextView) findViewById(R.id.txt_patternlock_info); tvInfo.setText(<span class="hljs-string">"请绘制图案密码"</span>); lockLayout = (PatternLockLayout) findViewById(R.id.layout_lock); lockLayout.setOnPatternStateListener(<span class="hljs-keyword">this</span>); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onFinish</span>(String password, <span class="hljs-keyword">int</span> sizeOfPoints) { <span class="hljs-keyword">if</span>(sizeOfPoints<<span class="hljs-number">5</span>) { tvInfo.setText(<span class="hljs-string">"请连接至少5个点"</span>); lockLayout.setAllSelectedPointsError(); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>( !password.equals(<span class="hljs-string">"1235789"</span>) ) { tvInfo.setText(<span class="hljs-string">"图案密码错误"</span>); lockLayout.setAllSelectedPointsError(); } <span class="hljs-keyword">else</span> { tvInfo.setText(<span class="hljs-string">"图案正确"</span>); } } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onReset</span>() { tvInfo.setText(<span class="hljs-string">"请绘制图案密码"</span>); } }</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></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></ul>
最后还是要重申一下,这个程序只是阐述了图案锁的核心功能,本身并不完美。很多写死在程序里的变量甚至是常量实际上都可以定义成属性写在xml文件里,然后在layout中配置,这样能使得程序的可扩展性达到一个更高的层次,使用起来更加自如。
原谅我犯懒,只做到这里了~