最终效果如下

1.进行定义实体point点

public class Point {private float x;
    private float y;
    //正常模式
    public static final int NORMAL_MODE = 1;
    //按下模式
    public static final int PRESSED_MODE = 2;
    //错误模式
    public static final int ERROR_MODE = 3;

    private int state  = NORMAL_MODE;
    private String mark;

    public Point(float x, float y, String mark) {this.x = x;
        this.y = y;
        this.mark = mark;
    }public float getX() {return x;
    }public void setX(float x) {this.x = x;
    }public float getY() {return y;
    }public void setY(float y) {this.y = y;
    }public int getState() {return state;
    }public void setState(int state) {this.state = state;
    }public String getMark() {return mark;
    }public void setMark(String mark) {this.mark = mark;
    }
}

2.自定义ScreenLockView

public class ScreenLockView extends View {private static final String TAG = "ScreenLockView";

    // 错误格子的图片
    private Bitmap errorBitmap;
    // 正常格子的图片
    private Bitmap normalBitmap;
    // 手指按下时格子的图片
    private Bitmap pressedBitmap;
    // 错误时连线的图片
    private Bitmap lineErrorBitmap;
    // 手指按住时连线的图片
    private Bitmap linePressedBitmap;
    // 偏移量,使九宫格在屏幕中央
    private int offset;
    // 九宫格的九个格子是否已经初始化
    private boolean init;
    // 格子的半径
    private int radius;
    // 密码
    private String password = "123456";
    // 九个格子
    private Point[][] points = new Point[3][3];
    private int width;
    private int height;
    private Matrix matrix = new Matrix();
    private float moveX = -1;
    private float moveY = -1;
    // 是否手指在移动
    private boolean isMove;
    // 是否可以触摸,当用户抬起手指,划出九宫格的密码不正确时为不可触摸
    private boolean isTouch = true;
    // 用来存储记录被按下的点
    private List<Point> pressedPoint = new ArrayList<>();
    // 屏幕解锁监听器
    private OnScreenLockListener listener;

