zoukankan      html  css  js  c++  java
  • 教你如何实现android上的九点连线锁

    教你如何实现android上的九点连线锁 - 周柯文 - 博客园

    这两天研究了View类,自己实现了一个九点连线锁,把心得分享下。

    下面是实现截图:

    我的思路是,首先绘制每个点,就是中间的小蓝点,当手指触摸到某个点的范围内时(就是当ACTION_DOWN发生在某个范围内时),绘制灰色大圆;当手指移动时(ACTION_MOVE),绘制每个点之间的线段,和最后一个点到手指当前位置的线段;当手指抬起时,把所有相关的坐标值设为初值0,并设置标志onUp为true,来等待用户下次画线。

    我固定的给每个点设置了一个ID,如图:

     

    然后设置了个全局的StringBuffer lockString ,每当用户滑动到某个点的范围内时,就向 lockString 的末尾添加这个点的ID,最终生成的String就可以保存在手机里,日后验证时就拿这个String验证。

    当然里面还有很多细节,下面是整个NinePointLineView.java的源代码,通过注释应该就明白了(文章最后有整个程序的源码的下载链接):

    复制代码
      1 package org.demo.custon_view;
      2 
      3 import org.demo.utils.MLog;
      4 
      5 import android.content.Context;
      6 import android.graphics.Bitmap;
      7 import android.graphics.BitmapFactory;
      8 import android.graphics.Canvas;
      9 import android.graphics.Color;
     10 import android.graphics.Paint;
     11 import android.graphics.Paint.Cap;
     12 import android.graphics.Typeface;
     13 import android.util.AttributeSet;
     14 import android.view.MotionEvent;
     15 import android.view.View;
     16 
     17 public class NinePointLineView extends View {
     18 
     19     Paint linePaint = new Paint();
     20 
     21     Paint whiteLinePaint = new Paint();
     22 
     23     Paint textPaint = new Paint();
     24 
     25     // 由于两个图片都是正方形,所以获取一个长度就行了 26     Bitmap defaultBitmap = BitmapFactory.decodeResource(getResources(),
     27             R.drawable.lock);
     28     int defaultBitmapRadius = defaultBitmap.getWidth() / 2;
     29 
     30     // 初始化被选中图片的直径、半径 31     Bitmap selectedBitmap = BitmapFactory.decodeResource(getResources(),
     32             R.drawable.indicator_lock_area);
     33     int selectedBitmapDiameter = selectedBitmap.getWidth();
     34     int selectedBitmapRadius = selectedBitmapDiameter / 2;
     35 
     36     // 定义好9个点的数组 37     PointInfo[] points = new PointInfo[9];
     38 
     39     // 相应ACTION_DOWN的那个点 40     PointInfo startPoint = null;
     41 
     42     // 屏幕的宽高 43     int width, height;
     44 
     45     // 当ACTION_MOVE时获取的X,Y坐标 46     int moveX, moveY;
     47 
     48     // 是否发生ACTION_UP 49     boolean isUp = false;
     50 
     51     // 最终生成的用户锁序列 52     StringBuffer lockString = new StringBuffer();
     53 
     54     public NinePointLineView(Context context) {
     55         super(context);
     56         this.setBackgroundColor(Color.WHITE);
     57         initPaint();
     58     }
     59 
     60     public NinePointLineView(Context context, AttributeSet attrs) {
     61         super(context, attrs);
     62         this.setBackgroundColor(Color.WHITE);
     63         initPaint();
     64     }
     65 
     66     @Override
     67     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     68         MLog.i("onMeasure");
     69         // 初始化屏幕大小 70         width = getWidth();
     71         height = getHeight();
     72         if (width != 0 && height != 0) {
     73             initPoints(points);
     74         }
     75         MLog.i("width、height = " + width + "、" + height);
     76         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     77     }
     78 
     79     @Override
     80     protected void onLayout(boolean changed, int left, int top, int right,
     81             int bottom) {
     82         MLog.i("onLayout");
     83         super.onLayout(changed, left, top, right, bottom);
     84     }
     85 
     86     private int startX = 0, startY = 0;
     87 
     88     @Override
     89     protected void onDraw(Canvas canvas) {
     90 
     91         canvas.drawText("用户的滑动顺序:" + lockString, 0, 40, textPaint);
     92 
     93         if (moveX != 0 && moveY != 0 && startX != 0 && startY != 0) {
     94             // 绘制当前活动的线段 95             drawLine(canvas, startX, startY, moveX, moveY);
     96         }
     97 
     98         drawNinePoint(canvas);
     99 
    100         super.onDraw(canvas);
    101     }
    102 
    103     // 记住,这个DOWN和MOVE、UP是成对的,如果没从UP释放,就不会再获得DOWN;
    104 // 而获得DOWN时,一定要确认消费该事件,否则MOVE和UP不会被这个View的onTouchEvent接收105     @Override
    106     public boolean onTouchEvent(MotionEvent event) {
    107 
    108         boolean flag = true;
    109 
    110         if (isUp) {// 如果已滑完,重置每个点的属性和lockString111 
    112             finishDraw();
    113 
    114             // 当UP后,要返回false,把事件释放给系统,否则无法获得Down事件115             flag = false;
    116 
    117         } else {// 没滑完,则继续绘制118 
    119             handlingEvent(event);
    120 
    121             // 这里要返回true,代表该View消耗此事件,否则不会收到MOVE和UP事件122             flag = true;
    123 
    124         }
    125         return flag;
    126     }
    127 
    128     private void handlingEvent(MotionEvent event) {
    129         switch (event.getAction()) {
    130         case MotionEvent.ACTION_MOVE:
    131             moveX = (int) event.getX();
    132             moveY = (int) event.getY();
    133             MLog.i("onMove:" + moveX + "、" + moveY);
    134             for (PointInfo temp : points) {
    135                 if (temp.isInMyPlace(moveX, moveY) && temp.isNotSelected()) {
    136                     temp.setSelected(true);
    137                     startX = temp.getCenterX();
    138                     startY = temp.getCenterY();
    139                     int len = lockString.length();
    140                     if (len != 0) {
    141                         int preId = lockString.charAt(len - 1) - 48;
    142                         points[preId].setNextId(temp.getId());
    143                     }
    144                     lockString.append(temp.getId());
    145                     break;
    146                 }
    147             }
    148 
    149             invalidate(0, height - width, width, height);
    150             break;
    151 
    152         case MotionEvent.ACTION_DOWN:
    153             int downX = (int) event.getX();
    154             int downY = (int) event.getY();
    155             MLog.i("onDown:" + downX + "、" + downY);
    156             for (PointInfo temp : points) {
    157                 if (temp.isInMyPlace(downX, downY)) {
    158                     temp.setSelected(true);
    159                     startPoint = temp;
    160                     startX = temp.getCenterX();
    161                     startY = temp.getCenterY();
    162                     lockString.append(temp.getId());
    163                     break;
    164                 }
    165             }
    166             invalidate(0, height - width, width, height);
    167             break;
    168 
    169         case MotionEvent.ACTION_UP:
    170             MLog.i("onUp");
    171             startX = startY = moveX = moveY = 0;
    172             isUp = true;
    173             invalidate();
    174             break;
    175         default:
    176             MLog.i("收到其他事件!!");
    177             break;
    178         }
    179     }
    180 
    181     private void finishDraw() {
    182         for (PointInfo temp : points) {
    183             temp.setSelected(false);
    184             temp.setNextId(temp.getId());
    185         }
    186         lockString.delete(0, lockString.length());
    187         isUp = false;
    188         invalidate();
    189     }
    190 
    191     private void initPoints(PointInfo[] points) {
    192 
    193         int len = points.length;
    194 
    195         int seletedSpacing = (width - selectedBitmapDiameter * 3) / 4;
    196 
    197         // 被选择时显示图片的左上角坐标198         int seletedX = seletedSpacing;
    199         int seletedY = height - width + seletedSpacing;
    200 
    201         // 没被选时图片的左上角坐标202         int defaultX = seletedX + selectedBitmapRadius - defaultBitmapRadius;
    203         int defaultY = seletedY + selectedBitmapRadius - defaultBitmapRadius;
    204 
    205         // 绘制好每个点206         for (int i = 0; i < len; i++) {
    207             if (i == 3 || i == 6) {
    208                 seletedX = seletedSpacing;
    209                 seletedY += selectedBitmapDiameter + seletedSpacing;
    210 
    211                 defaultX = seletedX + selectedBitmapRadius
    212                         - defaultBitmapRadius;
    213                 defaultY += selectedBitmapDiameter + seletedSpacing;
    214 
    215             }
    216             points[i] = new PointInfo(i, defaultX, defaultY, seletedX, seletedY);
    217 
    218             seletedX += selectedBitmapDiameter + seletedSpacing;
    219             defaultX += selectedBitmapDiameter + seletedSpacing;
    220 
    221         }
    222     }
    223 
    224     private void initPaint() {
    225         initLinePaint(linePaint);
    226         initTextPaint(textPaint);
    227         initWhiteLinePaint(whiteLinePaint);
    228     }
    229 
    230     /**231      * 初始化文本画笔
    232      * @param paint
    233 */
    234     private void initTextPaint(Paint paint) {
    235         textPaint.setTextSize(30);
    236         textPaint.setAntiAlias(true);
    237         textPaint.setTypeface(Typeface.MONOSPACE);
    238     }
    239 
    240     /**241      * 初始化黑线画笔
    242      * 
    243      * @param paint
    244 */
    245     private void initLinePaint(Paint paint) {
    246         paint.setColor(Color.GRAY);
    247         paint.setStrokeWidth(defaultBitmap.getWidth());
    248         paint.setAntiAlias(true);
    249         paint.setStrokeCap(Cap.ROUND);
    250     }
    251 
    252     /**253      * 初始化白线画笔
    254      * 
    255      * @param paint
    256 */
    257     private void initWhiteLinePaint(Paint paint) {
    258         paint.setColor(Color.WHITE);
    259         paint.setStrokeWidth(defaultBitmap.getWidth() - 5);
    260         paint.setAntiAlias(true);
    261         paint.setStrokeCap(Cap.ROUND);
    262 
    263     }
    264 
    265     /**266      * 绘制已完成的部分
    267      * 
    268      * @param canvas
    269 */
    270     private void drawNinePoint(Canvas canvas) {
    271 
    272         if (startPoint != null) {
    273             drawEachLine(canvas, startPoint);
    274         }
    275 
    276         // 绘制每个点的图片277         for (PointInfo pointInfo : points) {
    278             if (pointInfo.isSelected()) {// 绘制大圈279                 canvas.drawBitmap(selectedBitmap, pointInfo.getSeletedX(),
    280                         pointInfo.getSeletedY(), null);
    281             }
    282             // 绘制点283             canvas.drawBitmap(defaultBitmap, pointInfo.getDefaultX(),
    284                     pointInfo.getDefaultY(), null);
    285         }
    286 
    287     }
    288 
    289     /**290      * 递归绘制每两个点之间的线段
    291      * 
    292      * @param canvas
    293      * @param point
    294 */
    295     private void drawEachLine(Canvas canvas, PointInfo point) {
    296         if (point.hasNextId()) {
    297             int n = point.getNextId();
    298             drawLine(canvas, point.getCenterX(), point.getCenterY(),
    299                     points[n].getCenterX(), points[n].getCenterY());
    300             // 递归301             drawEachLine(canvas, points[n]);
    302         }
    303     }
    304 
    305     /**306      * 先绘制黑线,再在上面绘制白线,达到黑边白线的效果
    307      * 
    308      * @param canvas
    309      * @param startX
    310      * @param startY
    311      * @param stopX
    312      * @param stopY
    313 */
    314     private void drawLine(Canvas canvas, float startX, float startY,
    315             float stopX, float stopY) {
    316         canvas.drawLine(startX, startY, stopX, stopY, linePaint);
    317         canvas.drawLine(startX, startY, stopX, stopY, whiteLinePaint);
    318     }
    319 
    320     /**321      * 用来表示一个点
    322      * 
    323      * @author zkwlx
    324      * 
    325 */
    326     private class PointInfo {
    327 
    328         // 一个点的ID329         private int id;
    330 
    331         // 当前点所指向的下一个点的ID,当没有时为自己ID332         private int nextId;
    333 
    334         // 是否被选中335         private boolean selected;
    336 
    337         // 默认时图片的左上角X坐标338         private int defaultX;
    339 
    340         // 默认时图片的左上角Y坐标341         private int defaultY;
    342 
    343         // 被选中时图片的左上角X坐标344         private int seletedX;
    345 
    346         // 被选中时图片的左上角Y坐标347         private int seletedY;
    348 
    349         public PointInfo(int id, int defaultX, int defaultY, int seletedX,
    350                 int seletedY) {
    351             this.id = id;
    352             this.nextId = id;
    353             this.defaultX = defaultX;
    354             this.defaultY = defaultY;
    355             this.seletedX = seletedX;
    356             this.seletedY = seletedY;
    357         }
    358 
    359         public boolean isSelected() {
    360             return selected;
    361         }
    362 
    363         public boolean isNotSelected() {
    364             return !isSelected();
    365         }
    366 
    367         public void setSelected(boolean selected) {
    368             this.selected = selected;
    369         }
    370 
    371         public int getId() {
    372             return id;
    373         }
    374 
    375         public int getDefaultX() {
    376             return defaultX;
    377         }
    378 
    379         public int getDefaultY() {
    380             return defaultY;
    381         }
    382 
    383         public int getSeletedX() {
    384             return seletedX;
    385         }
    386 
    387         public int getSeletedY() {
    388             return seletedY;
    389         }
    390 
    391         public int getCenterX() {
    392             return seletedX + selectedBitmapRadius;
    393         }
    394 
    395         public int getCenterY() {
    396             return seletedY + selectedBitmapRadius;
    397         }
    398 
    399         public boolean hasNextId() {
    400             return nextId != id;
    401         }
    402 
    403         public int getNextId() {
    404             return nextId;
    405         }
    406 
    407         public void setNextId(int nextId) {
    408             this.nextId = nextId;
    409         }
    410 
    411         /**412          * 坐标(x,y)是否在当前点的范围内
    413          * 
    414          * @param x
    415          * @param y
    416          * @return417 */
    418         public boolean isInMyPlace(int x, int y) {
    419             boolean inX = x > seletedX
    420                     && x < (seletedX + selectedBitmapDiameter);
    421             boolean inY = y > seletedY
    422                     && y < (seletedY + selectedBitmapDiameter);
    423 
    424             return (inX && inY);
    425         }
    426 
    427     }
    428 
    429 }
    复制代码

    其实这个实现有个不完善的地方,就是表示用户整个滑动的顺序,我看一般的android手机上都是用小箭头代表,那个我想了好久也没想出来怎么实现,就自己发明了个方法,就是图中那种一层一层覆盖的方法,不过效果没有那个箭头好,嘿嘿。

     

    我写这个View时特意让他独立些,要用的时候就跟普通View一样。

    如果有什么不明白的,请直接留言,我会很快回复的,当然也可以给我发邮件:D

    下面是整个程序的源码:

    http://files.cnblogs.com/coding-way/MyCustomView.zip

  • 相关阅读:
    MVC思想-程序的控制流程-Struts2和SpringMVC黑马流程图
    代理模式
    显卡
    感悟:Java新手一点想法
    java企业级开发的实质就是前台后台如何交互的-各个对象之间如何交互,通信的-程序执行的流程是怎样的
    $.ajax()方法详解--极快瑞中的阿贾克斯函数
    初学者必读之AJAX简单实例2
    初学者必读原生AJAX-异步的javaScript和XML
    c#输入方法名来调用方法(反射)
    unity接入讯飞教程
  • 原文地址:https://www.cnblogs.com/seven1979/p/4171576.html
Copyright © 2011-2022 走看看