    public ScreenLockView(Context context) {super(context);
        init();
    }public ScreenLockView(Context context, AttributeSet attrs) {super(context, attrs);
        init();
    }public ScreenLockView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        init();
    }private void init() {errorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_error);
        normalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_normal);
        pressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_pressed);
        lineErrorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.line_error);
        linePressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed);
        radius = normalBitmap.getWidth() / 2;
    }@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (widthSize > heightSize) {offset = (widthSize - heightSize) / 2;
        } else {offset = (heightSize - widthSize) / 2;
        }setMeasuredDimension(widthSize, heightSize);
    }@Override
    protected void onDraw(Canvas canvas) {super.onDraw(canvas);
        if (!init) {width = getWidth();
            height = getHeight();
            initPoint();
            init = true;
        }drawPoint(canvas);
        if (moveX != -1 && moveY != -1) {drawLine(canvas);
        }}// 画直线
    private void drawLine(Canvas canvas) {// 将pressedPoint中的所有格子依次遍历,互相连线
        for (int i = 0; i < pressedPoint.size() - 1; i++) {// 得到当前格子
            Point point = pressedPoint.get(i);
            // 得到下一个格子
            Point nextPoint = pressedPoint.get(i + 1);
            // 旋转画布
            canvas.rotate(RotateDegrees.getDegrees(point, nextPoint), point.getX(), point.getY());

            matrix.reset();
            // 根据距离设置拉伸的长度
            matrix.setScale(getDistance(point, nextPoint) / linePressedBitmap.getWidth(), 1f);
            // 进行平移
            matrix.postTranslate(point.getX(), point.getY() - linePressedBitmap.getWidth() / 2);

            if (point.getState() == Point.PRESSED_MODE) {canvas.drawBitmap(linePressedBitmap, matrix, null);
            } else {canvas.drawBitmap(lineErrorBitmap, matrix, null);
            }// 把画布旋转回来
            canvas.rotate(-RotateDegrees.getDegrees(point, nextPoint), point.getX(), point.getY());
        }// 如果是手指在移动的情况
        if (isMove) {Point lastPoint = pressedPoint.get(pressedPoint.size() - 1);
            canvas.rotate(RotateDegrees.getDegrees(lastPoint, moveX, moveY), lastPoint.getX(), lastPoint.getY());

            matrix.reset();
            Log.i(TAG, "the distance : " + getDistance(lastPoint, moveX, moveY) / linePressedBitmap.getWidth());
            matrix.setScale(getDistance(lastPoint, moveX, moveY) / linePressedBitmap.getWidth(), 1f);
            matrix.postTranslate(lastPoint.getX(), lastPoint.getY() - linePressedBitmap.getWidth() / 2);
            canvas.drawBitmap(linePressedBitmap, matrix, null);

            canvas.rotate(-RotateDegrees.getDegrees(lastPoint, moveX, moveY), lastPoint.getX(), lastPoint.getY());
        }}// 根据point和坐标点计算出之间的距离
    private float getDistance(Point point, float moveX, float moveY) {Point b = new Point(moveX,moveY,null);
        return getDistance(point,b);
    }// 根据两个point计算出之间的距离
    private float getDistance(Point point, Point nextPoint) {return (float) Math.sqrt(Math.pow(nextPoint.getX() - point.getX(), 2f) + Math.pow(nextPoint.getY() - point.getY(), 2f));
    }private void drawPoint(Canvas canvas) {for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[i].length; j++) {int state = points[i][j].getState();
                if (state == Point.NORMAL_MODE) {canvas.drawBitmap(normalBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
                } else if (state == Point.PRESSED_MODE) {canvas.drawBitmap(pressedBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
                } else {canvas.drawBitmap(errorBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
                }}}}//初始化九宫格的点
    private void initPoint() {points[0][0] = new Point(width / 4, offset + width / 4, "0");
        points[0][1] = new Point(width / 2, offset + width / 4, "1");
        points[0][2] = new Point(width * 3 / 4, offset + width / 4, "2");

        points[1][0] = new Point(width / 4, offset + width / 2, "3");
        points[1][1] = new Point(width / 2, offset + width / 2, "4");
        points[1][2] = new Point(width * 3 / 4, offset + width / 2, "5");

        points[2][0] = new Point(width / 4, offset + width * 3 / 4, "6");
        points[2][1] = new Point(width / 2, offset + width * 3 / 4, "7");
        points[2][2] = new Point(width * 3 / 4, offset + width * 3 / 4, "8");

    }@Override
    public boolean onTouchEvent(MotionEvent event) {if (isTouch) {float x = event.getX();
            float y = event.getY();
            Point point;
            switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 判断用户触摸的点是否在九宫格的任意一个格子之内
                    point = isPoint(x, y);
                    if (point != null) {point.setState(Point.PRESSED_MODE);  // 切换为按下模式
                        pressedPoint.add(point);
                    }break;
                case MotionEvent.ACTION_MOVE:if (pressedPoint.size() > 0) {point = isPoint(x, y);
                        if (point != null) {if (!crossPoint(point)) {point.setState(Point.PRESSED_MODE);
                                pressedPoint.add(point);
                            }}moveX = x;
                        moveY = y;
                        isMove = true;
                    }break;
                case MotionEvent.ACTION_UP:isMove = false;
                    String tempPwd = "";
                    for (Point p : pressedPoint) {tempPwd += p.getMark();
                    }if (listener != null) {listener.getStringPassword(tempPwd);
                    }if (tempPwd.equals(password)) {if (listener != null) {listener.isPassword(true);
                            this.postDelayed(runnable, 1000);
                        }} else {for (Point p : pressedPoint) {p.setState(Point.ERROR_MODE);
                        }isTouch = false;
                        this.postDelayed(runnable, 1000);
                        if (listener != null) {listener.isPassword(false);
                        }}break;
            }invalidate();
        }return true;
    }private boolean crossPoint(Point point) {if (pressedPoint.contains(point)) {return true;
        }return false;
    }public interface OnScreenLockListener {public void getStringPassword(String password);

        public void isPassword(boolean flag);
    }public void setOnScreenLockListener(OnScreenLockListener listener) {this.listener = listener;
    }private Point isPoint(float x, float y) {Point point;
        for(int i = 0; i<points.length;i++){for (int j = 0; j < points[i].length; j++) {point = points[i][j];
                if (isContain(point, x, y)) {return point;
                }}}return null;
    }private boolean isContain(Point point, float x, float y) {return Math.sqrt(Math.pow(x - point.getX(), 2f) + Math.pow(y - point.getY(), 2f)) <= radius;
    }private Runnable runnable = new Runnable() {@Override
        public void run() {isTouch = true;
            reset();
            invalidate();
        }};

    // 重置格子
    private void reset(){for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[i].length; j++) {points[i][j].setState(Point.NORMAL_MODE);
            }}pressedPoint.clear();
    }
}

3.RotateDegress类

public class RotateDegrees {public static float getDegrees(Point a, Point b){float degrees = 0 ;
        float aX = a.getX();
        float aY = a.getY();
        float bX = b.getX();
        float bY = b.getY();
        if(aX == bX){if(aY<bY){degrees = 90;
            }else{degrees = 270;
            }}else if(bY == aY){if(aX<bX){degrees = 0 ;
            }else{degrees = 180;
            }}else{if(aX>bX){if(aY>bY){degrees = 180 + (float)(Math.atan2(aY-bY,aX-bX)*180/Math.PI);
                }else{degrees = 180 - (float)(Math.atan2(bY -aY,aX - bX)*180/Math.PI);
                }}else{if(aY>bY){degrees = 360 -(float)(Math.atan2(aY - bY,bX-aX)*180/Math.PI);
                }else{degrees = (float)(Math.atan2(bY - aY,bX - aX)*180/Math.PI);
                }}}return degrees;
    }public static float getDegrees(Point a, float bX,float bY){Point b = new Point(bX,bY,null);
        return getDegrees(a,b);
    }
}

用到的图片资源

4.MainActivity中使用

public class MainActivity extends AppCompatActivity {private ScreenLockView screenLockView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        screenLockView = (ScreenLockView) findViewById(R.id.slv);
        screenLockView.setOnScreenLockListener(new ScreenLockView.OnScreenLockListener() {@Override
            public void getStringPassword(String password) {}@Override
            public void isPassword(boolean flag) {String content;
                if (flag) {content = "密码正确";

                } else {content = "密码错误";
                }Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();

            }});
    }
}

5.布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.admin.ninegridunlock.MainActivity">

 <com.example.admin.ninegridunlock.ScreenLockView
     android:id="@+id/slv"
     android:layout_width="match_parent"
     android:layout_height="match_parent" />
</RelativeLayout>

Android自定义控件实现九宫格解锁相关推荐

  1. 猿创征文|Android kotlin实现九宫格解锁

    目录 2.kotlin实现九宫格解锁的实现步骤如下 2.1 自定义解锁绘制View 2.2 绘制用到的Point类

  2. android自定义滑块解锁,使用Android自定义控件实现滑动解锁九宫格

    本文概述: 滑动解锁九宫格的分析: 1.需要自定义控件: 2.需要重写事件onTouchEvent(); 3.需要给九个点设置序号和坐标,这里用Map类就行: 4.需要判断是否到滑到过九点之一,并存储 ...

  3. android自定义View: 九宫格解锁

    本系列自定义View全部采用kt 系统:mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 废话不多说,先 ...

  4. android简单的九宫格解锁view

    纯代码绘制view有点粗糙 直接给代码吧,挺简单的 下面是主view代码 package jack.com.mypoint;import android.content.Context; import ...

  5. Android九宫格解锁应用

    先上一张效果图: 实现思路: 1.绘制点层 2.绘制线层 3.处理触摸事件(相对来说的难点) 4.实现绘制结束时的回调 写在前面: 实现过程中我们先不做大小适配,将控件的大小写死,根据原型图,将控件大 ...

  6. android自定义view之九宫格解锁

    android自定义view之九宫格解锁 更多细节请看源码 https://github.com/que123567/lockview 1. 定义一个类作为九宫格的格子 包含坐标和索引(用来记录密码) ...

  7. Android之九宫格解锁的实现

    九宫格解锁在Android中应用的很广泛,也是Android特有的一种解锁方式,其实实现起来也并不是很复杂,下面我就根据系统源码LockPatternView,移植出来的一个更加简单小巧九宫格解锁的例 ...

  8. Android纯代码实现九宫格解锁

    最近由于Android项目需要,要求做一个类似于支付宝的九宫格解锁组件, 要求不使用美工提供的图片资源,纯代码实现, 我在百度上搜关键字"android九宫格解锁", 终于找到想要 ...

  9. Android 仿小米锁屏实现九宫格解锁

    最近公司要求做个九宫格解锁,本人用的是小米手机,看着他那个设置锁屏九宫格很好看,就做了该组件,不使用图片资源,纯代码实现. 尊重每个辛苦的博主,在http://blog.csdn.net/mu399/ ...

最新文章

  1. ToString 的几个思考
  2. 北京工业大学计算机科学与技术研究生,北京工业大学研究生专业介绍:计算机科学与技术...
  3. c语言用数组实现循环移位,如何将一个数组的元素循环左移?
  4. 马云马化腾李彦宏在IT峰会上的话-内容提炼
  5. java高效获取大文件的行数
  6. NIO的空轮询bug是什么?netty是如何解决NIO空轮询bug的?
  7. 定制操作(传递函数或lambda表达式)
  8. r4烧录卡内核安装_R4 Wood内核怎么用?R4烧录卡Wood R4内核详细使用教程
  9. JAVA 实现《中国象棋》游戏
  10. asp.net mysql 论坛源码_最新asp.net微信商城快速开发框架Magicodes.Shop微商城开发框架源码企业商业版源代码...
  11. [BZOJ 3654] 图样图森破
  12. Java中的“无限循环”结构
  13. 鸿蒙申请银行卡格式错误,办建设银行卡问题
  14. Java跨年祝福语代码_2018跨年夜精选祝福语贺词
  15. pointnet语义分割_训练并预测自己的数据
  16. 各个框架原理总结归纳
  17. 判断自己的网络是不是公网IP
  18. 适合女生学的技术有哪些?
  19. 2020 年度开发工具 Top 100
  20. PL/SQL Developer x64 官网下载、中文包、注册码

热门文章

  1. URL、Session、Cookies、Server.Transfer、Application和跨页面传送。
  2. Python学习笔记:安装python
  3. 一个操作系统的实现(1):分析linux下如何运行一个执行文件
  4. CPU所含有的寄存器
  5. latex箭头表示符号
  6. (Q 2)netstat命令 检测TCP/IP 网络链接是否存在异常
  7. 吐血整理!这可能是最全的机器学习工具手册
  8. LeetCode 795. 区间子数组个数
  9. Uncaught TypeError: Object #Document has no method 'load'
  10. JavaScript 模拟事件触发